The Next Generation of Testing

@mikoscz

Michał Staśkiewicz

Old fashion
New kids on the block
Migration

Agenda

Old fashion way

moduleFor()
moduleForModel()
moduleForComponent()
moduleForAcceptance()

?

Terrible asynchronous syntax

andThen()
andThen()
andThen()
andThen()

:<

andThen()

import { test } from 'qunit';
import moduleForAcceptance from 'some-app/tests/helpers/module-for-acceptance';

moduleForAcceptance('Acceptance | users');

test('should add new uesr', function(assert) {
  visit('/users/new');
  fillIn('input.email', 'm.staskiewicz@selleo.com');
  click('button.submit');

  andThen(() => assert.equal(find('ul.users li:first').text(), 'm.staskiewicz@selleo.com'));
});

ECMAScript 2017 FTW!

(async/await syntax)

import { test } from 'qunit';
import moduleForAcceptance from 'some-app/tests/helpers/module-for-acceptance';

moduleForAcceptance('Acceptance | users');

test('should add new uesr', async function(assert) {
  await visit('/users/new');
  await fillIn('input.email', 'm.staskiewicz@selleo.com');
  await click('button.submit');

  assert.equal(find('ul.users li:first').text(), 'm.staskiewicz@selleo.com');
});

jQuery :<

import { test } from 'qunit';
import moduleForAcceptance from 'some-app/tests/helpers/module-for-acceptance';

moduleForAcceptance('Acceptance | users');

test('should add new uesr', async function(assert) {
  await visit('/users/new');
  await fillIn('input.email', 'm.staskiewicz@selleo.com');
  await click('button.submit');

  assert.equal(find('ul.users li:first').text(), 'm.staskiewicz@selleo.com');
});

jQuery :<

await click('button.submit');
assert.equal(find('ul.users li:first').text(), 'm.staskiewicz@selleo.com');

Super

Awesome

Speed

Beautifull

Lovely

The world without jQuery <3

import { click, find } from 'ember-native-dom-helpers';

await click('button.submit');
assert.equal(find('ul.users li:first').textContent, 'm.staskiewicz@selleo.com');

ember install ember-native-dom-helpers

RFC #232

"Add new QUnit testing API"

module()
setupTest()
setupRenderingTest()

RFC #268

// old
moduleForAcceptance()

// new
module()
setupAcceptanceTest()

Plain QUnit tests

import { module, test } from 'qunit';

module('relativeDate', function(hooks) {
  test('format relative dates correctly', function() {
    assert.equal(relativeDate('2018/03/25 19:40:10'), 'just now');
  });
});

Container tests

- Test with access to the container(Services, Routes, Controllers)

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Service | events-counter', function(hooks) {
  setupTest(hooks);

  test('it counts events', function(assert) {
    const service = this.owner.lookup('service:events-counter');

    service.addEvent('first');
    service.addEvent('second');

    assert.equal(service.get('eventsCount'), 2);
  });
});

Rendering tests

import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';

module('Component | counter', function(hooks) {
  setupRenderingTest(hooks);

  test('it counts clicks', async function(assert) {
    this.set('count', 0);

    await render(hbs`{{click-counter value=value ...}}`);
    assert.equal(this.element.textContent, '0 clicks');

    await click('.counter');
    assert.equal(this.element.textContent, '1 clicks');
  });
});

Acceptance tests

import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { visit, fillIn, click } from '@ember/test-helpers';

module('Acceptance | users', function(hooks) {
  setupApplicationTest(hooks);

  test('should add new user', async function(assert) {
    await visit('/users/new');
    await fillIn('input.email', 'm.staskiewicz@selleo.com');
    await click('button.submit');

    const email = this.element.querySelector('ul.users li:first').textContent;
    assert.equal(email, 'm.staskiewicz@selleo.com');
  });
});

Cool!

But how to use it?

ember install
ember-cli-qunit 4.2.0 <

(works from ember 2.4.x <)

Migration

1. async/await syntax

2. ember-native-dom-helpers

(ember-native-dom-helpers-codemod)

3. moduleFor() -> setupTest()

(ember-qunit-codemod)

4. (at)ember/test-helpers

(ember-test-helpers-codemod)

@ -> (at) awesome font without at sing :'<

Helpful stuff

Loading state

test('shows loading spinner after submitting', async function(assert) {
  await visit('/comments/new');
  await fillIn('input.comment', 'Hello there');

  const promise = click('.submit');

  await waitFor('.loading-spinner');
  assert.ok(find('.spinner'));

  await promise;
  assert.ok(find('.some-success-notify'));
});

Custom Test Helpers

old registerAsyncHelper()

export async function createAccount(email) {
  await fillIn('#email', email);
  await click('button.submit');
}

Test Selectors

// template.hbs
<h1>{{title}}</h1>
<input class="title-field">

// test.js
await fillIn('.title-field', 'Hello from field');

const title = this.element.querySelector('h1').textContent;
assert.equal(title, 'Hello from field');
// template.hbs
<h1 data-test-title>{{title}}</h1>
<input class="title-field" data-test-title-field>

// test.js
await fillIn('[data-test-title-field]', 'Hello from field');

const title = this.element.querySelector('[data-test-title').textContent;
assert.equal(title, 'Hello from field');

ember install ember-test-selectors

Awesome!

Now make it readable

const title = this.element.querySelector('[data-test-title').textContent;
assert.equal(title, 'Hello from field');
assert.dom('[data-test-title]').hasText('Hello from field');

qunit-dom

qunit-dom

assert.dom('h1').exists();
assert.dom('h1').hasClass('title');
assert.dom('h1').hasText('Welcome to Ember, John Doe!');

assert.dom('input').isFocused();
assert.dom('input').hasValue(/.+ Doe/);
assert.dom('input').hasAttribute('type', 'text');

sources:

The Next Generation of Testing in Ember.js by Tobias Bieniek

ember-cli-qunit

qunit-dom

EmberConf 2018 presentations

Thanks!

and keep testing your code!

Made with Slides.com