SlideShare ist ein Scribd-Unternehmen logo
1 von 69
Downloaden Sie, um offline zu lesen
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

Weitere ähnliche Inhalte

Was ist angesagt?

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
 

Was ist angesagt? (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
 

Ähnlich wie 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
 

Ähnlich wie 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
 

Mehr von 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
 

Mehr von 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
 

Kürzlich hochgeladen

08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
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
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...HostedbyConfluent
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGSujit Pal
 

Kürzlich hochgeladen (20)

08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
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
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAG
 

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