Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing: The Modern Way

Testing: The Modern Way

Review what's new in testing land for Ember and then dive into examples, both good and bad, to help us level up our Ember tests.

Robert Jackson

April 26, 2018
Tweet

More Decks by Robert Jackson

Other Decks in Programming

Transcript

  1. 1
    Testing: The Modern Way

    View full-size slide

  2. 2
    Open Source Addict
    Ember Core Team
    OUR
    MISSION
    Investment generally results in acquiring

    View full-size slide

  3. 3
    Thank You!!

    View full-size slide

  4. Testing: The Future...
    Today?

    View full-size slide

  5. State of Testing Today
    ● Acceptance
    ● Integration
    ● Unit

    View full-size slide

  6. Acceptance
    moduleForAcceptance('Acceptance | login');
    test('visiting /login', function(assert) {
    visit('/login');
    andThen(function() {
    assert.equal(currentURL(), '/login');
    });
    });

    View full-size slide

  7. Integration
    moduleForComponent('pretty-color', {
    integration: true
    });
    test('button click', function(assert) {
    this.render(hbs`{{magic-title}}`);
    this.$('button').click();
    assert.equal(this.$().text(), 'This is Magic');
    });

    View full-size slide

  8. Unit
    moduleFor('route:application');
    test('perform the right query', function(assert) {
    let route = this.subject();
    let result = route._queryString();
    assert.equal(result, '?awesome=sauce');
    });

    View full-size slide

  9. Great, :shipit:

    View full-size slide

  10. ● Built for a globals world.
    ● “magic” `andThen`
    ● No ability to stub/mock services.
    Acceptance Test Issues

    View full-size slide

  11. ● Completely ignorant about async
    ● Manual triggering of user interaction
    ● Much less “tailored” API
    Unit/Integration Test Issues

    View full-size slide

  12. emberjs/rfcs#119

    View full-size slide

  13. ● Use `async` / `await`
    ● Consistent interface
    ● Extendability
    ● Backwards Compatibility
    Grand Testing Unification

    View full-size slide

  14. 25
    moduleForAcceptance('Acceptance | login');
    test('visiting /login', function(assert) {
    visit('/login');
    andThen(function() {
    assert.equal(currentURL(), '/login');
    });
    });
    History - Acceptance

    View full-size slide

  15. 26
    History - Integration
    moduleForComponent('pretty-color', {
    integration: true
    });
    test('button click', function(assert) {
    this.render(hbs`{{magic-title}}`);
    this.$('button').click();
    assert.equal(this.$().text(), 'This is Magic');
    });

    View full-size slide

  16. 27
    History - Unit
    moduleFor('route:application');
    test('perform the right query', function(assert) {
    let route = this.subject();
    let result = route._queryString();
    assert.equal(result, '?awesome=sauce');
    });

    View full-size slide

  17. 28
    History - Acceptance Issues
    “magical”
    Globals API
    No owner access

    View full-size slide

  18. 29
    History - Unit/Integration Issues
    Manual User Interaction
    Ignores Async
    Poorer design

    View full-size slide

  19. 32
    import { setupApplicationTest } from 'ember-qunit';
    import { module } from 'qunit';
    module('Acceptance | login', function(hooks) {
    setupApplicationTest(hooks);
    test('visiting /login', async function(assert) {
    await visit('/login');
    assert.equal(currentURL(), '/login');
    });
    });
    Today - Application

    View full-size slide

  20. 33
    Today - Rendering
    import { module } from 'qunit';
    import { setupRenderingTest } from 'ember-qunit';
    import { render, click } from '@ember/test-helpers';
    module('pretty-color', function(hooks) {
    setupRenderingTest(hooks);
    test('button click', async function(assert) {
    await render(hbs`{{magic-title}}`);
    await click('button');
    assert.dom().hasText('This is magic');
    });
    });

    View full-size slide

  21. 34
    Today - Basic
    import { module } from 'qunit';
    import { setupTest } from 'ember-qunit';
    module('route:application', function(hooks) {
    setupTest(hooks);
    test('perform the right query', function(assert) {
    let route = this.owner.lookup('route:application');
    let result = route._queryString();
    assert.equal(result, '?awesome=sauce');
    });
    });

    View full-size slide

  22. 35
    Today - Addons
    import setupMirageTest from 'ember-cli-mirage/test-support/setup-mirage';
    // ...snip...
    module('Integration | Component | light table', function(hooks) {
    setupRenderingTest(hooks);
    setupMirageTest(hooks); // <--
    test('button click', async function(assert) {
    await render(hbs`{{magic-title}}`);
    await click('button');
    assert.dom().hasText('This is magic');
    });
    });

    View full-size slide

  23. 36
    Today - Addons
    import { selectChoose } from 'ember-power-select/test-support';
    // ...snip...
    module('Integration | Component x-foo', function(hooks) {
    setupRenderingTest(hooks);
    test('button click', async function(assert) {
    await render(hbs`{{x-foo}}`);
    await selectChoose('.selector', 'Barbaz');
    assert.dom().hasText('This is magic');
    });
    });

    View full-size slide

  24. 37
    Today - Addons
    import { setupPact, given, interaction } from 'ember-cli-pact';
    // ...snip…
    module('Pact | People', function(hooks) {
    setupTest(hooks);
    setupPact(hooks, { provider: 'my-api', consumer: 'my-app'});
    test('locating a person by ID', async function(assert) {
    given('a person exists', { id: '123', name: 'Alice' });
    let person = await interaction(() =>
    this.store().findRecord('person', '123'));
    assert.equal(person.get('id'), '123');
    assert.equal(person.get('name'), 'Alice');
    });
    });

    View full-size slide

  25. 38
    Today - Summary
    Discoverability
    Consistent APIs
    Modularity

    View full-size slide

  26. 40
    Future
    Addon Entry Points
    Engine & FastBoot Specific APIs
    Common Entry Module

    View full-size slide

  27. 42
    `window.find` is a troll!

    View full-size slide

  28. 44
    Lots of tests === Slow!

    View full-size slide

  29. 45
    for (let i = 0; i < 1000; i++) {
    module('Acceptance | index | ' + i, function(hooks) {
    setupApplicationTest(hooks);
    test('visiting /', async function(assert) {
    await visit('/');
    await click('button');
    assert.dom('.other-landing').hasText('Landed on
    other...');
    });
    });
    }

    View full-size slide

  30. 48
    Ember 3.2 +
    emberjs/ember-test-helpers#370

    View full-size slide

  31. 53
    Testing intermediate states?

    View full-size slide

  32. 54
    test('shows and hides loading spinner after submit', async function(assert) {
    await visit('/');
    click('.submit'); // <-- don't await
    await waitFor('.loading-spinner');
    assert.dom('.loading-spinner').exists();
    await settled();
    assert.dom('.loading-spinner').doesNotExist();
    });

    View full-size slide

  33. 55
    Engine tests?

    View full-size slide

  34. 56
    import Resolver from 'my-app/resolver';
    import engineResolverFor from
    'ember-engines/test-support/engine-resolver-for';
    let engineResolver = engineResolverFor('the-async-engine');
    const resolver = Resolver.extend({
    resolve() {
    let resolved = this._super(...arguments);
    if (resolved) {
    return resolved;
    }
    return engineResolver.resolve(...arguments);
    },
    }).create();

    View full-size slide

  35. 57
    Fastboot tests?

    View full-size slide

  36. 59
    Passing models into
    components?

    View full-size slide

  37. 60
    test('rendering a blog post', async function(assert) {
    const post = this.server.create('post', 'inSomeState');
    await Ember.run(async function() {
    // Must be inside a Run Loop to use the store, abstraction feels really
    leaky
    this.set('post', this.store.find('post', post.id);
    });
    await render(hbs`
    {{blog-post post=post}}
    `);
    // Perform some assertion, finally
    });

    View full-size slide

  38. 61
    How do I get started?

    View full-size slide