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

Testing: The Future... Today?

Testing: The Future... Today?

Discuss the current state of testing in Ember applications, where we are going, and what we can do now...

This talk was given on 2017-03-23 at Ember NYC.

0dfd10ad198ae5a87640da046a77a90a?s=128

Robert Jackson

March 23, 2017
Tweet

Transcript

  1. Testing: The Future... Today?

  2. Who the heck is this guy? • Ember Core Team

    • General Open Source Addict • LinkedIn twitter: rwjblue github: rwjblue
  3. Thank You!!

  4. What are we talking about?

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

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

    { assert.equal(currentURL(), '/login'); }); });
  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'); });
  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'); });
  9. Great, :shipit:

  10. The End

  11. Seriously?

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

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

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

  17. emberjs/rfcs#119

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

    • Backwards Compatibility Grand Testing Unification
  19. None
  20. None
  21. None
  22. Acceptance moduleForAcceptance('Can see things'); test('clicking redirects', async function(assert) { await

    visit('/things'); let list = find('.thing-item'); assert.equal(list.length, 5); await click('.thing-item[data-id=1]'); assert.equal(this.currentRouteName, 'other-thing'); });
  23. Integration moduleForIntegration('post-display'); test('expand when clicked', async function(assert) { await render(hbs`{{post-display}}`);

    await click('.post-item'); assert.equal( this.find('.post-created-at').textContent, '2016-01-05' ); });
  24. • Overriding a service • Registering custom test helpers •

    Registering custom waiters • Accessing service instances General Testing Concerns
  25. • Hooks for work before/after tests. • Accessing general test

    information General Testing Concerns
  26. Overriding a Service import Mock from 'somewhere/else'; moduleForAcceptance('something', { beforeEach()

    { this.owner.register('service:stripe', Mock); } });
  27. Registering Custom Helpers import selectCategory from '../helpers/select-category'; import loginAsAdmin from

    '../helpers/login-as-admin'; moduleForAcceptance('Can see things', { beforeEach() { this.registerHelpers({ loginAsAdmin, selectCategory }); } });
  28. Custom Helpers // tests/helpers/login-as-admin'; import { testHelper } from 'ember-qunit';

    export default testHelper(async function() { await this.click('.login-now'); await this.fillIn('.username', 'rwjblue'); await this.fillIn('.password', 'moar beerz plz'); await this.click('.submit'); });
  29. Custom Helpers import selectCategory from '../helpers/select-category'; import loginAsAdmin from '../helpers/login-as-admin';

    moduleForAcceptance('Can see things', { beforeEach() { this.registerHelpers({ loginAsAdmin, selectCategory }); } });
  30. Custom Waiters import { testWaiter } from 'ember-test-helpers'; import {

    hasPendingTransactions } from 'app-name/services/transactions'; export default testWaiter(function() { return hasPendingTransactions(); });
  31. Custom Waiters import transactionWaiter from '../waiters/pending-transactions'; test('stuff happens', async function(assert)

    { // Normally done in setup, but slides.... this.registerWaiter(transactionWaiter); await click('.foo'); // all pending transactions are completed here... });
  32. Accessing Services test('foo', function(assert) { this.store = this.owner.lookup('service:store'); this.store.push(....); });

  33. General Hooks // */tests/configuration.js import { TestConfig } from 'ember-test-helpers';

    export default class extends TestConfig { beforeSuite() {} beforeEach(testType, testContext) {} afterEach(testType, testContext) {} }
  34. General Hooks // liquid-fire's addon-test-support/configuration.js import { TestConfig } from

    'ember-test-helpers'; import runningTransitionWaiter from './waiters/running-transition'; import randoHelper from './helpers/rando'; export default class extends TestConfig { beforeEach() { this.registerWaiter(runningTransitionWaiter); this.registerHelper('randoHelper', randoHelper); } }
  35. General Hooks // mirage's addon-test-support/configuration.js import { TestConfig } from

    'ember-test-helpers'; import setup from 'ember-cli-mirage/setup-server'; export default class extends TestConfiguration { beforeEach() { this.server = setup(this.owner); } }
  36. Test Information import { testHelper } from 'ember-qunit'; export default

    testHelper(function() { if (this.testInfo.type === 'acceptance') { // acceptance test stuff here } else { // integration/unit test stuff here } });
  37. What about today?

  38. • Use `async` / `await` • Consistent Helpers Today

  39. What about today? `async` / `await`

  40. Today: `async` / `await` ember new test-2-12-app cd test-2-12-app ember

    install ember-maybe-import-regenerator
  41. Today: `async` / `await` // .eslintrc.js // ...snip... parserOptions: {

    ecmaVersion: 2017, sourceType: 'module' }, // ...snip...
  42. Today: `async` / `await` moduleForAcceptance('Acceptance | index'); test('visiting /', async

    function(assert) { await visit('/'); assert.equal(currentURL(), '/'); });
  43. Today: `async` / `await` npm install --save-dev ember-cli@^2.13.0-beta.2 ember install

    ember-cli-babel@^6.0.0-beta.9 ember install ember-cli-htmlbars-inline-precompile@^0.4.0-beta.2
  44. Today: `async` / `await` // config/targets.js module.exports = { browsers:

    [ 'last 1 Chrome versions', ] };
  45. Today: `async` / `await`

  46. What about today? Consistent Helpers

  47. Today: Consistent Helpers

  48. Today: Consistent Helpers ember install ember-native-dom-helpers

  49. • Uses standard DOM API’s • Works in unit, integration,

    and acceptance • Implements helpers from emberjs/rfcs#119 Today: Consistent Helpers
  50. import { click, tap, fillIn, find, findAll, keyEvent, triggerEvent, waitUntil

    } from 'ember-native-dom-helpers'; Today: Consistent Helpers
  51. Today: Consistent Helpers moduleForAcceptance('Acceptance | Sign up'); test('Usage awaiting the

    world to settle', async function(assert) { await visit('/sign-up'); await fillIn('.first-name', 'Chuck'); await fillIn('.last-name', 'Berry'); await click('.submit-btn'); assert.ok(find('.welcome-msg'), 'There is a welcome banner'); assert.equal(find('.welcome-msg-name'), 'Chuck'); });
  52. Today: Consistent Helpers moduleForComponent('Integration | Component | my-component', { integration:

    true }); test('I can interact with my component', async function(assert) { this.render(hbs`{{my-component}}`); await fillIn('.some-input'); await click('.main-button'); await keyEvent('.other-input', 'keyup', 40); // down arrow await triggerEvent('.some-drop-area', 'mouseenter'); assert.ok(find('.result-of-event-happened')); assert.equal(findAll('.result-list-item').length, 3); })
  53. Today: Consistent Helpers test('using `waitUntil` to test unsettled state', async

    function(assert) { await visit('/signup-example'); assert.ok(find('.signup-example-form'), 'The signup form is displayed'); fillIn('.signup-example-form__email', 'some@email.com'); fillIn('.signup-example-form__password', '123123'); fillIn('.signup-example-form__password-confirmation', '123123'); let submitPromise = click('.signup-example-form__submit-btn'); await waitUntil(() => find('.dashbord-loading-substate-header')); assert.equal(find('.dashbord-loading-substate-header').textContent.tri
  54. What about today soon? Simpler QUnit API

  55. Today: Simpler QUnit API import { module, test, buildIntegrationOptions }

    from 'ember-qunit'; module('component:x-foo', buildIntegrationOptions({ beforeEach() { // special setup stuff } })); test('renders', function(assert) { assert.expect(1); this.render(hbs`{{pretty-color name="red"}}`); assert.equal(this.$('.color-name').text(), 'red'); });
  56. Today: Simpler QUnit API import { module, test, setupIntegrationTest }

    from 'ember-qunit'; module('component:x-foo', function(hooks) { setupIntegrationTest(hooks); test('renders', function(assert) { assert.expect(1); this.render(hbs`{{pretty-color name="red"}}`); assert.equal(this.$('.color-name').text(), 'red'); }); });
  57. The End