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.

0dfd10ad198ae5a87640da046a77a90a?s=128

Robert Jackson

April 26, 2018
Tweet

Transcript

  1. 1 Testing: The Modern Way

  2. 2 Open Source Addict Ember Core Team OUR MISSION Investment

    generally results in acquiring
  3. 3 Thank You!!

  4. 4

  5. 5 History...

  6. 6

  7. Testing: The Future... Today?

  8. State of Testing Today • Acceptance • Integration • Unit

  9. Acceptance moduleForAcceptance('Acceptance | login'); test('visiting /login', function(assert) { visit('/login'); andThen(function()

    { assert.equal(currentURL(), '/login'); }); });
  10. 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'); });
  11. Unit moduleFor('route:application'); test('perform the right query', function(assert) { let route

    = this.subject(); let result = route._queryString(); assert.equal(result, '?awesome=sauce'); });
  12. Great, :shipit:

  13. The End

  14. Seriously?

  15. • Built for a globals world. • “magic” `andThen` •

    No ability to stub/mock services. Acceptance Test Issues
  16. None
  17. • Completely ignorant about async • Manual triggering of user

    interaction • Much less “tailored” API Unit/Integration Test Issues
  18. None
  19. Now what?

  20. emberjs/rfcs#119

  21. • Use `async` / `await` • Consistent interface • Extendability

    • Backwards Compatibility Grand Testing Unification
  22. None
  23. None
  24. None
  25. 25 moduleForAcceptance('Acceptance | login'); test('visiting /login', function(assert) { visit('/login'); andThen(function()

    { assert.equal(currentURL(), '/login'); }); }); History - Acceptance
  26. 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'); });
  27. 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'); });
  28. 28 History - Acceptance Issues “magical” Globals API No owner

    access
  29. 29 History - Unit/Integration Issues Manual User Interaction Ignores Async

    Poorer design
  30. 30 Today...

  31. 31

  32. 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
  33. 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'); }); });
  34. 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'); }); });
  35. 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'); }); });
  36. 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'); }); });
  37. 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'); }); });
  38. 38 Today - Summary Discoverability Consistent APIs Modularity

  39. 39 Future

  40. 40 Future Addon Entry Points Engine & FastBoot Specific APIs

    Common Entry Module
  41. 41 Q/A

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

  43. 43

  44. 44 Lots of tests === Slow!

  45. 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...'); }); }); }
  46. 46 Ember 3.1

  47. 47 Ember 3.2

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

  49. 49

  50. 50 Runloop

  51. 51

  52. 52

  53. 53 Testing intermediate states?

  54. 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(); });
  55. 55 Engine tests?

  56. 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();
  57. 57 Fastboot tests?

  58. 58

  59. 59 Passing models into components?

  60. 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 });
  61. 61 How do I get started?

  62. 62