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!
The Next Generation of Testing in Ember.js
By Michał Staśkiewicz
The Next Generation of Testing in Ember.js
- 794