SlideShare a Scribd company logo
1 of 55
Download to read offline
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

More Related Content

What's hot

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
 

What's hot (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
 

Viewers also liked

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
 

Viewers also liked (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
 

Similar to 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
 

Similar to 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
 

Recently uploaded

Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Christo Ananth
 
UNIT-II FMM-Flow Through Circular Conduits
UNIT-II FMM-Flow Through Circular ConduitsUNIT-II FMM-Flow Through Circular Conduits
UNIT-II FMM-Flow Through Circular Conduitsrknatarajan
 
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...ranjana rawat
 
Porous Ceramics seminar and technical writing
Porous Ceramics seminar and technical writingPorous Ceramics seminar and technical writing
Porous Ceramics seminar and technical writingrakeshbaidya232001
 
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur EscortsHigh Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escortsranjana rawat
 
Online banking management system project.pdf
Online banking management system project.pdfOnline banking management system project.pdf
Online banking management system project.pdfKamal Acharya
 
(ANJALI) Dange Chowk Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANJALI) Dange Chowk Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(ANJALI) Dange Chowk Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANJALI) Dange Chowk Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...ranjana rawat
 
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...Call Girls in Nagpur High Profile
 
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Bookingdharasingh5698
 
Glass Ceramics: Processing and Properties
Glass Ceramics: Processing and PropertiesGlass Ceramics: Processing and Properties
Glass Ceramics: Processing and PropertiesPrabhanshu Chaturvedi
 
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service Nashik
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service NashikCall Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service Nashik
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service NashikCall Girls in Nagpur High Profile
 
Introduction to IEEE STANDARDS and its different types.pptx
Introduction to IEEE STANDARDS and its different types.pptxIntroduction to IEEE STANDARDS and its different types.pptx
Introduction to IEEE STANDARDS and its different types.pptxupamatechverse
 
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...ranjana rawat
 
MANUFACTURING PROCESS-II UNIT-1 THEORY OF METAL CUTTING
MANUFACTURING PROCESS-II UNIT-1 THEORY OF METAL CUTTINGMANUFACTURING PROCESS-II UNIT-1 THEORY OF METAL CUTTING
MANUFACTURING PROCESS-II UNIT-1 THEORY OF METAL CUTTINGSIVASHANKAR N
 
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdfONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdfKamal Acharya
 
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...ranjana rawat
 
UNIT-III FMM. DIMENSIONAL ANALYSIS
UNIT-III FMM.        DIMENSIONAL ANALYSISUNIT-III FMM.        DIMENSIONAL ANALYSIS
UNIT-III FMM. DIMENSIONAL ANALYSISrknatarajan
 
MANUFACTURING PROCESS-II UNIT-2 LATHE MACHINE
MANUFACTURING PROCESS-II UNIT-2 LATHE MACHINEMANUFACTURING PROCESS-II UNIT-2 LATHE MACHINE
MANUFACTURING PROCESS-II UNIT-2 LATHE MACHINESIVASHANKAR N
 
College Call Girls Nashik Nehal 7001305949 Independent Escort Service Nashik
College Call Girls Nashik Nehal 7001305949 Independent Escort Service NashikCollege Call Girls Nashik Nehal 7001305949 Independent Escort Service Nashik
College Call Girls Nashik Nehal 7001305949 Independent Escort Service NashikCall Girls in Nagpur High Profile
 
Russian Call Girls in Nagpur Grishma Call 7001035870 Meet With Nagpur Escorts
Russian Call Girls in Nagpur Grishma Call 7001035870 Meet With Nagpur EscortsRussian Call Girls in Nagpur Grishma Call 7001035870 Meet With Nagpur Escorts
Russian Call Girls in Nagpur Grishma Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur High Profile
 

Recently uploaded (20)

Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
 
UNIT-II FMM-Flow Through Circular Conduits
UNIT-II FMM-Flow Through Circular ConduitsUNIT-II FMM-Flow Through Circular Conduits
UNIT-II FMM-Flow Through Circular Conduits
 
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
 
Porous Ceramics seminar and technical writing
Porous Ceramics seminar and technical writingPorous Ceramics seminar and technical writing
Porous Ceramics seminar and technical writing
 
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur EscortsHigh Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
 
Online banking management system project.pdf
Online banking management system project.pdfOnline banking management system project.pdf
Online banking management system project.pdf
 
(ANJALI) Dange Chowk Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANJALI) Dange Chowk Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(ANJALI) Dange Chowk Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANJALI) Dange Chowk Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
 
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
 
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
 
Glass Ceramics: Processing and Properties
Glass Ceramics: Processing and PropertiesGlass Ceramics: Processing and Properties
Glass Ceramics: Processing and Properties
 
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service Nashik
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service NashikCall Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service Nashik
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service Nashik
 
Introduction to IEEE STANDARDS and its different types.pptx
Introduction to IEEE STANDARDS and its different types.pptxIntroduction to IEEE STANDARDS and its different types.pptx
Introduction to IEEE STANDARDS and its different types.pptx
 
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
 
MANUFACTURING PROCESS-II UNIT-1 THEORY OF METAL CUTTING
MANUFACTURING PROCESS-II UNIT-1 THEORY OF METAL CUTTINGMANUFACTURING PROCESS-II UNIT-1 THEORY OF METAL CUTTING
MANUFACTURING PROCESS-II UNIT-1 THEORY OF METAL CUTTING
 
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdfONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
 
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
 
UNIT-III FMM. DIMENSIONAL ANALYSIS
UNIT-III FMM.        DIMENSIONAL ANALYSISUNIT-III FMM.        DIMENSIONAL ANALYSIS
UNIT-III FMM. DIMENSIONAL ANALYSIS
 
MANUFACTURING PROCESS-II UNIT-2 LATHE MACHINE
MANUFACTURING PROCESS-II UNIT-2 LATHE MACHINEMANUFACTURING PROCESS-II UNIT-2 LATHE MACHINE
MANUFACTURING PROCESS-II UNIT-2 LATHE MACHINE
 
College Call Girls Nashik Nehal 7001305949 Independent Escort Service Nashik
College Call Girls Nashik Nehal 7001305949 Independent Escort Service NashikCollege Call Girls Nashik Nehal 7001305949 Independent Escort Service Nashik
College Call Girls Nashik Nehal 7001305949 Independent Escort Service Nashik
 
Russian Call Girls in Nagpur Grishma Call 7001035870 Meet With Nagpur Escorts
Russian Call Girls in Nagpur Grishma Call 7001035870 Meet With Nagpur EscortsRussian Call Girls in Nagpur Grishma Call 7001035870 Meet With Nagpur Escorts
Russian Call Girls in Nagpur Grishma Call 7001035870 Meet With Nagpur Escorts
 

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