Slide 1

Slide 1 text

1 Testing: The Modern Way

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

3 Thank You!!

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

5 History...

Slide 6

Slide 6 text

6

Slide 7

Slide 7 text

Testing: The Future... Today?

Slide 8

Slide 8 text

State of Testing Today ● Acceptance ● Integration ● Unit

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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'); });

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Great, :shipit:

Slide 13

Slide 13 text

The End

Slide 14

Slide 14 text

Seriously?

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Now what?

Slide 20

Slide 20 text

emberjs/rfcs#119

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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'); });

Slide 27

Slide 27 text

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'); });

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

30 Today...

Slide 31

Slide 31 text

31

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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'); }); });

Slide 34

Slide 34 text

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'); }); });

Slide 35

Slide 35 text

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'); }); });

Slide 36

Slide 36 text

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'); }); });

Slide 37

Slide 37 text

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'); }); });

Slide 38

Slide 38 text

38 Today - Summary Discoverability Consistent APIs Modularity

Slide 39

Slide 39 text

39 Future

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

41 Q/A

Slide 42

Slide 42 text

42 `window.find` is a troll!

Slide 43

Slide 43 text

43

Slide 44

Slide 44 text

44 Lots of tests === Slow!

Slide 45

Slide 45 text

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...'); }); }); }

Slide 46

Slide 46 text

46 Ember 3.1

Slide 47

Slide 47 text

47 Ember 3.2

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

49

Slide 50

Slide 50 text

50 Runloop

Slide 51

Slide 51 text

51

Slide 52

Slide 52 text

52

Slide 53

Slide 53 text

53 Testing intermediate states?

Slide 54

Slide 54 text

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(); });

Slide 55

Slide 55 text

55 Engine tests?

Slide 56

Slide 56 text

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();

Slide 57

Slide 57 text

57 Fastboot tests?

Slide 58

Slide 58 text

58

Slide 59

Slide 59 text

59 Passing models into components?

Slide 60

Slide 60 text

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 });

Slide 61

Slide 61 text

61 How do I get started?

Slide 62

Slide 62 text

62