SlideShare une entreprise Scribd logo
1  sur  55
Télécharger pour lire hors ligne
Ember Testing Internals
with Ember-CLI
Cory Forsyth
@bantic
201 Created
Matthew BealeCory Forsyth
http://201-created.com/
http://devopsreactions.tumblr.com/
The Ember-CLI Testing Triumvirate
• The test harness (tests/index.html)
• Unit Test Affordances
• Acceptance Test Affordances
$ ember new my-app
Ember-CLI makes testing
Easy
• `ember generate X` creates test for X
• 14 test types:
• acceptance, adapter, component, controller,
• helper, initializer, mixin, model, route,
• serializer, service, transform, util, view
Ember-CLI Test Harness
• A real strength of Ember-CLI
• Ember-CLI builds tests/index.html for you
• QUnit is built-in (more on this later)
<!DOCTYPE html>!
<html>!
<head>!
<meta charset="utf-8">!
<meta http-equiv="X-UA-Compatible" content="IE=edge">!
<title>EmberTestingTalk Tests</title>!
<meta name="description" content="">!
<meta name="viewport" content="width=device-width, initial-scale=1">!
!
{{content-for 'head'}}!
{{content-for 'test-head'}}!
!
<link rel="stylesheet" href="assets/vendor.css">!
<link rel="stylesheet" href="assets/ember-testing-talk.css">!
<link rel="stylesheet" href="assets/test-support.css">!
<style>!
#ember-testing-container {!
position: absolute;!
background: white;!
bottom: 0;!
right: 0;!
width: 640px;!
height: 384px;!
overflow: auto;!
z-index: 9999;!
border: 1px solid #ccc;!
}!
#ember-testing {!
zoom: 50%;!
}!
</style>!
</head>!
config in meta tag
addons can modify
Ember-CLI builds these
makes that mini-me
app on the test page
tests/index.html
<body>!
<div id="qunit"></div>!
<div id="qunit-fixture"></div>!
!
{{content-for 'body'}}!
{{content-for 'test-body'}}!
<script src="assets/vendor.js"></script>!
<script src="assets/test-support.js"></script>!
<script src="assets/ember-testing-talk.js"></script>!
<script src="testem.js"></script>!
<script src="assets/test-loader.js"></script>!
</body>!
</html>!
for QUnit
addons can modify
tests/index.html
<body>!
<div id="qunit"></div>!
<div id="qunit-fixture"></div>!
!
{{content-for 'body'}}!
{{content-for 'test-body'}}!
<script src="assets/vendor.js"></script>!
<script src="assets/test-support.js"></script>!
<script src="assets/ember-testing-talk.js"></script>!
<script src="testem.js"></script>!
<script src="assets/test-loader.js"></script>!
</body>!
</html>!
jQuery, Handlebars,
Ember, `app.import`
QUnit, ember-qunit
app code, including
tests (in non-prod env)
app code, including
tests (in non-prod env)
`require`s all the tests
tests/index.html
/* globals requirejs, require */!
!
var moduleName, shouldLoad;!
!
QUnit.config.urlConfig.push({ id: 'nojshint', label: 'Disable JSHint'});!
!
// TODO: load based on params!
for (moduleName in requirejs.entries) {!
shouldLoad = false;!
!
if (moduleName.match(/[-_]test$/)) { shouldLoad = true; }!
if (!QUnit.urlParams.nojshint && moduleName.match(/.jshint$/)) { shouldLoad = true; }!
!
if (shouldLoad) { require(moduleName); }!
}!
!
if (QUnit.notifications) {!
QUnit.notifications({!
icons: {!
passed: '/assets/passed.png',!
failed: '/assets/failed.png'!
}!
});!
}!
Requires every module name ending in _test or -test
(named AMD modules, not npm modules or QUnit modules)
test-loader.js
module("a basic test");!
!
test("this test will pass", function(){!
ok(true, "yep, it did");!
});!
define("ember-testing-talk/tests/unit/basic-test", [], function(){!
! "use strict";!
! module("a basic test");!
!
! test("this test will pass", function(){!
! ! ok(true, "yep, it did");!
! });!
});
test-loader.js requires this, QUnit runs it
Ember-CLI compiles to
named AMD module ending in -test
tests/unit/basic-test.js
$ ember g controller index
import {!
moduleFor,!
test!
} from 'ember-qunit';!
!
moduleFor('controller:index', 'IndexController', {!
// Specify the other units that are required for this test.!
// needs: ['controller:foo']!
});!
!
// Replace this with your real tests.!
test('it exists', function() {!
var controller = this.subject();!
ok(controller);!
});!
Ember-CLI Test Harness
• tests/index.html:
• app code as named AMD modules
• app test code as named AMD modules
• vendor js (Ember, Handlebars, jQuery)
• test support (QUnit, ember-qunit AMD)
• test-loader.js: `require`s each AMD test module
• QUnit runs the tests
Ember-CLI Test Harness
• How does QUnit and ember-qunit end up in test-
support.js?
• ember-cli-qunit! (it is an ember-cli addon)
Ember-CLI Test Harness
Anatomy of a Unit Test
• How does Ember actually run a unit test?
• What does that boilerplate do?
import {!
moduleFor,!
test!
} from 'ember-qunit';!
!
moduleFor('controller:index', 'IndexController', {!
// Specify the other units that are required for this test.!
// needs: ['controller:foo']!
});!
!
// Replace this with your real tests.!
test('it exists', function() {!
var controller = this.subject();!
ok(controller);!
});!
tests/unit/controllers/index-test.js
import {!
moduleFor,!
test!
} from 'ember-qunit';!
!
moduleFor('controller:index', 'IndexController', {!
// Specify the other units that are required for this test.!
// needs: ['controller:foo']!
});!
!
// Replace this with your real tests.!
test('it exists', function() {!
var controller = this.subject();!
ok(controller);!
});!
tests/unit/controllers/index-test.js
ember-qunit
• imported via ember-cli-qunit addon
• provides `moduleFor`
• also: `moduleForModel`, `moduleForComponent`
• provides `test`
ember-qunit: moduleFor
• wraps QUnit’s native `QUnit.module`
• creates an isolated container with `needs` array
• provides a context for test:
• this.subject(), this.container, etc
ember-qunit: moduleForX
• moduleForComponent
• registers my-component.js and my-component.hbs
• connects the template to the component as ‘layout’
• adds `this.render`, `this.append` and `this.$`
• moduleForModel
• sets up ember-data (registers default transforms, etc)
• adds `this.store()`
• registers application:adapter, defaults to DS.FixtureAdapter
ember-qunit: test
• wraps QUnit’s native `QUnit.test`
• casts the test function result to a promise
• uses `stop` and `start` to handle potential async
• if you `return` a promise, the test will handle it
correctly
• runs the promise resolution in an Ember.run loop
ember-qunit
• Builds on ember-test-helpers (library)
• ember-test-helpers is test-framework-agnostic
• provides methods for creating test suites (aka
QUnit modules), setup/teardown, etc
• future framework adapters can build on it
• ember-cli-mocha!
ember-cli-mocha
Ember Testing Affordances
• Two primary types of tests in Ember:
• Unit Tests
• need isolated containers, specific setup
• use moduleFor
Ember Testing Affordances
• Two primary types of tests in Ember:
• Unit Tests and
• Acceptance Tests
• Totally different animal
• must manage async, interact with DOM
Ember Acceptance Tests
$ ember g acceptance-test index
import Ember from 'ember';!
import startApp from '../helpers/start-app';!
!
var App;!
!
module('Acceptance: Index', {!
setup: function() {!
App = startApp();!
},!
teardown: function() {!
Ember.run(App, 'destroy');!
}!
});!
!
test('visiting /', function() {!
visit('/');!
!
andThen(function() {!
equal(currentPath(), 'index');!
});!
});!
tests/unit/controllers/index-test.js
import Ember from 'ember';!
import startApp from '../helpers/start-app';!
!
var App;!
!
module('Acceptance: Index', {!
setup: function() {!
App = startApp();!
},!
teardown: function() {!
Ember.run(App, 'destroy');!
}!
});!
!
test('visiting /', function() {!
visit('/');!
!
andThen(function() {!
equal(currentPath(), 'index');!
});!
});!
tests/unit/controllers/index-test.js
What if visiting / takes
5 seconds?
How does this know to wait?
import Ember from 'ember';!
import startApp from '../helpers/start-app';!
!
var App;!
!
module('Acceptance: Index', {!
setup: function() {!
App = startApp();!
},!
teardown: function() {!
Ember.run(App, 'destroy');!
}!
});!
!
test('visiting /', function() {!
visit('/');!
!
andThen(function() {!
equal(currentPath(), 'index');!
});!
});!
What if visiting / takes
5 seconds?
How does this know to wait?
tests/unit/controllers/index-test.js
import Ember from 'ember';!
import startApp from '../helpers/start-app';!
!
var App;!
!
module('Acceptance: Index', {!
setup: function() {!
App = startApp();!
},!
teardown: function() {!
Ember.run(App, 'destroy');!
}!
});!
!
test('visiting /', function() {!
visit('/');!
!
andThen(function() {!
equal(currentPath(), 'index');!
});!
});!
vanilla QUnit module
tests/acceptance/index-test.js
import Ember from 'ember';!
import startApp from '../helpers/start-app';!
!
var App;!
!
module('Acceptance: Index', {!
setup: function() {!
App = startApp();!
},!
teardown: function() {!
Ember.run(App, 'destroy');!
}!
});!
!
test('visiting /', function() {!
visit('/');!
!
andThen(function() {!
equal(currentPath(), 'index');!
});!
});!
vanilla QUnit module
special test helpers:
visit, andThen,
currentPath
tests/acceptance/index-test.js
import Ember from 'ember';!
import startApp from '../helpers/start-app';!
!
var App;!
!
module('Acceptance: Index', {!
setup: function() {!
App = startApp();!
},!
teardown: function() {!
Ember.run(App, 'destroy');!
}!
});!
!
test('visiting /', function() {!
visit('/');!
!
andThen(function() {!
equal(currentPath(), 'index');!
});!
});!
What is `startApp`?
tests/acceptance/index-test.js
import Ember from 'ember';!
import Application from '../../app';!
import Router from '../../router';!
import config from '../../config/environment';!
!
export default function startApp(attrs) {!
var App;!
!
var attributes = Ember.merge({}, config.APP);!
attributes = Ember.merge(attributes, attrs);!
!
Router.reopen({!
location: 'none'!
});!
!
Ember.run(function() {!
App = Application.create(attributes);!
App.setupForTesting();!
App.injectTestHelpers();!
});!
!
App.reset();!
!
return App;!
}!
don’t change URL
start application
tests/helpers/start_app.js
import Ember from 'ember';!
import Application from '../../app';!
import Router from '../../router';!
import config from '../../config/environment';!
!
export default function startApp(attrs) {!
var App;!
!
var attributes = Ember.merge({}, config.APP);!
attributes = Ember.merge(attributes, attrs);!
!
Router.reopen({!
location: 'none'!
});!
!
Ember.run(function() {!
App = Application.create(attributes);!
App.setupForTesting();!
App.injectTestHelpers();!
});!
!
App.reset();!
!
return App;!
}!
• set Ember.testing = true
• set a test adapter
• prep for ajax:
• listeners for ajaxSend,
ajaxComplete
tests/helpers/start_app.js
import Ember from 'ember';!
import Application from '../../app';!
import Router from '../../router';!
import config from '../../config/environment';!
!
export default function startApp(attrs) {!
var App;!
!
var attributes = Ember.merge({}, config.APP);!
attributes = Ember.merge(attributes, attrs);!
!
Router.reopen({!
location: 'none'!
});!
!
Ember.run(function() {!
App = Application.create(attributes);!
App.setupForTesting();!
App.injectTestHelpers();!
});!
!
App.reset();!
!
return App;!
}!
• wrap all registered test helpers
• 2 types: sync and async
tests/helpers/start_app.js
injectTestHelpers
• sets up all existing registered test helpers,
including built-ins (find, visit, click, etc) on `window`
• each helper fn closes over the running app
• sync helper: returns value of running the helper
• async helper: complicated code to detect when
async behavior (routing, promises, ajax) is in
progress
function helper(app, name) {!
var fn = helpers[name].method;!
var meta = helpers[name].meta;!
!
return function() {!
var args = slice.call(arguments);!
var lastPromise = Test.lastPromise;!
!
args.unshift(app);!
!
// not async!
if (!meta.wait) {!
return fn.apply(app, args);!
}!
!
if (!lastPromise) {!
// It's the first async helper in current context!
lastPromise = fn.apply(app, args);!
} else {!
// wait for last helper's promise to resolve!
// and then execute!
run(function() {!
lastPromise = Test.resolve(lastPromise).then(function() {!
return fn.apply(app, args);!
});!
});!
}!
!
return lastPromise;!
};!
}!
Test.lastPromise “global”
chain onto the existing test
promise!
inside injectTestHelpers
Timeline
Test.lastPromise
Code
visit(‘/posts’); fillIn(‘input’); click(‘.submit’);
.then .then .then
visit(‘/posts’);
fillIn(‘input’);
click(‘.submit’);
magic ember async chaining
Ember Sync Test Helpers
• Used for inspecting app state or DOM
• find(selector) — just like jQuery(selector)
• currentPathName()
• currentRouteName()
• currentURL()
• pauseTest() — new!
Ember Async Test Helpers
• visit(url)
• fillIn(selector, text)
• click(selector)
• keyEvent(selector, keyCode)
• andThen(callback)
• wait() — this one is special
How does `wait` know to
wait?
• polling!
• check for active router transition
• check for pending ajax requests
• check if active runloop or Ember.run.later scheduled
• check for user-specified async via
registerWaiter(callback)
• all async helpers must return a call to `wait()`
function wait(app, value) {!
return Test.promise(function(resolve) {!
// If this is the first async promise, kick off the async test!
if (++countAsync === 1) {!
Test.adapter.asyncStart();!
}!
!
// Every 10ms, poll for the async thing to have finished!
var watcher = setInterval(function() {!
// 1. If the router is loading, keep polling!
var routerIsLoading = !!app.__container__.lookup('router:main').router.activeTransition;!
if (routerIsLoading) { return; }!
!
// 2. If there are pending Ajax requests, keep polling!
if (Test.pendingAjaxRequests) { return; }!
!
// 3. If there are scheduled timers or we are inside of a run loop, keep polling!
if (run.hasScheduledTimers() || run.currentRunLoop) { return; }!
if (Test.waiters && Test.waiters.any(function(waiter) {!
var context = waiter[0];!
var callback = waiter[1];!
return !callback.call(context);!
})) { return; }!
// Stop polling!
clearInterval(watcher);!
!
// If this is the last async promise, end the async test!
if (--countAsync === 0) {!
Test.adapter.asyncEnd();!
}!
!
// Synchronously resolve the promise!
run(null, resolve, value);!
}, 10);!
});!
}!
check for ajax
poll every 10ms
check for active routing
transition
check user-registered
waiters via registerWaiter()
wait()
A good test & framework
should guide you
visit(‘/foo’) The URL '/foo' did not match any routes …
click(‘input.button’) Element input.button not found.
Error messages can guide you, sometimes
? TypeError: Cannot read property 'get' of undefined
but not all the time
Ember.Test.registerAsyncHelper('signIn', function(app) {!
! visit('/signin');!
! fillIn('input.email', 'abc@def.com');!
! fillIn('input.password', 'secret');!
! click('button.sign-in');!
});!
test('signs in and then does X', function(){!
signIn();!
!
andThen(function(){!
!// ... I am signed in!!
});!
});!
Use domain-specific async helpers
Ember.Test.registerHelper('navbarContains', function(app, text)
{!
! var el = find('.nav-bar:contains(' + text + ')');!
! ok(el.length, 'has a nav bar with text: ' + text);!
});!
test('sees name in nav-bar', function(){!
! visit('/');!
! andThen(function(){!
! ! navbarContains('My App');!
! });!
});!
Use domain-specific sync helpers
• (alpha)
• `npm install —save-dev ember-cli-acceptance-test-helpers`
• expectComponent(componentName)
• clickComponent(componentName)
• expectElement(selector)
• withinElement(), expectInput() — coming soon
ember-cli-acceptance-test-helpers
• expectComponent
• clickComponent!
!
• expectElement
No component called X was found in the container
Expected to find component X
Found 3 of .some-div but expected 2
Found 1 of .some-div but 0 containing “some text”
ember-cli-acceptance-test-helpers
http://devopsreactions.tumblr.com/
testing your own code
doesn’t have to be like this
Thank you
Cory Forsyth
@bantic
Photo credits
! !
http://devopsreactions.tumblr.com/!
www.ohmagif.com
Cory Forsyth
@bantic
Photo credits
! !
http://devopsreactions.tumblr.com/!
www.ohmagif.com
• Slides: http://bit.ly/ember-testing-talk-to
• ember-test-helpers
• ember-cli-acceptance-test-helpers
• ember-cli-mocha
• setupForTesting()
• injectTestHelpers()
• wait() async test helper
• ember-cli-qunit
• ember-qunit
Links

Contenu connexe

Tendances

AngularJS Unit Test
AngularJS Unit TestAngularJS Unit Test
AngularJS Unit TestChiew Carol
 
Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Morris Singer
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsFITC
 
Intro to testing Javascript with jasmine
Intro to testing Javascript with jasmineIntro to testing Javascript with jasmine
Intro to testing Javascript with jasmineTimothy Oxley
 
You do not need automation engineer - Sqa Days - 2015 - EN
You do not need automation engineer  - Sqa Days - 2015 - ENYou do not need automation engineer  - Sqa Days - 2015 - EN
You do not need automation engineer - Sqa Days - 2015 - ENIakiv Kramarenko
 
AngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and JasmineAngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and Jasminefoxp2code
 
Unit Testing JavaScript Applications
Unit Testing JavaScript ApplicationsUnit Testing JavaScript Applications
Unit Testing JavaScript ApplicationsYnon Perek
 
AngularJS Unit Testing
AngularJS Unit TestingAngularJS Unit Testing
AngularJS Unit TestingPrince Norin
 
Angularjs - Unit testing introduction
Angularjs - Unit testing introductionAngularjs - Unit testing introduction
Angularjs - Unit testing introductionNir Kaufman
 
Testing in AngularJS
Testing in AngularJSTesting in AngularJS
Testing in AngularJSPeter Drinnan
 
Painless JavaScript Testing with Jest
Painless JavaScript Testing with JestPainless JavaScript Testing with Jest
Painless JavaScript Testing with JestMichał Pierzchała
 
Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express MiddlewareMorris Singer
 
Ember and containers
Ember and containersEmber and containers
Ember and containersMatthew Beale
 
Rapid prototyping and easy testing with ember cli mirage
Rapid prototyping and easy testing with ember cli mirageRapid prototyping and easy testing with ember cli mirage
Rapid prototyping and easy testing with ember cli mirageKrzysztof Bialek
 
Unit testing in JavaScript with Jasmine and Karma
Unit testing in JavaScript with Jasmine and KarmaUnit testing in JavaScript with Jasmine and Karma
Unit testing in JavaScript with Jasmine and KarmaAndrey Kolodnitsky
 
Angular JS Unit Testing - Overview
Angular JS Unit Testing - OverviewAngular JS Unit Testing - Overview
Angular JS Unit Testing - OverviewThirumal Sakthivel
 
Painless Javascript Unit Testing
Painless Javascript Unit TestingPainless Javascript Unit Testing
Painless Javascript Unit TestingBenjamin Wilson
 

Tendances (20)

AngularJS Unit Test
AngularJS Unit TestAngularJS Unit Test
AngularJS Unit Test
 
Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS Applications
 
Intro to testing Javascript with jasmine
Intro to testing Javascript with jasmineIntro to testing Javascript with jasmine
Intro to testing Javascript with jasmine
 
You do not need automation engineer - Sqa Days - 2015 - EN
You do not need automation engineer  - Sqa Days - 2015 - ENYou do not need automation engineer  - Sqa Days - 2015 - EN
You do not need automation engineer - Sqa Days - 2015 - EN
 
AngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and JasmineAngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and Jasmine
 
Full Stack Unit Testing
Full Stack Unit TestingFull Stack Unit Testing
Full Stack Unit Testing
 
Unit Testing JavaScript Applications
Unit Testing JavaScript ApplicationsUnit Testing JavaScript Applications
Unit Testing JavaScript Applications
 
AngularJS Unit Testing
AngularJS Unit TestingAngularJS Unit Testing
AngularJS Unit Testing
 
Angularjs - Unit testing introduction
Angularjs - Unit testing introductionAngularjs - Unit testing introduction
Angularjs - Unit testing introduction
 
Testing in AngularJS
Testing in AngularJSTesting in AngularJS
Testing in AngularJS
 
Painless JavaScript Testing with Jest
Painless JavaScript Testing with JestPainless JavaScript Testing with Jest
Painless JavaScript Testing with Jest
 
Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express Middleware
 
Ember and containers
Ember and containersEmber and containers
Ember and containers
 
Rapid prototyping and easy testing with ember cli mirage
Rapid prototyping and easy testing with ember cli mirageRapid prototyping and easy testing with ember cli mirage
Rapid prototyping and easy testing with ember cli mirage
 
Jasmine BDD for Javascript
Jasmine BDD for JavascriptJasmine BDD for Javascript
Jasmine BDD for Javascript
 
Unit testing in JavaScript with Jasmine and Karma
Unit testing in JavaScript with Jasmine and KarmaUnit testing in JavaScript with Jasmine and Karma
Unit testing in JavaScript with Jasmine and Karma
 
Angular JS Unit Testing - Overview
Angular JS Unit Testing - OverviewAngular JS Unit Testing - Overview
Angular JS Unit Testing - Overview
 
Painless Javascript Unit Testing
Painless Javascript Unit TestingPainless Javascript Unit Testing
Painless Javascript Unit Testing
 
Angular testing
Angular testingAngular testing
Angular testing
 

En vedette

Stackup New Languages Talk: Ember is for Everybody
Stackup New Languages Talk: Ember is for EverybodyStackup New Languages Talk: Ember is for Everybody
Stackup New Languages Talk: Ember is for EverybodyCory Forsyth
 
APIs: Internet for Robots
APIs: Internet for RobotsAPIs: Internet for Robots
APIs: Internet for RobotsCory Forsyth
 
EmberFest Mobiledoc Demo Lightning Talk
EmberFest Mobiledoc Demo Lightning TalkEmberFest Mobiledoc Demo Lightning Talk
EmberFest Mobiledoc Demo Lightning TalkCory Forsyth
 
Making ember-wormhole work with Fastboot
Making ember-wormhole work with FastbootMaking ember-wormhole work with Fastboot
Making ember-wormhole work with FastbootCory Forsyth
 
Microsoft tech talk march 28 2014
Microsoft tech talk march 28 2014Microsoft tech talk march 28 2014
Microsoft tech talk march 28 2014Cory Forsyth
 
OAuth 2.0 Misconceptions
OAuth 2.0 MisconceptionsOAuth 2.0 Misconceptions
OAuth 2.0 MisconceptionsCory Forsyth
 
Chrome Extensions at Manhattan JS
Chrome Extensions at Manhattan JSChrome Extensions at Manhattan JS
Chrome Extensions at Manhattan JSCory Forsyth
 
E tutorial -
E tutorial -E tutorial -
E tutorial -PSPCL
 
Torii: Ember.js Authentication Library
Torii: Ember.js Authentication LibraryTorii: Ember.js Authentication Library
Torii: Ember.js Authentication LibraryCory Forsyth
 
Gradle起步走: 以CLI Application為例 @ JCConf 2014
Gradle起步走: 以CLI Application為例 @ JCConf 2014Gradle起步走: 以CLI Application為例 @ JCConf 2014
Gradle起步走: 以CLI Application為例 @ JCConf 2014Chen-en Lu
 
HTTP by Hand: Exploring HTTP/1.0, 1.1 and 2.0
HTTP by Hand: Exploring HTTP/1.0, 1.1 and 2.0HTTP by Hand: Exploring HTTP/1.0, 1.1 and 2.0
HTTP by Hand: Exploring HTTP/1.0, 1.1 and 2.0Cory Forsyth
 
Introduction to HTTP/2
Introduction to HTTP/2Introduction to HTTP/2
Introduction to HTTP/2Ido Flatow
 
Ericsson License Assisted Access (LAA) January 2015
Ericsson License Assisted Access (LAA) January 2015Ericsson License Assisted Access (LAA) January 2015
Ericsson License Assisted Access (LAA) January 2015Ericsson
 
Lte epc trial experience
Lte epc trial experienceLte epc trial experience
Lte epc trial experienceHussien Mahmoud
 
What HTTP/2.0 Will Do For You
What HTTP/2.0 Will Do For YouWhat HTTP/2.0 Will Do For You
What HTTP/2.0 Will Do For YouMark Nottingham
 

En vedette (15)

Stackup New Languages Talk: Ember is for Everybody
Stackup New Languages Talk: Ember is for EverybodyStackup New Languages Talk: Ember is for Everybody
Stackup New Languages Talk: Ember is for Everybody
 
APIs: Internet for Robots
APIs: Internet for RobotsAPIs: Internet for Robots
APIs: Internet for Robots
 
EmberFest Mobiledoc Demo Lightning Talk
EmberFest Mobiledoc Demo Lightning TalkEmberFest Mobiledoc Demo Lightning Talk
EmberFest Mobiledoc Demo Lightning Talk
 
Making ember-wormhole work with Fastboot
Making ember-wormhole work with FastbootMaking ember-wormhole work with Fastboot
Making ember-wormhole work with Fastboot
 
Microsoft tech talk march 28 2014
Microsoft tech talk march 28 2014Microsoft tech talk march 28 2014
Microsoft tech talk march 28 2014
 
OAuth 2.0 Misconceptions
OAuth 2.0 MisconceptionsOAuth 2.0 Misconceptions
OAuth 2.0 Misconceptions
 
Chrome Extensions at Manhattan JS
Chrome Extensions at Manhattan JSChrome Extensions at Manhattan JS
Chrome Extensions at Manhattan JS
 
E tutorial -
E tutorial -E tutorial -
E tutorial -
 
Torii: Ember.js Authentication Library
Torii: Ember.js Authentication LibraryTorii: Ember.js Authentication Library
Torii: Ember.js Authentication Library
 
Gradle起步走: 以CLI Application為例 @ JCConf 2014
Gradle起步走: 以CLI Application為例 @ JCConf 2014Gradle起步走: 以CLI Application為例 @ JCConf 2014
Gradle起步走: 以CLI Application為例 @ JCConf 2014
 
HTTP by Hand: Exploring HTTP/1.0, 1.1 and 2.0
HTTP by Hand: Exploring HTTP/1.0, 1.1 and 2.0HTTP by Hand: Exploring HTTP/1.0, 1.1 and 2.0
HTTP by Hand: Exploring HTTP/1.0, 1.1 and 2.0
 
Introduction to HTTP/2
Introduction to HTTP/2Introduction to HTTP/2
Introduction to HTTP/2
 
Ericsson License Assisted Access (LAA) January 2015
Ericsson License Assisted Access (LAA) January 2015Ericsson License Assisted Access (LAA) January 2015
Ericsson License Assisted Access (LAA) January 2015
 
Lte epc trial experience
Lte epc trial experienceLte epc trial experience
Lte epc trial experience
 
What HTTP/2.0 Will Do For You
What HTTP/2.0 Will Do For YouWhat HTTP/2.0 Will Do For You
What HTTP/2.0 Will Do For You
 

Similaire à Ember testing internals with ember cli

Javascript tdd byandreapaciolla
Javascript tdd byandreapaciollaJavascript tdd byandreapaciolla
Javascript tdd byandreapaciollaAndrea Paciolla
 
Describe's Full of It's
Describe's Full of It'sDescribe's Full of It's
Describe's Full of It'sJim Lynch
 
Turn your spaghetti code into ravioli with JavaScript modules
Turn your spaghetti code into ravioli with JavaScript modulesTurn your spaghetti code into ravioli with JavaScript modules
Turn your spaghetti code into ravioli with JavaScript modulesjerryorr
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingVisual Engineering
 
Angular Unit Testing from the Trenches
Angular Unit Testing from the TrenchesAngular Unit Testing from the Trenches
Angular Unit Testing from the TrenchesJustin James
 
Automated javascript unit testing
Automated javascript unit testingAutomated javascript unit testing
Automated javascript unit testingryan_chambers
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaExoLeaders.com
 
(Unit) Testing in Emberjs – Munich Ember.js Meetup July 2014
(Unit) Testing in Emberjs – Munich Ember.js Meetup July 2014(Unit) Testing in Emberjs – Munich Ember.js Meetup July 2014
(Unit) Testing in Emberjs – Munich Ember.js Meetup July 2014istefo
 
Protractor framework architecture with example
Protractor framework architecture with exampleProtractor framework architecture with example
Protractor framework architecture with exampleshadabgilani
 
Unit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSUnit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSKnoldus Inc.
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockRobot Media
 
Unit Testing from Setup to Deployment
Unit Testing from Setup to DeploymentUnit Testing from Setup to Deployment
Unit Testing from Setup to DeploymentMark Niebergall
 
Quick tour to front end unit testing using jasmine
Quick tour to front end unit testing using jasmineQuick tour to front end unit testing using jasmine
Quick tour to front end unit testing using jasmineGil Fink
 
EmberJS BucharestJS
EmberJS BucharestJSEmberJS BucharestJS
EmberJS BucharestJSRemus Rusanu
 
Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014Barry Kooij
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Michelangelo van Dam
 

Similaire à Ember testing internals with ember cli (20)

Javascript tdd byandreapaciolla
Javascript tdd byandreapaciollaJavascript tdd byandreapaciolla
Javascript tdd byandreapaciolla
 
Describe's Full of It's
Describe's Full of It'sDescribe's Full of It's
Describe's Full of It's
 
Turn your spaghetti code into ravioli with JavaScript modules
Turn your spaghetti code into ravioli with JavaScript modulesTurn your spaghetti code into ravioli with JavaScript modules
Turn your spaghetti code into ravioli with JavaScript modules
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
Angular Unit Testing
Angular Unit TestingAngular Unit Testing
Angular Unit Testing
 
Angular Unit Testing from the Trenches
Angular Unit Testing from the TrenchesAngular Unit Testing from the Trenches
Angular Unit Testing from the Trenches
 
Automated javascript unit testing
Automated javascript unit testingAutomated javascript unit testing
Automated javascript unit testing
 
Unit testing
Unit testingUnit testing
Unit testing
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with Karma
 
(Unit) Testing in Emberjs – Munich Ember.js Meetup July 2014
(Unit) Testing in Emberjs – Munich Ember.js Meetup July 2014(Unit) Testing in Emberjs – Munich Ember.js Meetup July 2014
(Unit) Testing in Emberjs – Munich Ember.js Meetup July 2014
 
Junit_.pptx
Junit_.pptxJunit_.pptx
Junit_.pptx
 
Protractor framework architecture with example
Protractor framework architecture with exampleProtractor framework architecture with example
Protractor framework architecture with example
 
Unit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSUnit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJS
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
 
Unit Testing from Setup to Deployment
Unit Testing from Setup to DeploymentUnit Testing from Setup to Deployment
Unit Testing from Setup to Deployment
 
Turner js
Turner jsTurner js
Turner js
 
Quick tour to front end unit testing using jasmine
Quick tour to front end unit testing using jasmineQuick tour to front end unit testing using jasmine
Quick tour to front end unit testing using jasmine
 
EmberJS BucharestJS
EmberJS BucharestJSEmberJS BucharestJS
EmberJS BucharestJS
 
Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014Unit testing @ WordPress Meetup Tilburg 7 januari 2014
Unit testing @ WordPress Meetup Tilburg 7 januari 2014
 
Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8Unit testing after Zend Framework 1.8
Unit testing after Zend Framework 1.8
 

Dernier

Arduino_CSE ece ppt for working and principal of arduino.ppt
Arduino_CSE ece ppt for working and principal of arduino.pptArduino_CSE ece ppt for working and principal of arduino.ppt
Arduino_CSE ece ppt for working and principal of arduino.pptSAURABHKUMAR892774
 
Call Girls Narol 7397865700 Independent Call Girls
Call Girls Narol 7397865700 Independent Call GirlsCall Girls Narol 7397865700 Independent Call Girls
Call Girls Narol 7397865700 Independent Call Girlsssuser7cb4ff
 
welding defects observed during the welding
welding defects observed during the weldingwelding defects observed during the welding
welding defects observed during the weldingMuhammadUzairLiaqat
 
Work Experience-Dalton Park.pptxfvvvvvvv
Work Experience-Dalton Park.pptxfvvvvvvvWork Experience-Dalton Park.pptxfvvvvvvv
Work Experience-Dalton Park.pptxfvvvvvvvLewisJB
 
Past, Present and Future of Generative AI
Past, Present and Future of Generative AIPast, Present and Future of Generative AI
Past, Present and Future of Generative AIabhishek36461
 
Transport layer issues and challenges - Guide
Transport layer issues and challenges - GuideTransport layer issues and challenges - Guide
Transport layer issues and challenges - GuideGOPINATHS437943
 
Indian Dairy Industry Present Status and.ppt
Indian Dairy Industry Present Status and.pptIndian Dairy Industry Present Status and.ppt
Indian Dairy Industry Present Status and.pptMadan Karki
 
complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...asadnawaz62
 
Vishratwadi & Ghorpadi Bridge Tender documents
Vishratwadi & Ghorpadi Bridge Tender documentsVishratwadi & Ghorpadi Bridge Tender documents
Vishratwadi & Ghorpadi Bridge Tender documentsSachinPawar510423
 
Correctly Loading Incremental Data at Scale
Correctly Loading Incremental Data at ScaleCorrectly Loading Incremental Data at Scale
Correctly Loading Incremental Data at ScaleAlluxio, Inc.
 
An experimental study in using natural admixture as an alternative for chemic...
An experimental study in using natural admixture as an alternative for chemic...An experimental study in using natural admixture as an alternative for chemic...
An experimental study in using natural admixture as an alternative for chemic...Chandu841456
 
8251 universal synchronous asynchronous receiver transmitter
8251 universal synchronous asynchronous receiver transmitter8251 universal synchronous asynchronous receiver transmitter
8251 universal synchronous asynchronous receiver transmitterShivangiSharma879191
 
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor CatchersTechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catcherssdickerson1
 
Oxy acetylene welding presentation note.
Oxy acetylene welding presentation note.Oxy acetylene welding presentation note.
Oxy acetylene welding presentation note.eptoze12
 
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfg
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfgUnit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfg
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfgsaravananr517913
 
Sachpazis Costas: Geotechnical Engineering: A student's Perspective Introduction
Sachpazis Costas: Geotechnical Engineering: A student's Perspective IntroductionSachpazis Costas: Geotechnical Engineering: A student's Perspective Introduction
Sachpazis Costas: Geotechnical Engineering: A student's Perspective IntroductionDr.Costas Sachpazis
 
Application of Residue Theorem to evaluate real integrations.pptx
Application of Residue Theorem to evaluate real integrations.pptxApplication of Residue Theorem to evaluate real integrations.pptx
Application of Residue Theorem to evaluate real integrations.pptx959SahilShah
 
Earthing details of Electrical Substation
Earthing details of Electrical SubstationEarthing details of Electrical Substation
Earthing details of Electrical Substationstephanwindworld
 

Dernier (20)

Arduino_CSE ece ppt for working and principal of arduino.ppt
Arduino_CSE ece ppt for working and principal of arduino.pptArduino_CSE ece ppt for working and principal of arduino.ppt
Arduino_CSE ece ppt for working and principal of arduino.ppt
 
Call Girls Narol 7397865700 Independent Call Girls
Call Girls Narol 7397865700 Independent Call GirlsCall Girls Narol 7397865700 Independent Call Girls
Call Girls Narol 7397865700 Independent Call Girls
 
welding defects observed during the welding
welding defects observed during the weldingwelding defects observed during the welding
welding defects observed during the welding
 
Design and analysis of solar grass cutter.pdf
Design and analysis of solar grass cutter.pdfDesign and analysis of solar grass cutter.pdf
Design and analysis of solar grass cutter.pdf
 
Work Experience-Dalton Park.pptxfvvvvvvv
Work Experience-Dalton Park.pptxfvvvvvvvWork Experience-Dalton Park.pptxfvvvvvvv
Work Experience-Dalton Park.pptxfvvvvvvv
 
Past, Present and Future of Generative AI
Past, Present and Future of Generative AIPast, Present and Future of Generative AI
Past, Present and Future of Generative AI
 
Transport layer issues and challenges - Guide
Transport layer issues and challenges - GuideTransport layer issues and challenges - Guide
Transport layer issues and challenges - Guide
 
Indian Dairy Industry Present Status and.ppt
Indian Dairy Industry Present Status and.pptIndian Dairy Industry Present Status and.ppt
Indian Dairy Industry Present Status and.ppt
 
complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...
 
Vishratwadi & Ghorpadi Bridge Tender documents
Vishratwadi & Ghorpadi Bridge Tender documentsVishratwadi & Ghorpadi Bridge Tender documents
Vishratwadi & Ghorpadi Bridge Tender documents
 
Correctly Loading Incremental Data at Scale
Correctly Loading Incremental Data at ScaleCorrectly Loading Incremental Data at Scale
Correctly Loading Incremental Data at Scale
 
An experimental study in using natural admixture as an alternative for chemic...
An experimental study in using natural admixture as an alternative for chemic...An experimental study in using natural admixture as an alternative for chemic...
An experimental study in using natural admixture as an alternative for chemic...
 
8251 universal synchronous asynchronous receiver transmitter
8251 universal synchronous asynchronous receiver transmitter8251 universal synchronous asynchronous receiver transmitter
8251 universal synchronous asynchronous receiver transmitter
 
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor CatchersTechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
 
Oxy acetylene welding presentation note.
Oxy acetylene welding presentation note.Oxy acetylene welding presentation note.
Oxy acetylene welding presentation note.
 
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfg
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfgUnit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfg
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfg
 
Sachpazis Costas: Geotechnical Engineering: A student's Perspective Introduction
Sachpazis Costas: Geotechnical Engineering: A student's Perspective IntroductionSachpazis Costas: Geotechnical Engineering: A student's Perspective Introduction
Sachpazis Costas: Geotechnical Engineering: A student's Perspective Introduction
 
Application of Residue Theorem to evaluate real integrations.pptx
Application of Residue Theorem to evaluate real integrations.pptxApplication of Residue Theorem to evaluate real integrations.pptx
Application of Residue Theorem to evaluate real integrations.pptx
 
POWER SYSTEMS-1 Complete notes examples
POWER SYSTEMS-1 Complete notes  examplesPOWER SYSTEMS-1 Complete notes  examples
POWER SYSTEMS-1 Complete notes examples
 
Earthing details of Electrical Substation
Earthing details of Electrical SubstationEarthing details of Electrical Substation
Earthing details of Electrical Substation
 

Ember testing internals with ember cli

  • 1. Ember Testing Internals with Ember-CLI Cory Forsyth @bantic
  • 2. 201 Created Matthew BealeCory Forsyth http://201-created.com/
  • 4. The Ember-CLI Testing Triumvirate • The test harness (tests/index.html) • Unit Test Affordances • Acceptance Test Affordances
  • 5. $ ember new my-app
  • 6.
  • 7. Ember-CLI makes testing Easy • `ember generate X` creates test for X • 14 test types: • acceptance, adapter, component, controller, • helper, initializer, mixin, model, route, • serializer, service, transform, util, view
  • 8. Ember-CLI Test Harness • A real strength of Ember-CLI • Ember-CLI builds tests/index.html for you • QUnit is built-in (more on this later)
  • 9. <!DOCTYPE html>! <html>! <head>! <meta charset="utf-8">! <meta http-equiv="X-UA-Compatible" content="IE=edge">! <title>EmberTestingTalk Tests</title>! <meta name="description" content="">! <meta name="viewport" content="width=device-width, initial-scale=1">! ! {{content-for 'head'}}! {{content-for 'test-head'}}! ! <link rel="stylesheet" href="assets/vendor.css">! <link rel="stylesheet" href="assets/ember-testing-talk.css">! <link rel="stylesheet" href="assets/test-support.css">! <style>! #ember-testing-container {! position: absolute;! background: white;! bottom: 0;! right: 0;! width: 640px;! height: 384px;! overflow: auto;! z-index: 9999;! border: 1px solid #ccc;! }! #ember-testing {! zoom: 50%;! }! </style>! </head>! config in meta tag addons can modify Ember-CLI builds these makes that mini-me app on the test page tests/index.html
  • 10. <body>! <div id="qunit"></div>! <div id="qunit-fixture"></div>! ! {{content-for 'body'}}! {{content-for 'test-body'}}! <script src="assets/vendor.js"></script>! <script src="assets/test-support.js"></script>! <script src="assets/ember-testing-talk.js"></script>! <script src="testem.js"></script>! <script src="assets/test-loader.js"></script>! </body>! </html>! for QUnit addons can modify tests/index.html
  • 11. <body>! <div id="qunit"></div>! <div id="qunit-fixture"></div>! ! {{content-for 'body'}}! {{content-for 'test-body'}}! <script src="assets/vendor.js"></script>! <script src="assets/test-support.js"></script>! <script src="assets/ember-testing-talk.js"></script>! <script src="testem.js"></script>! <script src="assets/test-loader.js"></script>! </body>! </html>! jQuery, Handlebars, Ember, `app.import` QUnit, ember-qunit app code, including tests (in non-prod env) app code, including tests (in non-prod env) `require`s all the tests tests/index.html
  • 12. /* globals requirejs, require */! ! var moduleName, shouldLoad;! ! QUnit.config.urlConfig.push({ id: 'nojshint', label: 'Disable JSHint'});! ! // TODO: load based on params! for (moduleName in requirejs.entries) {! shouldLoad = false;! ! if (moduleName.match(/[-_]test$/)) { shouldLoad = true; }! if (!QUnit.urlParams.nojshint && moduleName.match(/.jshint$/)) { shouldLoad = true; }! ! if (shouldLoad) { require(moduleName); }! }! ! if (QUnit.notifications) {! QUnit.notifications({! icons: {! passed: '/assets/passed.png',! failed: '/assets/failed.png'! }! });! }! Requires every module name ending in _test or -test (named AMD modules, not npm modules or QUnit modules) test-loader.js
  • 13. module("a basic test");! ! test("this test will pass", function(){! ok(true, "yep, it did");! });! define("ember-testing-talk/tests/unit/basic-test", [], function(){! ! "use strict";! ! module("a basic test");! ! ! test("this test will pass", function(){! ! ! ok(true, "yep, it did");! ! });! }); test-loader.js requires this, QUnit runs it Ember-CLI compiles to named AMD module ending in -test tests/unit/basic-test.js
  • 14. $ ember g controller index import {! moduleFor,! test! } from 'ember-qunit';! ! moduleFor('controller:index', 'IndexController', {! // Specify the other units that are required for this test.! // needs: ['controller:foo']! });! ! // Replace this with your real tests.! test('it exists', function() {! var controller = this.subject();! ok(controller);! });!
  • 15. Ember-CLI Test Harness • tests/index.html: • app code as named AMD modules • app test code as named AMD modules • vendor js (Ember, Handlebars, jQuery) • test support (QUnit, ember-qunit AMD) • test-loader.js: `require`s each AMD test module • QUnit runs the tests
  • 16. Ember-CLI Test Harness • How does QUnit and ember-qunit end up in test- support.js? • ember-cli-qunit! (it is an ember-cli addon)
  • 18. Anatomy of a Unit Test • How does Ember actually run a unit test? • What does that boilerplate do?
  • 19. import {! moduleFor,! test! } from 'ember-qunit';! ! moduleFor('controller:index', 'IndexController', {! // Specify the other units that are required for this test.! // needs: ['controller:foo']! });! ! // Replace this with your real tests.! test('it exists', function() {! var controller = this.subject();! ok(controller);! });! tests/unit/controllers/index-test.js
  • 20. import {! moduleFor,! test! } from 'ember-qunit';! ! moduleFor('controller:index', 'IndexController', {! // Specify the other units that are required for this test.! // needs: ['controller:foo']! });! ! // Replace this with your real tests.! test('it exists', function() {! var controller = this.subject();! ok(controller);! });! tests/unit/controllers/index-test.js
  • 21. ember-qunit • imported via ember-cli-qunit addon • provides `moduleFor` • also: `moduleForModel`, `moduleForComponent` • provides `test`
  • 22. ember-qunit: moduleFor • wraps QUnit’s native `QUnit.module` • creates an isolated container with `needs` array • provides a context for test: • this.subject(), this.container, etc
  • 23. ember-qunit: moduleForX • moduleForComponent • registers my-component.js and my-component.hbs • connects the template to the component as ‘layout’ • adds `this.render`, `this.append` and `this.$` • moduleForModel • sets up ember-data (registers default transforms, etc) • adds `this.store()` • registers application:adapter, defaults to DS.FixtureAdapter
  • 24. ember-qunit: test • wraps QUnit’s native `QUnit.test` • casts the test function result to a promise • uses `stop` and `start` to handle potential async • if you `return` a promise, the test will handle it correctly • runs the promise resolution in an Ember.run loop
  • 25. ember-qunit • Builds on ember-test-helpers (library) • ember-test-helpers is test-framework-agnostic • provides methods for creating test suites (aka QUnit modules), setup/teardown, etc • future framework adapters can build on it • ember-cli-mocha!
  • 27. Ember Testing Affordances • Two primary types of tests in Ember: • Unit Tests • need isolated containers, specific setup • use moduleFor
  • 28. Ember Testing Affordances • Two primary types of tests in Ember: • Unit Tests and • Acceptance Tests • Totally different animal • must manage async, interact with DOM
  • 29. Ember Acceptance Tests $ ember g acceptance-test index
  • 30. import Ember from 'ember';! import startApp from '../helpers/start-app';! ! var App;! ! module('Acceptance: Index', {! setup: function() {! App = startApp();! },! teardown: function() {! Ember.run(App, 'destroy');! }! });! ! test('visiting /', function() {! visit('/');! ! andThen(function() {! equal(currentPath(), 'index');! });! });! tests/unit/controllers/index-test.js
  • 31. import Ember from 'ember';! import startApp from '../helpers/start-app';! ! var App;! ! module('Acceptance: Index', {! setup: function() {! App = startApp();! },! teardown: function() {! Ember.run(App, 'destroy');! }! });! ! test('visiting /', function() {! visit('/');! ! andThen(function() {! equal(currentPath(), 'index');! });! });! tests/unit/controllers/index-test.js What if visiting / takes 5 seconds? How does this know to wait?
  • 32. import Ember from 'ember';! import startApp from '../helpers/start-app';! ! var App;! ! module('Acceptance: Index', {! setup: function() {! App = startApp();! },! teardown: function() {! Ember.run(App, 'destroy');! }! });! ! test('visiting /', function() {! visit('/');! ! andThen(function() {! equal(currentPath(), 'index');! });! });! What if visiting / takes 5 seconds? How does this know to wait? tests/unit/controllers/index-test.js
  • 33. import Ember from 'ember';! import startApp from '../helpers/start-app';! ! var App;! ! module('Acceptance: Index', {! setup: function() {! App = startApp();! },! teardown: function() {! Ember.run(App, 'destroy');! }! });! ! test('visiting /', function() {! visit('/');! ! andThen(function() {! equal(currentPath(), 'index');! });! });! vanilla QUnit module tests/acceptance/index-test.js
  • 34. import Ember from 'ember';! import startApp from '../helpers/start-app';! ! var App;! ! module('Acceptance: Index', {! setup: function() {! App = startApp();! },! teardown: function() {! Ember.run(App, 'destroy');! }! });! ! test('visiting /', function() {! visit('/');! ! andThen(function() {! equal(currentPath(), 'index');! });! });! vanilla QUnit module special test helpers: visit, andThen, currentPath tests/acceptance/index-test.js
  • 35. import Ember from 'ember';! import startApp from '../helpers/start-app';! ! var App;! ! module('Acceptance: Index', {! setup: function() {! App = startApp();! },! teardown: function() {! Ember.run(App, 'destroy');! }! });! ! test('visiting /', function() {! visit('/');! ! andThen(function() {! equal(currentPath(), 'index');! });! });! What is `startApp`? tests/acceptance/index-test.js
  • 36. import Ember from 'ember';! import Application from '../../app';! import Router from '../../router';! import config from '../../config/environment';! ! export default function startApp(attrs) {! var App;! ! var attributes = Ember.merge({}, config.APP);! attributes = Ember.merge(attributes, attrs);! ! Router.reopen({! location: 'none'! });! ! Ember.run(function() {! App = Application.create(attributes);! App.setupForTesting();! App.injectTestHelpers();! });! ! App.reset();! ! return App;! }! don’t change URL start application tests/helpers/start_app.js
  • 37. import Ember from 'ember';! import Application from '../../app';! import Router from '../../router';! import config from '../../config/environment';! ! export default function startApp(attrs) {! var App;! ! var attributes = Ember.merge({}, config.APP);! attributes = Ember.merge(attributes, attrs);! ! Router.reopen({! location: 'none'! });! ! Ember.run(function() {! App = Application.create(attributes);! App.setupForTesting();! App.injectTestHelpers();! });! ! App.reset();! ! return App;! }! • set Ember.testing = true • set a test adapter • prep for ajax: • listeners for ajaxSend, ajaxComplete tests/helpers/start_app.js
  • 38. import Ember from 'ember';! import Application from '../../app';! import Router from '../../router';! import config from '../../config/environment';! ! export default function startApp(attrs) {! var App;! ! var attributes = Ember.merge({}, config.APP);! attributes = Ember.merge(attributes, attrs);! ! Router.reopen({! location: 'none'! });! ! Ember.run(function() {! App = Application.create(attributes);! App.setupForTesting();! App.injectTestHelpers();! });! ! App.reset();! ! return App;! }! • wrap all registered test helpers • 2 types: sync and async tests/helpers/start_app.js
  • 39. injectTestHelpers • sets up all existing registered test helpers, including built-ins (find, visit, click, etc) on `window` • each helper fn closes over the running app • sync helper: returns value of running the helper • async helper: complicated code to detect when async behavior (routing, promises, ajax) is in progress
  • 40. function helper(app, name) {! var fn = helpers[name].method;! var meta = helpers[name].meta;! ! return function() {! var args = slice.call(arguments);! var lastPromise = Test.lastPromise;! ! args.unshift(app);! ! // not async! if (!meta.wait) {! return fn.apply(app, args);! }! ! if (!lastPromise) {! // It's the first async helper in current context! lastPromise = fn.apply(app, args);! } else {! // wait for last helper's promise to resolve! // and then execute! run(function() {! lastPromise = Test.resolve(lastPromise).then(function() {! return fn.apply(app, args);! });! });! }! ! return lastPromise;! };! }! Test.lastPromise “global” chain onto the existing test promise! inside injectTestHelpers
  • 41. Timeline Test.lastPromise Code visit(‘/posts’); fillIn(‘input’); click(‘.submit’); .then .then .then visit(‘/posts’); fillIn(‘input’); click(‘.submit’); magic ember async chaining
  • 42. Ember Sync Test Helpers • Used for inspecting app state or DOM • find(selector) — just like jQuery(selector) • currentPathName() • currentRouteName() • currentURL() • pauseTest() — new!
  • 43. Ember Async Test Helpers • visit(url) • fillIn(selector, text) • click(selector) • keyEvent(selector, keyCode) • andThen(callback) • wait() — this one is special
  • 44. How does `wait` know to wait? • polling! • check for active router transition • check for pending ajax requests • check if active runloop or Ember.run.later scheduled • check for user-specified async via registerWaiter(callback) • all async helpers must return a call to `wait()`
  • 45. function wait(app, value) {! return Test.promise(function(resolve) {! // If this is the first async promise, kick off the async test! if (++countAsync === 1) {! Test.adapter.asyncStart();! }! ! // Every 10ms, poll for the async thing to have finished! var watcher = setInterval(function() {! // 1. If the router is loading, keep polling! var routerIsLoading = !!app.__container__.lookup('router:main').router.activeTransition;! if (routerIsLoading) { return; }! ! // 2. If there are pending Ajax requests, keep polling! if (Test.pendingAjaxRequests) { return; }! ! // 3. If there are scheduled timers or we are inside of a run loop, keep polling! if (run.hasScheduledTimers() || run.currentRunLoop) { return; }! if (Test.waiters && Test.waiters.any(function(waiter) {! var context = waiter[0];! var callback = waiter[1];! return !callback.call(context);! })) { return; }! // Stop polling! clearInterval(watcher);! ! // If this is the last async promise, end the async test! if (--countAsync === 0) {! Test.adapter.asyncEnd();! }! ! // Synchronously resolve the promise! run(null, resolve, value);! }, 10);! });! }! check for ajax poll every 10ms check for active routing transition check user-registered waiters via registerWaiter() wait()
  • 46. A good test & framework should guide you
  • 47. visit(‘/foo’) The URL '/foo' did not match any routes … click(‘input.button’) Element input.button not found. Error messages can guide you, sometimes
  • 48. ? TypeError: Cannot read property 'get' of undefined but not all the time
  • 49. Ember.Test.registerAsyncHelper('signIn', function(app) {! ! visit('/signin');! ! fillIn('input.email', 'abc@def.com');! ! fillIn('input.password', 'secret');! ! click('button.sign-in');! });! test('signs in and then does X', function(){! signIn();! ! andThen(function(){! !// ... I am signed in!! });! });! Use domain-specific async helpers
  • 50. Ember.Test.registerHelper('navbarContains', function(app, text) {! ! var el = find('.nav-bar:contains(' + text + ')');! ! ok(el.length, 'has a nav bar with text: ' + text);! });! test('sees name in nav-bar', function(){! ! visit('/');! ! andThen(function(){! ! ! navbarContains('My App');! ! });! });! Use domain-specific sync helpers
  • 51. • (alpha) • `npm install —save-dev ember-cli-acceptance-test-helpers` • expectComponent(componentName) • clickComponent(componentName) • expectElement(selector) • withinElement(), expectInput() — coming soon ember-cli-acceptance-test-helpers
  • 52. • expectComponent • clickComponent! ! • expectElement No component called X was found in the container Expected to find component X Found 3 of .some-div but expected 2 Found 1 of .some-div but 0 containing “some text” ember-cli-acceptance-test-helpers
  • 53. http://devopsreactions.tumblr.com/ testing your own code doesn’t have to be like this
  • 54. Thank you Cory Forsyth @bantic Photo credits ! ! http://devopsreactions.tumblr.com/! www.ohmagif.com
  • 55. Cory Forsyth @bantic Photo credits ! ! http://devopsreactions.tumblr.com/! www.ohmagif.com • Slides: http://bit.ly/ember-testing-talk-to • ember-test-helpers • ember-cli-acceptance-test-helpers • ember-cli-mocha • setupForTesting() • injectTestHelpers() • wait() async test helper • ember-cli-qunit • ember-qunit Links