SlideShare a Scribd company logo
1 of 69
Download to read offline
Testing Ember.js Apps 
Managing Dependency
A guaranteed Successful Talk
A guaranteed Successful Talk ✓
@mixonic 
httP://madhatted.com 
matt.beale@madhatted.com
201 Created 
We build õ-age apps with Ember.js. We take 
teams from £ to • in no time flat.
http://bit.ly/emberfest-edge
A conference planning app 
• Submitting a talk 
• Purchasing a ticket 
• Preparing a talk
• Submitting a talk 
• Purchasing a ticket 
• Preparing a talk 
The domain 
A conference planning app
A conference planning app 
• Submitting a talk 
• Purchasing a ticket 
• Preparing a talk
A conference planning app 
• Adapting to a server 
• Managing asynchronous APIs 
• Browser APIs 
• Submitting a talk 
• Purchasing a ticket 
• Preparing a talk 
ᗜੂͦ﹏ᗜੂͦ
A conference planning app 
• Adapting to a server 
• Managing asynchronous APIs 
• Browser APIs 
• Submitting a talk 
• Purchasing a ticket 
• Preparing a talk 
The domain
A conference planning app What is this?! 
• Adapting to a server 
• Managing asynchronous APIs 
• Browser APIs 
• Submitting a talk 
• Purchasing a ticket 
• Preparing a talk 
ᗜੂͦ﹏ᗜੂͦ
Dependencies
Dependencies 
Domain
Dependencies 
Domain 
Own it
Dependencies 
Domain 
Own it
Ember-CLI Review 
1 // app/utils/some-object.js! 
2 export default SomeObject;! 
3 export default Ember.Route.extend({! 
4 });! 
5 ! 
6 import MyObject from "app/utils/some-object";! 
7 ! 
8 // app/utils/many-things.js! 
9 export {gadget, anotherGadget};! 
10 ! 
11 import {anotherGadget} from "app/utils/many-things";! 
12 ! 
13 // tests/routes/proposal-test.js! 
14 moduleFor("route:routeName", "Test Module Name", {! 
15 // options include: needs, subject, setup, teardown! 
16 });!
Constraining Dependencies 
1. Isolate implementation 
2. Normalize behavior
Constraining a dependency 
1 // app/route/proposal.js! 
2 import Proposal from "../model/proposal";! 
3 ! 
4 export default Ember.Route.extend({! 
5 ! 
6 model: function(){! 
7 var recovered = window.localStorage.getItem('proposalSnapshot');! 
8 if (recovered) {! 
9 return Proposal.load(recovered);! 
10 } else {! 
11 return Proposal.build();! 
12 }! 
13 }! 
14 ! 
15 });!
Constraining a dependency Implementation 
1 // app/route/proposal.js! 
2 import Proposal from "../model/proposal";! 
3 ! 
4 export default Ember.Route.extend({! 
5 ! 
6 model: function(){! 
7 var recovered = window.localStorage.getItem('proposalSnapshot');! 
8 if (recovered) {! 
9 return Proposal.load(recovered);! 
10 } else {! 
11 return Proposal.build();! 
12 }! 
13 }! 
14 ! 
15 });!
Implementation 
Constraining a dependency 
1 // app/route/proposal.js! 
2 import Proposal from "../model/proposal";! 
3 ! 
4 export default Ember.Route.extend({! 
5 ! 
6 model: function(){! 
7 var recovered = window.localStorage.getItem('proposalSnapshot');! 
8 if (recovered) {! 
9 return Proposal.load(recovered);! 
10 } else {! 
11 return Proposal.build();! 
12 }! 
13 }! 
14 ! 
15 });! 
Synchronous behavior
Constraining a dependency 
app/route/proposal 
model lookup 
localStorage 
sync behavior
Constraining a dependency 
app/route/proposal 
model lookup 
app/utils/recovery-store 
localStorage 
sync behavior
Constraining a dependency 
1 // app/utils/recovery-store.js! 
2 ! 
3 export default Ember.Object.extend({! 
4 recover: function(key){! 
5 return new Ember.RSVP.Promise(function(resolve, reject){! 
6 resolve(window.localStorage.getItem(key));! 
7 });! 
8 }! 
9 });!
Constraining a dependency 
Isolated in recoveryStore 
1 // app/utils/recovery-store.js! 
2 ! 
3 export default Ember.Object.extend({! 
4 recover: function(key){! 
5 return new Ember.RSVP.Promise(function(resolve, reject){! 
6 resolve(window.localStorage.getItem(key));! 
7 });! 
8 }! 
9 });!
Constraining a dependency 
Isolated in recoveryStore 
1 // app/utils/recovery-store.js! 
2 ! 
3 export default Ember.Object.extend({! 
4 recover: function(key){! 
5 return new Ember.RSVP.Promise(function(resolve, reject){! 
6 resolve(window.localStorage.getItem(key));! 
7 });! 
8 }! 
9 });! 
Normalize sync and async
Using a synchronous Promise 
1 import RecoveryStore from "app/utils/recovery-store";! 
2 ! 
3 var recoveryStore = new RecoveryStore();! 
4 recoveryStore.recover('proposalSnapshot').then(function(data){! 
5 console.log('recovering ', data);! 
6 });!
Using a synchronous Promise 
1 import RecoveryStore from "app/utils/recovery-store";! 
2 ! 
3 var recoveryStore = new RecoveryStore();! 
4 recoveryStore.recover('proposalSnapshot').then(function(data){! 
5 console.log('recovering ', data);! 
6 });! 
Using an asynchronous Promise 
1 import RecoveryStore from "app/utils/recovery-store";! 
2 ! 
3 var recoveryStore = new RecoveryStore();! 
4 recoveryStore.recover('proposalSnapshot').then(function(data){! 
5 console.log('recovering ', data);! 
6 });!
Constraining a dependency 
1 // app/utils/recovery-store.js! 
2 ! 
3 export default Ember.Object.extend({! 
4 recover: function(key){! 
5 return new Ember.RSVP.Promise(function(resolve, reject){! 
6 resolve(window.localStorage.getItem(key));! 
7 });! 
8 }! 
9 });! 
localStorage
Constraining a dependency 
1 // app/utils/recovery-store.js! 
2 ! 
3 export default Ember.Object.extend({! 
4 recover: function(key){! 
5 return new Ember.RSVP.Promise(function(resolve, reject){! 
6 Ember.$.ajax("GET", "/recovery/"+key, {dataType: 'json'}, {! 
7 success: Ember.run.bind(this, function(data){! 
8 resolve(data);! 
9 })! 
10 });! 
11 });! 
12 }! 
13 });! AJAX!
Using a synchronous Promise 
1 import RecoveryStore from "app/utils/recovery-store";! 
2 ! 
3 var recoveryStore = new RecoveryStore();! 
4 recoveryStore.recover('proposalSnapshot').then(function(data){! 
5 console.log('recovering ', data);! 
6 });! 
Using an asynchronous Promise 
1 import RecoveryStore from "app/utils/recovery-store";! 
2 ! 
3 var recoveryStore = new RecoveryStore();! 
4 recoveryStore.recover('proposalSnapshot').then(function(data){! 
5 console.log('recovering ', data);! 
6 });!
Unit Testing Dependencies
Mocking a dependency in units 
1 // app/route/proposal.js! 
2 import RecoveryStore from "../utils/recovery-store";! 
3 import Proposal from "../model/proposal";! 
4 ! 
5 export default Ember.Route.extend({! 
6 ! 
7 recoveryStore: function(){! 
8 return new RecoveryStore();! 
9 }.property(),! 
10 ! 
11 model: function(){! 
12 return this.get('recoveryStore').recover('proposalSnapshot').! 
13 then(function(data){! 
14 if (data) {! 
15 return Proposal.load(data);! 
16 } else {! 
17 return Proposal.build();! 
18 }! 
19 });! 
20 }! 
21 ! 
22 });!
Mocking a dependency in units 
1 import { test, moduleFor } from 'ember-qunit';! 
2 ! 
3 moduleFor('route:proposal', 'ProposalRoute');! 
4 ! 
5 test('recovers a proposal', function() {! 
6 expect(1);! 
7 window.localStorage.setItem('proposalSnapshot', {body: 'pitch Angular'});! 
8 var route = this.subject();! 
9 return route.model().then(function(proposal){! 
10 ok(proposal.get('body'), 'pitch Angular');! 
11 });! 
12 });!
Mocking a dependency in units 
Implementation Leak 
1 import { test, moduleFor } from 'ember-qunit';! 
2 ! 
3 moduleFor('route:proposal', 'ProposalRoute');! 
4 ! 
5 test('recovers a proposal', function() {! 
6 expect(1);! 
7 window.localStorage.setItem('proposalSnapshot', {body: 'pitch Angular'});! 
8 var route = this.subject();! 
9 return route.model().then(function(proposal){! 
10 ok(proposal.get('body'), 'pitch Angular');! 
11 });! 
12 });! 
͟͞͞ ⌨ 
(۶ૈ ᵒ̌ Дᵒ̌)۶ૈ=͟
Mocking a dependency in units 
1 import { test, moduleFor } from 'ember-qunit';! 
2 ! 
3 var mockRecoveryStore = { recover: function(){! 
4 return new Ember.RSVP.Promise(function(resolve){! 
5 resolve(Ember.Object.create({body: 'pitch Angular'}));! 
6 });! 
7 } };! 
8 ! 
9 moduleFor('route:proposal', 'ProposalRoute');! 
10 ! 
11 test('recovers a proposal', function() {! 
12 expect(1);! 
13 var route = this.subject({recoveryStore: mockRecoveryStore});! 
14 return route.model().then(function(proposal){! 
15 ok(proposal.get('body'), 'pitch Angular');! 
16 });! 
17 });!
Mocking a dependency in units 
1 import { test, moduleFor } from 'ember-qunit';! 
2 ! 
3 var mockRecoveryStore = { recover: function(){! 
4 return new Ember.RSVP.Promise(function(resolve){! 
5 resolve(Ember.Object.create({body: 'pitch Angular'}));! 
6 });! 
7 } };! 
8 ! 
9 moduleFor('route:proposal', 'ProposalRoute', {! 
10 subject: function(options, klass, container){! 
11 return klass.create(Ember.merge({! 
12 recoveryStore: mockRecoveryStore! 
13 }, options));! 
14 }! 
15 });! 
16 ! 
17 test('recovers a proposal', function() {! 
18 expect(1);! 
19 var route = this.subject();! 
20 return route.model().then(function(proposal){! 
21 ok(proposal.get('body'), 'pitch Angular');! 
22 });! 
23 });!
Mocking a dependency in units 
1 import { test, moduleFor } from 'ember-qunit';! 
2 ! 
3 var mockRecoveryStore = { recover: function(){! 
4 return new Ember.RSVP.Promise(function(resolve){! 
5 resolve(Ember.Object.create({body: 'pitch Angular'}));! 
6 });! 
7 } };! 
8 ! 
9 moduleFor('route:proposal', 'ProposalRoute', {! 
10 subject: function(options, klass, container){! 
11 return klass.create(Ember.merge({! 
12 recoveryStore: mockRecoveryStore! 
13 }, options));! 
14 }! 
15 });! 
16 ! 
17 test('recovers a proposal', function() {! 
18 expect(1);! 
✓ 
19 var route = this.subject();! 
20 return route.model().then(function(proposal){! 
21 ok(proposal.get('body'), 'pitch Angular');! 
22 });! 
23 });!
Why is this code good?
Why is this code good? Well Gary Bernhardt sez… 
Three goals of testing
Why is this code good? Well Gary Bernhardt sez… 
#1: Prevent regressions 
#2: Prevent fear 
#3: Prevent bad design
Why is this code good? Well Gary Bernhardt sez… 
#1: Prevent regressions✓ 
#2: Prevent fear 
#3: Prevent bad design
Why is this code good? Well Gary Bernhardt sez… 
#1: Prevent regressions✓ 
#2: Prevent fear 
✓ 
#3: Prevent bad design
Why is this code good? Well Gary Bernhardt sez… 
#1: Prevent regressions✓ 
#2: Prevent fear 
✓✓Is fast! 
#3: Prevent bad design
Why is this code good? Well Gary Bernhardt sez… 
#1: Prevent regressions✓ 
#2: Prevent fear 
✓ 
✓ 
#3: Prevent bad design 
ᕕ( ᐛ )ᕗ 
✓Is fast!
Acceptance Testing Dependencies
Mocking a dependency in acceptance 
1 // app/route/proposal.js! 
2 import RecoveryStore from "../utils/recovery-store";! 
3 import Proposal from "../model/proposal";! 
4 ! 
5 export default Ember.Route.extend({! 
6 ! 
7 recoveryStore: function(){! 
8 return new RecoveryStore();! 
9 }.property(),! 
10 ! 
11 model: function(){! 
12 return this.get('recoveryStore').recovery('proposalSnapshot').! 
13 then(function(data){! 
14 if (data) {! 
15 return Proposal.load(data);! 
16 } else {! 
17 return Proposal.build();! 
18 }! 
19 });! 
20 }! 
21 ! 
22 });!
Mocking a dependency in acceptance 
1 // tests/acceptance/proposal-test.js! 
2 ! 
3 import Ember from 'ember';! 
4 import startApp from '../helpers/start-app';! 
5 ! 
6 var App;! 
7 ! 
8 module('Acceptance: Proposal', {! 
9 setup: function() {! 
10 App = startApp();! 
11 },! 
12 teardown: function() {! 
13 Ember.run(App, 'destroy');! 
14 }! 
15 });! 
16 ! 
17 test('visiting /proposal', function() {! 
18 visit('/proposal');! 
19 ! 
20 andThen(function() {! 
21 equal(currentPath(), 'proposal');! 
22 });! 
23 });!
Mocking a dependency in acceptance 
1 // tests/acceptance/proposal-test.js! 
2 ! 
3 import Ember from 'ember';! 
4 import startApp from '../helpers/start-app';! 
5 ! 
6 var App;! 
7 ! 
8 module('Acceptance: Proposal', {! 
9 setup: function() {! 
10 App = startApp();! 
11 },! 
12 teardown: function() {! 
13 Ember.run(App, 'destroy');! 
14 }! 
15 });! 
16 ! 
17 test('visiting /proposal', function() {! 
18 visit('/proposal');! 
19 ! 
20 andThen(function() {! 
21 equal(currentPath(), 'proposal');! 
22 });! 
23 });! 
Where can we mock recoveryStore?
Mocking a dependency in acceptance 
1 // app/route/proposal.js! 
2 import RecoveryStore from "../utils/recovery-store";! 
3 import Proposal from "../model/proposal";! 
4 ! 
5 export default Ember.Route.extend({! 
6 ! 
7 recoveryStore: function(){! 
8 return new RecoveryStore();! 
9 }.property(),! 
10 ! 
11 model: function(){! 
12 return this.get('recoveryStore').recovery('proposalSnapshot').! 
13 then(function(data){! 
14 return Proposal.load(data);! 
15 }, function(){! 
16 return Proposal.build();! 
17 });! 
18 }! 
19 ! 
20 });! 
“Dependency Lookup”
Dependency Lookup 
1 var Gadget = Ember.Object.create({! 
2 shelf: function(){! 
3 return Shelf.create();! 
4 }! 
5 });! 
6 ! 
7 Gadget.create();!
Dependency Lookup 
1 var Gadget = Ember.Object.create({! 
2 shelf: function(){! 
3 return Shelf.create();! 
4 }! 
5 });! 
6 ! 
7 Gadget.create();! 
Gadget is in charge
Dependency Lookup 
1 var Gadget = Ember.Object.create({! 
2 shelf: function(){! 
3 return Shelf.create();! 
4 }! 
5 });! 
6 ! 
7 Gadget.create();! 
Dependency Injection 
1 var Gadget = Ember.Object.create({! 
2 });! 
3 ! 
4 function buildGadget(){! 
5 return Gadget.create({! 
6 shelf: Shelf.create()! 
7 });! 
8 }!
Dependency Lookup 
1 var Gadget = Ember.Object.create({! 
2 shelf: function(){! 
3 return Shelf.create();! 
4 }! 
5 });! 
6 ! 
7 Gadget.create();! 
Dependency Injection 
1 var Gadget = Ember.Object.create({! 
2 });! 
3 ! 
4 function buildGadget(){! 
5 return Gadget.create({! 
6 shelf: Shelf.create()! 
7 });! 
8 }! 
Gadget’s dependency is injected
Dependency Lookup 
1 var Gadget = Ember.Object.create({! 
2 shelf: function(){! 
3 return Shelf.create();! 
4 }! 
5 });! 
6 ! 
7 Gadget.create();! 
Dependency Injection / Inversion of Control 
1 var Gadget = Ember.Object.create({! 
2 });! 
3 ! 
4 function buildGadget(){! 
5 return Gadget.create({! 
6 shelf: Shelf.create()! 
7 });! 
8 }!
Converting “Dependency Lookup” to 
“Dependency Injection” 
1. Create an initializer 
2. Register the dependency 
3. Inject the dependency 
4. Drop the lookup
Converting “Dependency Lookup” to 
“Dependency Injection” 
Create an initializer 
1 // app/initializers/recovery-store.js! 
2 ! 
3 import RecoveryStore from "../utils/recovery-store";! 
4 ! 
5 export default {! 
6 name: 'recovery-store',! 
7 initialize: function(container, application) {! 
8 application.register('services:recoveryStore', RecoveryStore);! 
9 application.inject('route', 'recoveryStore', 'services:recoveryStore');! 
10 }! 
11 };!
Converting “Dependency Lookup” to 
“Dependency Injection” 
1 // app/initializers/recovery-store.js! 
2 ! 
3 import RecoveryStore from "../utils/recovery-store";! 
4 ! 
5 export default {! 
6 name: 'recovery-store',! 
7 initialize: function(container, application) {! 
8 application.register('services:recoveryStore', RecoveryStore);! 
9 application.inject('route', 'recoveryStore', 'services:recoveryStore');! 
10 }! 
11 };! 
Register the dependency
Converting “Dependency Lookup” to 
“Dependency Injection” 
1 // app/initializers/recovery-store.js! 
2 ! 
3 import RecoveryStore from "../utils/recovery-store";! 
4 ! 
5 export default {! 
6 name: 'recovery-store',! 
7 initialize: function(container, application) {! 
8 application.register('services:recoveryStore', RecoveryStore);! 
9 application.inject('route', 'recoveryStore', 'services:recoveryStore');! 
10 }! 
11 };! 
Inject the dependency
Converting “Dependency Lookup” to 
“Dependency Injection” 
1 // app/route/proposal.js! 
2 import RecoveryStore from "../utils/recovery-store";! 
3 import Proposal from "../model/proposal";! 
4 ! 
5 export default Ember.Route.extend({! 
6 ! 
7 model: function(){! 
8 return this.recoveryStore.recover('proposalSnapshot').! 
9 then(function(data){! 
10 return Proposal.load(data);! 
11 }, function(){! 
12 return Proposal.build();! 
13 });! 
14 }! 
15 ! 
16 });! 
Drop the lookup
Mocking a dependency in acceptance 
1 // tests/acceptance/proposal-test.js! 
2 ! 
3 import Ember from 'ember';! 
4 import startApp from '../helpers/start-app';! 
5 import mockRecoveryStore from '../helpers/mock-recovery-store';! 
6 ! 
7 var App;! 
8 ! 
9 module('Acceptance: Proposal', {! 
10 setup: function() {! 
11 App = startApp();! 
12 // unregister is not yet a first-class API :-/! 
13 App.__container__.unregister('services:recoveryStore');! 
14 App.register('services:recoveryStore', mockRecoveryStore, {! 
15 instantiate: false });! 
16 },! 
17 teardown: function() {! 
18 Ember.run(App, 'destroy');! 
19 }! 
20 });! 
21 ! 
22 test('visiting /proposal', function() {! 
23 visit('/proposal');! 
24 ! 
25 andThen(function() {! 
26 equal(currentPath(), 'proposal');! 
27 });! 
28 });! 
Unregister the normal recoveryStore
Mocking a dependency in acceptance 
1 // tests/acceptance/proposal-test.js! 
2 ! 
3 import Ember from 'ember';! 
4 import startApp from '../helpers/start-app';! 
5 import mockRecoveryStore from '../helpers/mock-recovery-store';! 
6 ! 
7 var App;! 
8 ! 
9 module('Acceptance: Proposal', {! 
10 setup: function() {! 
11 App = startApp();! 
12 // unregister is not yet a first-class API :-/! 
13 App.__container__.unregister('services:recoveryStore');! 
14 App.register('services:recoveryStore', mockRecoveryStore, {! 
15 instantiate: false });! 
16 },! 
17 teardown: function() {! 
18 Ember.run(App, 'destroy');! 
19 }! 
20 });! 
21 ! 
22 test('visiting /proposal', function() {! 
23 visit('/proposal');! 
24 ! 
25 andThen(function() {! 
26 equal(currentPath(), 'proposal');! 
27 });! 
28 });! 
Register the mockRecoveryStore
#1: Prevent regressions 
#2: Prevent fear 
#3: Prevent bad design 
ᕕ( ᐛ )ᕗ 
✓ 
✓ 
✓ 
✓Is fast! 
Why is this code good?
STOP IT WITH 
ALL THE CODE 
(ノಥ益ಥ)ノ ┻━┻
Starting points: 
ember-cli.com 
emberjs.com/guides/testing 
github.com/rwjblue/ember-qunit 
emberjs.com/api/classes/Ember.Test.html
Starting points: 
wikipedia.org/wiki/Inversion_of_control 
www.martinfowler.com/articles/injection.html
Own your dependencies 
Contain their APIs
Own your dependencies 
Design better code with better tests
Gracias

More Related Content

What's hot

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 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
 
Django Celery - A distributed task queue
Django Celery - A distributed task queueDjango Celery - A distributed task queue
Django Celery - A distributed task queueAlex Eftimie
 
An Introduction to Celery
An Introduction to CeleryAn Introduction to Celery
An Introduction to CeleryIdan Gazit
 
Intro to JavaScript
Intro to JavaScriptIntro to JavaScript
Intro to JavaScriptYakov Fain
 
The road to Ember.js 2.0
The road to Ember.js 2.0The road to Ember.js 2.0
The road to Ember.js 2.0Codemotion
 
AngularJS Unit Test
AngularJS Unit TestAngularJS Unit Test
AngularJS Unit TestChiew Carol
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Roy Yu
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyIgor Napierala
 
Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express MiddlewareMorris Singer
 
Intro to testing Javascript with jasmine
Intro to testing Javascript with jasmineIntro to testing Javascript with jasmine
Intro to testing Javascript with jasmineTimothy Oxley
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsFITC
 
AngularJS Unit Testing
AngularJS Unit TestingAngularJS Unit Testing
AngularJS Unit TestingPrince Norin
 
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
 
Angularjs - Unit testing introduction
Angularjs - Unit testing introductionAngularjs - Unit testing introduction
Angularjs - Unit testing introductionNir Kaufman
 
Painless JavaScript Testing with Jest
Painless JavaScript Testing with JestPainless JavaScript Testing with Jest
Painless JavaScript Testing with JestMichał Pierzchała
 

What's hot (20)

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
 
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
 
Ember - introduction
Ember - introductionEmber - introduction
Ember - introduction
 
Full Stack Unit Testing
Full Stack Unit TestingFull Stack Unit Testing
Full Stack Unit Testing
 
Django Celery - A distributed task queue
Django Celery - A distributed task queueDjango Celery - A distributed task queue
Django Celery - A distributed task queue
 
An Introduction to Celery
An Introduction to CeleryAn Introduction to Celery
An Introduction to Celery
 
Jasmine BDD for Javascript
Jasmine BDD for JavascriptJasmine BDD for Javascript
Jasmine BDD for Javascript
 
Intro to JavaScript
Intro to JavaScriptIntro to JavaScript
Intro to JavaScript
 
The road to Ember.js 2.0
The road to Ember.js 2.0The road to Ember.js 2.0
The road to Ember.js 2.0
 
AngularJS Unit Test
AngularJS Unit TestAngularJS Unit Test
AngularJS Unit Test
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishy
 
Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express Middleware
 
Intro to testing Javascript with jasmine
Intro to testing Javascript with jasmineIntro to testing Javascript with jasmine
Intro to testing Javascript with jasmine
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS Applications
 
AngularJS Unit Testing
AngularJS Unit TestingAngularJS Unit Testing
AngularJS Unit Testing
 
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
 
Angularjs - Unit testing introduction
Angularjs - Unit testing introductionAngularjs - Unit testing introduction
Angularjs - Unit testing introduction
 
Practical Celery
Practical CeleryPractical Celery
Practical Celery
 
Painless JavaScript Testing with Jest
Painless JavaScript Testing with JestPainless JavaScript Testing with Jest
Painless JavaScript Testing with Jest
 

Similar to Testing Ember Apps: Managing Dependency

Ultimate Introduction To AngularJS
Ultimate Introduction To AngularJSUltimate Introduction To AngularJS
Ultimate Introduction To AngularJSJacopo Nardiello
 
Micro Front-Ends
Micro Front-EndsMicro Front-Ends
Micro Front-EndsOri Calvo
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications Juliana Lucena
 
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Frost
 
Quick and Easy Development with Node.js and Couchbase Server
Quick and Easy Development with Node.js and Couchbase ServerQuick and Easy Development with Node.js and Couchbase Server
Quick and Easy Development with Node.js and Couchbase ServerNic Raboy
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013Laurent_VB
 
Helma / RingoJS – Vienna.js Minitalk
Helma / RingoJS – Vienna.js MinitalkHelma / RingoJS – Vienna.js Minitalk
Helma / RingoJS – Vienna.js MinitalkPhilipp Naderer
 
Introducing Applitude: Simple Module Management
Introducing Applitude: Simple Module ManagementIntroducing Applitude: Simple Module Management
Introducing Applitude: Simple Module ManagementEric Hamilton
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasminePaulo Ragonha
 
EcmaScript 6 - The future is here
EcmaScript 6 - The future is hereEcmaScript 6 - The future is here
EcmaScript 6 - The future is hereSebastiano Armeli
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsFrancois Zaninotto
 
How React Native, Appium and me made each other shine @Frontmania 16-11-2018
How React Native, Appium and me made each other shine @Frontmania 16-11-2018How React Native, Appium and me made each other shine @Frontmania 16-11-2018
How React Native, Appium and me made each other shine @Frontmania 16-11-2018Wim Selles
 
AngularJS - Services
AngularJS - ServicesAngularJS - Services
AngularJS - ServicesNir Kaufman
 
Modern Web Developement
Modern Web DevelopementModern Web Developement
Modern Web Developementpeychevi
 

Similar to Testing Ember Apps: Managing Dependency (20)

Ultimate Introduction To AngularJS
Ultimate Introduction To AngularJSUltimate Introduction To AngularJS
Ultimate Introduction To AngularJS
 
Micro Front-Ends
Micro Front-EndsMicro Front-Ends
Micro Front-Ends
 
ParisJS #10 : RequireJS
ParisJS #10 : RequireJSParisJS #10 : RequireJS
ParisJS #10 : RequireJS
 
Intro to Ember.JS 2016
Intro to Ember.JS 2016Intro to Ember.JS 2016
Intro to Ember.JS 2016
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications
 
Intro to ReactJS
Intro to ReactJSIntro to ReactJS
Intro to ReactJS
 
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
Building an angular application -1 ( API: Golang, Database: Postgres) v1.0
 
Quick and Easy Development with Node.js and Couchbase Server
Quick and Easy Development with Node.js and Couchbase ServerQuick and Easy Development with Node.js and Couchbase Server
Quick and Easy Development with Node.js and Couchbase Server
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013
 
ES6 metaprogramming unleashed
ES6 metaprogramming unleashedES6 metaprogramming unleashed
ES6 metaprogramming unleashed
 
You promise?
You promise?You promise?
You promise?
 
Helma / RingoJS – Vienna.js Minitalk
Helma / RingoJS – Vienna.js MinitalkHelma / RingoJS – Vienna.js Minitalk
Helma / RingoJS – Vienna.js Minitalk
 
Nevermore Unit Testing
Nevermore Unit TestingNevermore Unit Testing
Nevermore Unit Testing
 
Introducing Applitude: Simple Module Management
Introducing Applitude: Simple Module ManagementIntroducing Applitude: Simple Module Management
Introducing Applitude: Simple Module Management
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
 
EcmaScript 6 - The future is here
EcmaScript 6 - The future is hereEcmaScript 6 - The future is here
EcmaScript 6 - The future is here
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
How React Native, Appium and me made each other shine @Frontmania 16-11-2018
How React Native, Appium and me made each other shine @Frontmania 16-11-2018How React Native, Appium and me made each other shine @Frontmania 16-11-2018
How React Native, Appium and me made each other shine @Frontmania 16-11-2018
 
AngularJS - Services
AngularJS - ServicesAngularJS - Services
AngularJS - Services
 
Modern Web Developement
Modern Web DevelopementModern Web Developement
Modern Web Developement
 

More from Matthew Beale

Ember.js Module Loading
Ember.js Module LoadingEmber.js Module Loading
Ember.js Module LoadingMatthew Beale
 
LA Ember.js Meetup, Jan 2017
LA Ember.js Meetup, Jan 2017LA Ember.js Meetup, Jan 2017
LA Ember.js Meetup, Jan 2017Matthew Beale
 
Interoperable Component Patterns
Interoperable Component PatternsInteroperable Component Patterns
Interoperable Component PatternsMatthew Beale
 
Ember Community 2016 - Be the Bark
Ember Community 2016 - Be the BarkEmber Community 2016 - Be the Bark
Ember Community 2016 - Be the BarkMatthew Beale
 
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)Matthew Beale
 
Aligning Ember.js with Web Standards
Aligning Ember.js with Web StandardsAligning Ember.js with Web Standards
Aligning Ember.js with Web StandardsMatthew Beale
 
New Component Patterns in Ember.js
New Component Patterns in Ember.jsNew Component Patterns in Ember.js
New Component Patterns in Ember.jsMatthew Beale
 
Scalable vector ember
Scalable vector emberScalable vector ember
Scalable vector emberMatthew Beale
 
Parse Apps with Ember.js
Parse Apps with Ember.jsParse Apps with Ember.js
Parse Apps with Ember.jsMatthew Beale
 
Snappy Means Happy: Performance in Ember Apps
Snappy Means Happy: Performance in Ember AppsSnappy Means Happy: Performance in Ember Apps
Snappy Means Happy: Performance in Ember AppsMatthew Beale
 
Client-side Auth with Ember.js
Client-side Auth with Ember.jsClient-side Auth with Ember.js
Client-side Auth with Ember.jsMatthew Beale
 
Complex Architectures in Ember
Complex Architectures in EmberComplex Architectures in Ember
Complex Architectures in EmberMatthew Beale
 

More from Matthew Beale (13)

Ember.js Module Loading
Ember.js Module LoadingEmber.js Module Loading
Ember.js Module Loading
 
LA Ember.js Meetup, Jan 2017
LA Ember.js Meetup, Jan 2017LA Ember.js Meetup, Jan 2017
LA Ember.js Meetup, Jan 2017
 
Interoperable Component Patterns
Interoperable Component PatternsInteroperable Component Patterns
Interoperable Component Patterns
 
Ember Community 2016 - Be the Bark
Ember Community 2016 - Be the BarkEmber Community 2016 - Be the Bark
Ember Community 2016 - Be the Bark
 
Attribute actions
Attribute actionsAttribute actions
Attribute actions
 
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
 
Aligning Ember.js with Web Standards
Aligning Ember.js with Web StandardsAligning Ember.js with Web Standards
Aligning Ember.js with Web Standards
 
New Component Patterns in Ember.js
New Component Patterns in Ember.jsNew Component Patterns in Ember.js
New Component Patterns in Ember.js
 
Scalable vector ember
Scalable vector emberScalable vector ember
Scalable vector ember
 
Parse Apps with Ember.js
Parse Apps with Ember.jsParse Apps with Ember.js
Parse Apps with Ember.js
 
Snappy Means Happy: Performance in Ember Apps
Snappy Means Happy: Performance in Ember AppsSnappy Means Happy: Performance in Ember Apps
Snappy Means Happy: Performance in Ember Apps
 
Client-side Auth with Ember.js
Client-side Auth with Ember.jsClient-side Auth with Ember.js
Client-side Auth with Ember.js
 
Complex Architectures in Ember
Complex Architectures in EmberComplex Architectures in Ember
Complex Architectures in Ember
 

Recently uploaded

FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024The Digital Insurer
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...apidays
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyKhushali Kathiriya
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdfSandro Moreira
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWERMadyBayot
 
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelDeepika Singh
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Victor Rentea
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Bhuvaneswari Subramani
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...Zilliz
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingEdi Saputra
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxRustici Software
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...DianaGray10
 

Recently uploaded (20)

FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 

Testing Ember Apps: Managing Dependency

  • 1. Testing Ember.js Apps Managing Dependency
  • 5. 201 Created We build õ-age apps with Ember.js. We take teams from £ to • in no time flat.
  • 7. A conference planning app • Submitting a talk • Purchasing a ticket • Preparing a talk
  • 8. • Submitting a talk • Purchasing a ticket • Preparing a talk The domain A conference planning app
  • 9. A conference planning app • Submitting a talk • Purchasing a ticket • Preparing a talk
  • 10. A conference planning app • Adapting to a server • Managing asynchronous APIs • Browser APIs • Submitting a talk • Purchasing a ticket • Preparing a talk ᗜੂͦ﹏ᗜੂͦ
  • 11. A conference planning app • Adapting to a server • Managing asynchronous APIs • Browser APIs • Submitting a talk • Purchasing a ticket • Preparing a talk The domain
  • 12. A conference planning app What is this?! • Adapting to a server • Managing asynchronous APIs • Browser APIs • Submitting a talk • Purchasing a ticket • Preparing a talk ᗜੂͦ﹏ᗜੂͦ
  • 17. Ember-CLI Review 1 // app/utils/some-object.js! 2 export default SomeObject;! 3 export default Ember.Route.extend({! 4 });! 5 ! 6 import MyObject from "app/utils/some-object";! 7 ! 8 // app/utils/many-things.js! 9 export {gadget, anotherGadget};! 10 ! 11 import {anotherGadget} from "app/utils/many-things";! 12 ! 13 // tests/routes/proposal-test.js! 14 moduleFor("route:routeName", "Test Module Name", {! 15 // options include: needs, subject, setup, teardown! 16 });!
  • 18. Constraining Dependencies 1. Isolate implementation 2. Normalize behavior
  • 19. Constraining a dependency 1 // app/route/proposal.js! 2 import Proposal from "../model/proposal";! 3 ! 4 export default Ember.Route.extend({! 5 ! 6 model: function(){! 7 var recovered = window.localStorage.getItem('proposalSnapshot');! 8 if (recovered) {! 9 return Proposal.load(recovered);! 10 } else {! 11 return Proposal.build();! 12 }! 13 }! 14 ! 15 });!
  • 20. Constraining a dependency Implementation 1 // app/route/proposal.js! 2 import Proposal from "../model/proposal";! 3 ! 4 export default Ember.Route.extend({! 5 ! 6 model: function(){! 7 var recovered = window.localStorage.getItem('proposalSnapshot');! 8 if (recovered) {! 9 return Proposal.load(recovered);! 10 } else {! 11 return Proposal.build();! 12 }! 13 }! 14 ! 15 });!
  • 21. Implementation Constraining a dependency 1 // app/route/proposal.js! 2 import Proposal from "../model/proposal";! 3 ! 4 export default Ember.Route.extend({! 5 ! 6 model: function(){! 7 var recovered = window.localStorage.getItem('proposalSnapshot');! 8 if (recovered) {! 9 return Proposal.load(recovered);! 10 } else {! 11 return Proposal.build();! 12 }! 13 }! 14 ! 15 });! Synchronous behavior
  • 22. Constraining a dependency app/route/proposal model lookup localStorage sync behavior
  • 23. Constraining a dependency app/route/proposal model lookup app/utils/recovery-store localStorage sync behavior
  • 24. Constraining a dependency 1 // app/utils/recovery-store.js! 2 ! 3 export default Ember.Object.extend({! 4 recover: function(key){! 5 return new Ember.RSVP.Promise(function(resolve, reject){! 6 resolve(window.localStorage.getItem(key));! 7 });! 8 }! 9 });!
  • 25. Constraining a dependency Isolated in recoveryStore 1 // app/utils/recovery-store.js! 2 ! 3 export default Ember.Object.extend({! 4 recover: function(key){! 5 return new Ember.RSVP.Promise(function(resolve, reject){! 6 resolve(window.localStorage.getItem(key));! 7 });! 8 }! 9 });!
  • 26. Constraining a dependency Isolated in recoveryStore 1 // app/utils/recovery-store.js! 2 ! 3 export default Ember.Object.extend({! 4 recover: function(key){! 5 return new Ember.RSVP.Promise(function(resolve, reject){! 6 resolve(window.localStorage.getItem(key));! 7 });! 8 }! 9 });! Normalize sync and async
  • 27. Using a synchronous Promise 1 import RecoveryStore from "app/utils/recovery-store";! 2 ! 3 var recoveryStore = new RecoveryStore();! 4 recoveryStore.recover('proposalSnapshot').then(function(data){! 5 console.log('recovering ', data);! 6 });!
  • 28. Using a synchronous Promise 1 import RecoveryStore from "app/utils/recovery-store";! 2 ! 3 var recoveryStore = new RecoveryStore();! 4 recoveryStore.recover('proposalSnapshot').then(function(data){! 5 console.log('recovering ', data);! 6 });! Using an asynchronous Promise 1 import RecoveryStore from "app/utils/recovery-store";! 2 ! 3 var recoveryStore = new RecoveryStore();! 4 recoveryStore.recover('proposalSnapshot').then(function(data){! 5 console.log('recovering ', data);! 6 });!
  • 29. Constraining a dependency 1 // app/utils/recovery-store.js! 2 ! 3 export default Ember.Object.extend({! 4 recover: function(key){! 5 return new Ember.RSVP.Promise(function(resolve, reject){! 6 resolve(window.localStorage.getItem(key));! 7 });! 8 }! 9 });! localStorage
  • 30. Constraining a dependency 1 // app/utils/recovery-store.js! 2 ! 3 export default Ember.Object.extend({! 4 recover: function(key){! 5 return new Ember.RSVP.Promise(function(resolve, reject){! 6 Ember.$.ajax("GET", "/recovery/"+key, {dataType: 'json'}, {! 7 success: Ember.run.bind(this, function(data){! 8 resolve(data);! 9 })! 10 });! 11 });! 12 }! 13 });! AJAX!
  • 31. Using a synchronous Promise 1 import RecoveryStore from "app/utils/recovery-store";! 2 ! 3 var recoveryStore = new RecoveryStore();! 4 recoveryStore.recover('proposalSnapshot').then(function(data){! 5 console.log('recovering ', data);! 6 });! Using an asynchronous Promise 1 import RecoveryStore from "app/utils/recovery-store";! 2 ! 3 var recoveryStore = new RecoveryStore();! 4 recoveryStore.recover('proposalSnapshot').then(function(data){! 5 console.log('recovering ', data);! 6 });!
  • 33. Mocking a dependency in units 1 // app/route/proposal.js! 2 import RecoveryStore from "../utils/recovery-store";! 3 import Proposal from "../model/proposal";! 4 ! 5 export default Ember.Route.extend({! 6 ! 7 recoveryStore: function(){! 8 return new RecoveryStore();! 9 }.property(),! 10 ! 11 model: function(){! 12 return this.get('recoveryStore').recover('proposalSnapshot').! 13 then(function(data){! 14 if (data) {! 15 return Proposal.load(data);! 16 } else {! 17 return Proposal.build();! 18 }! 19 });! 20 }! 21 ! 22 });!
  • 34. Mocking a dependency in units 1 import { test, moduleFor } from 'ember-qunit';! 2 ! 3 moduleFor('route:proposal', 'ProposalRoute');! 4 ! 5 test('recovers a proposal', function() {! 6 expect(1);! 7 window.localStorage.setItem('proposalSnapshot', {body: 'pitch Angular'});! 8 var route = this.subject();! 9 return route.model().then(function(proposal){! 10 ok(proposal.get('body'), 'pitch Angular');! 11 });! 12 });!
  • 35. Mocking a dependency in units Implementation Leak 1 import { test, moduleFor } from 'ember-qunit';! 2 ! 3 moduleFor('route:proposal', 'ProposalRoute');! 4 ! 5 test('recovers a proposal', function() {! 6 expect(1);! 7 window.localStorage.setItem('proposalSnapshot', {body: 'pitch Angular'});! 8 var route = this.subject();! 9 return route.model().then(function(proposal){! 10 ok(proposal.get('body'), 'pitch Angular');! 11 });! 12 });! ͟͞͞ ⌨ (۶ૈ ᵒ̌ Дᵒ̌)۶ૈ=͟
  • 36. Mocking a dependency in units 1 import { test, moduleFor } from 'ember-qunit';! 2 ! 3 var mockRecoveryStore = { recover: function(){! 4 return new Ember.RSVP.Promise(function(resolve){! 5 resolve(Ember.Object.create({body: 'pitch Angular'}));! 6 });! 7 } };! 8 ! 9 moduleFor('route:proposal', 'ProposalRoute');! 10 ! 11 test('recovers a proposal', function() {! 12 expect(1);! 13 var route = this.subject({recoveryStore: mockRecoveryStore});! 14 return route.model().then(function(proposal){! 15 ok(proposal.get('body'), 'pitch Angular');! 16 });! 17 });!
  • 37. Mocking a dependency in units 1 import { test, moduleFor } from 'ember-qunit';! 2 ! 3 var mockRecoveryStore = { recover: function(){! 4 return new Ember.RSVP.Promise(function(resolve){! 5 resolve(Ember.Object.create({body: 'pitch Angular'}));! 6 });! 7 } };! 8 ! 9 moduleFor('route:proposal', 'ProposalRoute', {! 10 subject: function(options, klass, container){! 11 return klass.create(Ember.merge({! 12 recoveryStore: mockRecoveryStore! 13 }, options));! 14 }! 15 });! 16 ! 17 test('recovers a proposal', function() {! 18 expect(1);! 19 var route = this.subject();! 20 return route.model().then(function(proposal){! 21 ok(proposal.get('body'), 'pitch Angular');! 22 });! 23 });!
  • 38. Mocking a dependency in units 1 import { test, moduleFor } from 'ember-qunit';! 2 ! 3 var mockRecoveryStore = { recover: function(){! 4 return new Ember.RSVP.Promise(function(resolve){! 5 resolve(Ember.Object.create({body: 'pitch Angular'}));! 6 });! 7 } };! 8 ! 9 moduleFor('route:proposal', 'ProposalRoute', {! 10 subject: function(options, klass, container){! 11 return klass.create(Ember.merge({! 12 recoveryStore: mockRecoveryStore! 13 }, options));! 14 }! 15 });! 16 ! 17 test('recovers a proposal', function() {! 18 expect(1);! ✓ 19 var route = this.subject();! 20 return route.model().then(function(proposal){! 21 ok(proposal.get('body'), 'pitch Angular');! 22 });! 23 });!
  • 39. Why is this code good?
  • 40. Why is this code good? Well Gary Bernhardt sez… Three goals of testing
  • 41. Why is this code good? Well Gary Bernhardt sez… #1: Prevent regressions #2: Prevent fear #3: Prevent bad design
  • 42. Why is this code good? Well Gary Bernhardt sez… #1: Prevent regressions✓ #2: Prevent fear #3: Prevent bad design
  • 43. Why is this code good? Well Gary Bernhardt sez… #1: Prevent regressions✓ #2: Prevent fear ✓ #3: Prevent bad design
  • 44. Why is this code good? Well Gary Bernhardt sez… #1: Prevent regressions✓ #2: Prevent fear ✓✓Is fast! #3: Prevent bad design
  • 45. Why is this code good? Well Gary Bernhardt sez… #1: Prevent regressions✓ #2: Prevent fear ✓ ✓ #3: Prevent bad design ᕕ( ᐛ )ᕗ ✓Is fast!
  • 47. Mocking a dependency in acceptance 1 // app/route/proposal.js! 2 import RecoveryStore from "../utils/recovery-store";! 3 import Proposal from "../model/proposal";! 4 ! 5 export default Ember.Route.extend({! 6 ! 7 recoveryStore: function(){! 8 return new RecoveryStore();! 9 }.property(),! 10 ! 11 model: function(){! 12 return this.get('recoveryStore').recovery('proposalSnapshot').! 13 then(function(data){! 14 if (data) {! 15 return Proposal.load(data);! 16 } else {! 17 return Proposal.build();! 18 }! 19 });! 20 }! 21 ! 22 });!
  • 48. Mocking a dependency in acceptance 1 // tests/acceptance/proposal-test.js! 2 ! 3 import Ember from 'ember';! 4 import startApp from '../helpers/start-app';! 5 ! 6 var App;! 7 ! 8 module('Acceptance: Proposal', {! 9 setup: function() {! 10 App = startApp();! 11 },! 12 teardown: function() {! 13 Ember.run(App, 'destroy');! 14 }! 15 });! 16 ! 17 test('visiting /proposal', function() {! 18 visit('/proposal');! 19 ! 20 andThen(function() {! 21 equal(currentPath(), 'proposal');! 22 });! 23 });!
  • 49. Mocking a dependency in acceptance 1 // tests/acceptance/proposal-test.js! 2 ! 3 import Ember from 'ember';! 4 import startApp from '../helpers/start-app';! 5 ! 6 var App;! 7 ! 8 module('Acceptance: Proposal', {! 9 setup: function() {! 10 App = startApp();! 11 },! 12 teardown: function() {! 13 Ember.run(App, 'destroy');! 14 }! 15 });! 16 ! 17 test('visiting /proposal', function() {! 18 visit('/proposal');! 19 ! 20 andThen(function() {! 21 equal(currentPath(), 'proposal');! 22 });! 23 });! Where can we mock recoveryStore?
  • 50. Mocking a dependency in acceptance 1 // app/route/proposal.js! 2 import RecoveryStore from "../utils/recovery-store";! 3 import Proposal from "../model/proposal";! 4 ! 5 export default Ember.Route.extend({! 6 ! 7 recoveryStore: function(){! 8 return new RecoveryStore();! 9 }.property(),! 10 ! 11 model: function(){! 12 return this.get('recoveryStore').recovery('proposalSnapshot').! 13 then(function(data){! 14 return Proposal.load(data);! 15 }, function(){! 16 return Proposal.build();! 17 });! 18 }! 19 ! 20 });! “Dependency Lookup”
  • 51. Dependency Lookup 1 var Gadget = Ember.Object.create({! 2 shelf: function(){! 3 return Shelf.create();! 4 }! 5 });! 6 ! 7 Gadget.create();!
  • 52. Dependency Lookup 1 var Gadget = Ember.Object.create({! 2 shelf: function(){! 3 return Shelf.create();! 4 }! 5 });! 6 ! 7 Gadget.create();! Gadget is in charge
  • 53. Dependency Lookup 1 var Gadget = Ember.Object.create({! 2 shelf: function(){! 3 return Shelf.create();! 4 }! 5 });! 6 ! 7 Gadget.create();! Dependency Injection 1 var Gadget = Ember.Object.create({! 2 });! 3 ! 4 function buildGadget(){! 5 return Gadget.create({! 6 shelf: Shelf.create()! 7 });! 8 }!
  • 54. Dependency Lookup 1 var Gadget = Ember.Object.create({! 2 shelf: function(){! 3 return Shelf.create();! 4 }! 5 });! 6 ! 7 Gadget.create();! Dependency Injection 1 var Gadget = Ember.Object.create({! 2 });! 3 ! 4 function buildGadget(){! 5 return Gadget.create({! 6 shelf: Shelf.create()! 7 });! 8 }! Gadget’s dependency is injected
  • 55. Dependency Lookup 1 var Gadget = Ember.Object.create({! 2 shelf: function(){! 3 return Shelf.create();! 4 }! 5 });! 6 ! 7 Gadget.create();! Dependency Injection / Inversion of Control 1 var Gadget = Ember.Object.create({! 2 });! 3 ! 4 function buildGadget(){! 5 return Gadget.create({! 6 shelf: Shelf.create()! 7 });! 8 }!
  • 56. Converting “Dependency Lookup” to “Dependency Injection” 1. Create an initializer 2. Register the dependency 3. Inject the dependency 4. Drop the lookup
  • 57. Converting “Dependency Lookup” to “Dependency Injection” Create an initializer 1 // app/initializers/recovery-store.js! 2 ! 3 import RecoveryStore from "../utils/recovery-store";! 4 ! 5 export default {! 6 name: 'recovery-store',! 7 initialize: function(container, application) {! 8 application.register('services:recoveryStore', RecoveryStore);! 9 application.inject('route', 'recoveryStore', 'services:recoveryStore');! 10 }! 11 };!
  • 58. Converting “Dependency Lookup” to “Dependency Injection” 1 // app/initializers/recovery-store.js! 2 ! 3 import RecoveryStore from "../utils/recovery-store";! 4 ! 5 export default {! 6 name: 'recovery-store',! 7 initialize: function(container, application) {! 8 application.register('services:recoveryStore', RecoveryStore);! 9 application.inject('route', 'recoveryStore', 'services:recoveryStore');! 10 }! 11 };! Register the dependency
  • 59. Converting “Dependency Lookup” to “Dependency Injection” 1 // app/initializers/recovery-store.js! 2 ! 3 import RecoveryStore from "../utils/recovery-store";! 4 ! 5 export default {! 6 name: 'recovery-store',! 7 initialize: function(container, application) {! 8 application.register('services:recoveryStore', RecoveryStore);! 9 application.inject('route', 'recoveryStore', 'services:recoveryStore');! 10 }! 11 };! Inject the dependency
  • 60. Converting “Dependency Lookup” to “Dependency Injection” 1 // app/route/proposal.js! 2 import RecoveryStore from "../utils/recovery-store";! 3 import Proposal from "../model/proposal";! 4 ! 5 export default Ember.Route.extend({! 6 ! 7 model: function(){! 8 return this.recoveryStore.recover('proposalSnapshot').! 9 then(function(data){! 10 return Proposal.load(data);! 11 }, function(){! 12 return Proposal.build();! 13 });! 14 }! 15 ! 16 });! Drop the lookup
  • 61. Mocking a dependency in acceptance 1 // tests/acceptance/proposal-test.js! 2 ! 3 import Ember from 'ember';! 4 import startApp from '../helpers/start-app';! 5 import mockRecoveryStore from '../helpers/mock-recovery-store';! 6 ! 7 var App;! 8 ! 9 module('Acceptance: Proposal', {! 10 setup: function() {! 11 App = startApp();! 12 // unregister is not yet a first-class API :-/! 13 App.__container__.unregister('services:recoveryStore');! 14 App.register('services:recoveryStore', mockRecoveryStore, {! 15 instantiate: false });! 16 },! 17 teardown: function() {! 18 Ember.run(App, 'destroy');! 19 }! 20 });! 21 ! 22 test('visiting /proposal', function() {! 23 visit('/proposal');! 24 ! 25 andThen(function() {! 26 equal(currentPath(), 'proposal');! 27 });! 28 });! Unregister the normal recoveryStore
  • 62. Mocking a dependency in acceptance 1 // tests/acceptance/proposal-test.js! 2 ! 3 import Ember from 'ember';! 4 import startApp from '../helpers/start-app';! 5 import mockRecoveryStore from '../helpers/mock-recovery-store';! 6 ! 7 var App;! 8 ! 9 module('Acceptance: Proposal', {! 10 setup: function() {! 11 App = startApp();! 12 // unregister is not yet a first-class API :-/! 13 App.__container__.unregister('services:recoveryStore');! 14 App.register('services:recoveryStore', mockRecoveryStore, {! 15 instantiate: false });! 16 },! 17 teardown: function() {! 18 Ember.run(App, 'destroy');! 19 }! 20 });! 21 ! 22 test('visiting /proposal', function() {! 23 visit('/proposal');! 24 ! 25 andThen(function() {! 26 equal(currentPath(), 'proposal');! 27 });! 28 });! Register the mockRecoveryStore
  • 63. #1: Prevent regressions #2: Prevent fear #3: Prevent bad design ᕕ( ᐛ )ᕗ ✓ ✓ ✓ ✓Is fast! Why is this code good?
  • 64. STOP IT WITH ALL THE CODE (ノಥ益ಥ)ノ ┻━┻
  • 65. Starting points: ember-cli.com emberjs.com/guides/testing github.com/rwjblue/ember-qunit emberjs.com/api/classes/Ember.Test.html
  • 66. Starting points: wikipedia.org/wiki/Inversion_of_control www.martinfowler.com/articles/injection.html
  • 67. Own your dependencies Contain their APIs
  • 68. Own your dependencies Design better code with better tests