SlideShare une entreprise Scribd logo
1  sur  28
Télécharger pour lire hors ligne
PAINLESS JAVASCRIPT UNIT TESTING
The Presenters
@traviskaufman @benmidi
Setup
Introduction Structure Patterns Resources
Setup
Introduction Structure Patterns Resources
Setup
Introduction Patterns ResourcesStructure
Setup
Introduction Structure Patterns Resources
Introduction
Team Bicycle
● A Director of Product and Two developers
● Behavior Driven Development and Pairing
● Launched the new mobile web experience in 5 months
● 1000 Unit Tests and 200 Acceptance Tests
Introduction
Pairing
How many times have you been here?
Introduction
Pairing
Warning: They may laugh at you.
Introduction
Team Bicycle
● A Director of Product and Two developers
● Behavior Driven Development and Pairing
● Launched the new mobile web experience in 5 months
● 1000 Unit Tests and 200 Acceptance Tests
A team built for speed.
Introduction
Product
As close to native as possible. http://www.refinery29.com
Structure Grunt sets up tasks for running tests, example: grunt specs:debug
Gruntfile.js # Grunt for automating test runs
karma.conf.js # Karma for unit testing (Chrome for debugging, PhantomJS for CI builds in
Jenkins)
app/
| js/
| | models/
| | **/*.js
| | views/
| | **/*.js
spec/
| js/
| | models/
| | **/*_spec.js
| | views/
| | **/*_spec.js
Structure Karma sets up our testing environments. Great for running tests on actual devices.
Gruntfile.js # Grunt for automating test runs
karma.conf.js # Karma for unit testing (Chrome for debugging, PhantomJS for CI builds in
Jenkins)
app/
| js/
| | models/
| | **/*.js
| | views/
| | **/*.js
spec/
| js/
| | models/
| | **/*_spec.js
| | views/
| | **/*_spec.js
Structure The spec/ directory mirrors app/
Gruntfile.js # Grunt for automating test runs
karma.conf.js # Karma for unit testing
app/
| js/
| | models/
| | **/*.js
| | views/
| | **/*.js
spec/
| js/
| | models/
| | **/*_spec.js
| | views/
| | **/*_spec.js
Structure Inside of spec/ is the helpers/ directory for global setup/tear down,
mocks, convenience functions and custom Jasmine matchers.
Gruntfile.js # Grunt for automating test runs
karma.conf.js # Karma for unit testing
app/
| js/
| | models/
| | **/*.js
| | views/
| | **/*.js
spec/
| helpers/
| | *.js # Helper files loaded in Karma
| js/
| | models/
| | **/*_spec.js
| | views/
| | **/*_spec.js
Structure Our rspec acceptance tests also live in spec/
Gruntfile.js # Grunt for automating test runs
karma.conf.js # Karma for unit testing
app/
| js/
| | models/
| | **/*.js
| | views/
| | **/*.js
spec/
| helpers/
| | *.js # Helper files loaded in Karma
| js/
| | models/
| | **/*_spec.js
| | views/
| | **/*_spec.js
| features/
| | *_spec.rb
| | spec_helper.rb
| | support/
| | **/*.rb
Patterns
DRY it up
describe('views.card', function() {
var model, view;
beforeEach(function() {
model = {};
view = new CardView(model);
});
describe('.render', function() {
beforeEach(function() {
model.title = 'An Article';
view.render();
});
it('creates a "cardTitle" h3 tag set to the model's title', function() {
expect(view.$el.find('.cardTitle')).toContainText(model.title);
});
});
describe('when the model card type is "author_card"', function() {
beforeEach(function() {
model.type = 'author_card';
view.render();
});
it('adds an "authorCard" class to it's $el', function() {
expect(view.$el).toHaveClass('authorCard');
});
});
});
Patterns
Using this
describe('views.card', function() {
beforeEach(function() {
this.model = {};
this.view = new CardView(this.model);
});
describe('.render', function() {
beforeEach(function() {
this.model.title = 'An Article';
this.view.render();
});
it('creates a "cardTitle" h3 tag set to the model's title', function() {
expect(this.view.$el.find('.cardTitle')).toContainText(this.model.title);
});
});
describe('when the model card type is "author_card"', function() {
beforeEach(function() {
this.model.type = 'author_card';
this.view.render();
});
it('adds an "authorCard" class to it's $el', function() {
expect(this.view.$el).toHaveClass('authorCard');
});
});
});
Patterns
Using this
Jasmine’s userContext (aka this)
● Shared between before/afterEach hooks and tests
(including nested tests)
● Cleaned up after every test
● Removes the need for keeping track of variable declarations
● Removes problems that may occur due to scoping and/or hoisting issues
● No global leaks
● Clearer meaning within tests
● Leads the way to...
Patterns
Lazy Eval
beforeEach(function() {
this.let_ = function(propName, getter) { // need to use "_" suffix since 'let' is a token in ES6
var _lazy;
Object.defineProperty(this, propName, {
get: function() {
if (!_lazy) {
_lazy = getter.call(this);
}
return _lazy;
},
set: function() {},
enumerable: true,
configurable: true
});
};
});
Lazy Evaluation (a la Rspec’s let)
describe('.render', function() {
beforeEach(function() {
this.let_('renderedView', function() {
return this.view.render();
});
this.model.title = 'An Article';
});
// ...
Patterns
Lazy Eval
describe('views.Card', function() {
beforeEach(function() {
this.model = {};
this.view = new CardView(this.model);
});
describe('.render', function() {
beforeEach(function() {
this.model.title = 'An Article';
this.let_('renderedView', function() {
return this.view.render();
});
});
it('creates a "cardTitle" h3 tag set to the model's title', function() {
expect(this.renderedView.$el.find('.cardTitle')).toContainText(this.model.title);
});
describe('when the model card type is "author_card"', function() {
beforeEach(function() {
this.model.type = 'author_card'; // no need to re-render the view here!
});
it('adds an "authorCard" class to its $el', function() {
expect(this.renderedView.$el).toHaveClass('authorCard');
});
// ...
Patterns
Lazy Eval
Lazy Evaluation
● Our render spec, now uses let_
● We only have to call render once, in the getter function, even if we change
the model in nested beforeEach blocks
● Reduced code duplication
● Reduced chance of pollution due to side effects
● Smaller file sizes!
● Note: everything here can be used for Mocha as well!
Patterns
Behaves Like
describe('Email', function() {
beforeEach(function() {
this.emailAddress = 'some@example.com';
this.let_('email', function() {
return new Email(this.emailAddress);
});
});
describe('.validate', function() {
describe('when emailAddress is missing the "@" symbol', function() {
beforeEach(function() {
this.emailAddress = 'someexample.com';
});
it('returns false', function() {
expect(this.email.validate()).toBe(false);
});
});
describe('when emailAddress is missing a domain after the "@" symbol', function() {
beforeEach(function() {
this.emailAddress = 'some@.org';
});
it('returns false', function() {
expect(this.email.validate()).toBe(false);
});
});
});
});
Patterns
Behaves Like
Shared Behavior
● Thorough, but lots of copy-pasta code
● If API changes, all tests have to be updated
○ e.g. what if validate changes to return a string?
● RSpec solves this with “shared examples”
● Need something like that here...
Patterns
Behaves Like
describe('Email', function() {
beforeEach(function() {
this.emailAddress = 'some@example.com';
this.let_('email', function() {
return new Email(this.emailAddress);
});
});
describe('.validate', function() {
shouldInvalidate('an email without an "@" sign', 'someexample.org');
shouldInvalidate('missing a domain', 'some@.org');
//..
function shouldInvalidate(desc, example) {
describe('when emailAddress is ' + desc, function() {
beforeEach(function() {
this.emailAddress = example;
});
it('returns false', function() {
expect(this.email.validate()).toBe(false);
});
});
}
});
});
Patterns
Behaves Like
Shared Behavior
● No more duplication of testing logic
● Extremely readable
● Leverage this along with javascripts metaprogramming techniques to create
dynamically built suites
● Looks great in the console
● Completely DRY
● Huge win for something like email validation, where there are tons of cases
to test
● Prevents having one test with a large amount of assertions
Resources
We’re Hiring
Front-End Engineer | Full Time
QA Engineer | Full Time
Developer Tools & Automation Engineer | Full Time
Junior DevOps Engineer | Full Time
Desktop Support Engineer | Full Time
Back-End Engineer | Full Time
Resources
Links
● Our Blog:
http://j.mp/R29MobileBlog
● Travis’ Gist on userContext:
http://bit.ly/BetterJasmine
● Slides:
http://bit.ly/JSStackUp
Thanks!

Contenu connexe

Tendances

Tendances (20)

AngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and JasmineAngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and Jasmine
 
Intro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSIntro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJS
 
Rspec presentation
Rspec presentationRspec presentation
Rspec presentation
 
Ruby on Rails testing with Rspec
Ruby on Rails testing with RspecRuby on Rails testing with Rspec
Ruby on Rails testing with Rspec
 
Unit testing JavaScript: Jasmine & karma intro
Unit testing JavaScript: Jasmine & karma introUnit testing JavaScript: Jasmine & karma intro
Unit testing JavaScript: Jasmine & karma intro
 
Testing Ember Apps: Managing Dependency
Testing Ember Apps: Managing DependencyTesting Ember Apps: Managing Dependency
Testing Ember Apps: Managing Dependency
 
Angular Unit Testing NDC Minn 2018
Angular Unit Testing NDC Minn 2018Angular Unit Testing NDC Minn 2018
Angular Unit Testing NDC Minn 2018
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with Karma
 
RSpec 3: The new, the old, the good
RSpec 3: The new, the old, the goodRSpec 3: The new, the old, the good
RSpec 3: The new, the old, the good
 
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
 
Automated Testing in EmberJS
Automated Testing in EmberJSAutomated Testing in EmberJS
Automated Testing in EmberJS
 
Angular Unit Testing
Angular Unit TestingAngular Unit Testing
Angular Unit Testing
 
Angularjs - Unit testing introduction
Angularjs - Unit testing introductionAngularjs - Unit testing introduction
Angularjs - Unit testing introduction
 
JavaScript Test-Driven Development with Jasmine 2.0 and Karma
JavaScript Test-Driven Development with Jasmine 2.0 and Karma JavaScript Test-Driven Development with Jasmine 2.0 and Karma
JavaScript Test-Driven Development with Jasmine 2.0 and Karma
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101
 
Spring talk111204
Spring talk111204Spring talk111204
Spring talk111204
 
Client side unit tests - using jasmine & karma
Client side unit tests - using jasmine & karmaClient side unit tests - using jasmine & karma
Client side unit tests - using jasmine & karma
 
Describe's Full of It's
Describe's Full of It'sDescribe's Full of It's
Describe's Full of It's
 
Testing most things in JavaScript - LeedsJS 31/05/2017
Testing most things in JavaScript - LeedsJS 31/05/2017Testing most things in JavaScript - LeedsJS 31/05/2017
Testing most things in JavaScript - LeedsJS 31/05/2017
 
Intro to testing Javascript with jasmine
Intro to testing Javascript with jasmineIntro to testing Javascript with jasmine
Intro to testing Javascript with jasmine
 

Similaire à Painless Javascript Unit Testing

Javascript unit testing, yes we can e big
Javascript unit testing, yes we can   e bigJavascript unit testing, yes we can   e big
Javascript unit testing, yes we can e big
Andy Peterson
 

Similaire à Painless Javascript Unit Testing (20)

JavaScript and UI Architecture Best Practices
JavaScript and UI Architecture Best PracticesJavaScript and UI Architecture Best Practices
JavaScript and UI Architecture Best Practices
 
Quick tour to front end unit testing using jasmine
Quick tour to front end unit testing using jasmineQuick tour to front end unit testing using jasmine
Quick tour to front end unit testing using jasmine
 
Unit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSUnit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJS
 
Protractor framework architecture with example
Protractor framework architecture with exampleProtractor framework architecture with example
Protractor framework architecture with example
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to java
 
AngularJS application architecture
AngularJS application architectureAngularJS application architecture
AngularJS application architecture
 
Beyond DOMReady: Ultra High-Performance Javascript
Beyond DOMReady: Ultra High-Performance JavascriptBeyond DOMReady: Ultra High-Performance Javascript
Beyond DOMReady: Ultra High-Performance Javascript
 
SproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsSproutCore and the Future of Web Apps
SproutCore and the Future of Web Apps
 
A few good JavaScript development tools
A few good JavaScript development toolsA few good JavaScript development tools
A few good JavaScript development tools
 
Reliable Javascript
Reliable Javascript Reliable Javascript
Reliable Javascript
 
Build Web Apps using Node.js
Build Web Apps using Node.jsBuild Web Apps using Node.js
Build Web Apps using Node.js
 
Ecmascript 2015 – best of new features()
Ecmascript 2015 – best of new features()Ecmascript 2015 – best of new features()
Ecmascript 2015 – best of new features()
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
EcmaScript 6 - The future is here
EcmaScript 6 - The future is hereEcmaScript 6 - The future is here
EcmaScript 6 - The future is here
 
JSConf: All You Can Leet
JSConf: All You Can LeetJSConf: All You Can Leet
JSConf: All You Can Leet
 
Javascript ui for rest services
Javascript ui for rest servicesJavascript ui for rest services
Javascript ui for rest services
 
Xopus Application Framework
Xopus Application FrameworkXopus Application Framework
Xopus Application Framework
 
Javascript unit testing, yes we can e big
Javascript unit testing, yes we can   e bigJavascript unit testing, yes we can   e big
Javascript unit testing, yes we can e big
 
Junit_.pptx
Junit_.pptxJunit_.pptx
Junit_.pptx
 

Dernier

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Dernier (20)

What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
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
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
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 future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
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
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 

Painless Javascript Unit Testing

  • 7. Introduction Team Bicycle ● A Director of Product and Two developers ● Behavior Driven Development and Pairing ● Launched the new mobile web experience in 5 months ● 1000 Unit Tests and 200 Acceptance Tests
  • 10. Introduction Team Bicycle ● A Director of Product and Two developers ● Behavior Driven Development and Pairing ● Launched the new mobile web experience in 5 months ● 1000 Unit Tests and 200 Acceptance Tests A team built for speed.
  • 11. Introduction Product As close to native as possible. http://www.refinery29.com
  • 12. Structure Grunt sets up tasks for running tests, example: grunt specs:debug Gruntfile.js # Grunt for automating test runs karma.conf.js # Karma for unit testing (Chrome for debugging, PhantomJS for CI builds in Jenkins) app/ | js/ | | models/ | | **/*.js | | views/ | | **/*.js spec/ | js/ | | models/ | | **/*_spec.js | | views/ | | **/*_spec.js
  • 13. Structure Karma sets up our testing environments. Great for running tests on actual devices. Gruntfile.js # Grunt for automating test runs karma.conf.js # Karma for unit testing (Chrome for debugging, PhantomJS for CI builds in Jenkins) app/ | js/ | | models/ | | **/*.js | | views/ | | **/*.js spec/ | js/ | | models/ | | **/*_spec.js | | views/ | | **/*_spec.js
  • 14. Structure The spec/ directory mirrors app/ Gruntfile.js # Grunt for automating test runs karma.conf.js # Karma for unit testing app/ | js/ | | models/ | | **/*.js | | views/ | | **/*.js spec/ | js/ | | models/ | | **/*_spec.js | | views/ | | **/*_spec.js
  • 15. Structure Inside of spec/ is the helpers/ directory for global setup/tear down, mocks, convenience functions and custom Jasmine matchers. Gruntfile.js # Grunt for automating test runs karma.conf.js # Karma for unit testing app/ | js/ | | models/ | | **/*.js | | views/ | | **/*.js spec/ | helpers/ | | *.js # Helper files loaded in Karma | js/ | | models/ | | **/*_spec.js | | views/ | | **/*_spec.js
  • 16. Structure Our rspec acceptance tests also live in spec/ Gruntfile.js # Grunt for automating test runs karma.conf.js # Karma for unit testing app/ | js/ | | models/ | | **/*.js | | views/ | | **/*.js spec/ | helpers/ | | *.js # Helper files loaded in Karma | js/ | | models/ | | **/*_spec.js | | views/ | | **/*_spec.js | features/ | | *_spec.rb | | spec_helper.rb | | support/ | | **/*.rb
  • 17. Patterns DRY it up describe('views.card', function() { var model, view; beforeEach(function() { model = {}; view = new CardView(model); }); describe('.render', function() { beforeEach(function() { model.title = 'An Article'; view.render(); }); it('creates a "cardTitle" h3 tag set to the model's title', function() { expect(view.$el.find('.cardTitle')).toContainText(model.title); }); }); describe('when the model card type is "author_card"', function() { beforeEach(function() { model.type = 'author_card'; view.render(); }); it('adds an "authorCard" class to it's $el', function() { expect(view.$el).toHaveClass('authorCard'); }); }); });
  • 18. Patterns Using this describe('views.card', function() { beforeEach(function() { this.model = {}; this.view = new CardView(this.model); }); describe('.render', function() { beforeEach(function() { this.model.title = 'An Article'; this.view.render(); }); it('creates a "cardTitle" h3 tag set to the model's title', function() { expect(this.view.$el.find('.cardTitle')).toContainText(this.model.title); }); }); describe('when the model card type is "author_card"', function() { beforeEach(function() { this.model.type = 'author_card'; this.view.render(); }); it('adds an "authorCard" class to it's $el', function() { expect(this.view.$el).toHaveClass('authorCard'); }); }); });
  • 19. Patterns Using this Jasmine’s userContext (aka this) ● Shared between before/afterEach hooks and tests (including nested tests) ● Cleaned up after every test ● Removes the need for keeping track of variable declarations ● Removes problems that may occur due to scoping and/or hoisting issues ● No global leaks ● Clearer meaning within tests ● Leads the way to...
  • 20. Patterns Lazy Eval beforeEach(function() { this.let_ = function(propName, getter) { // need to use "_" suffix since 'let' is a token in ES6 var _lazy; Object.defineProperty(this, propName, { get: function() { if (!_lazy) { _lazy = getter.call(this); } return _lazy; }, set: function() {}, enumerable: true, configurable: true }); }; }); Lazy Evaluation (a la Rspec’s let) describe('.render', function() { beforeEach(function() { this.let_('renderedView', function() { return this.view.render(); }); this.model.title = 'An Article'; }); // ...
  • 21. Patterns Lazy Eval describe('views.Card', function() { beforeEach(function() { this.model = {}; this.view = new CardView(this.model); }); describe('.render', function() { beforeEach(function() { this.model.title = 'An Article'; this.let_('renderedView', function() { return this.view.render(); }); }); it('creates a "cardTitle" h3 tag set to the model's title', function() { expect(this.renderedView.$el.find('.cardTitle')).toContainText(this.model.title); }); describe('when the model card type is "author_card"', function() { beforeEach(function() { this.model.type = 'author_card'; // no need to re-render the view here! }); it('adds an "authorCard" class to its $el', function() { expect(this.renderedView.$el).toHaveClass('authorCard'); }); // ...
  • 22. Patterns Lazy Eval Lazy Evaluation ● Our render spec, now uses let_ ● We only have to call render once, in the getter function, even if we change the model in nested beforeEach blocks ● Reduced code duplication ● Reduced chance of pollution due to side effects ● Smaller file sizes! ● Note: everything here can be used for Mocha as well!
  • 23. Patterns Behaves Like describe('Email', function() { beforeEach(function() { this.emailAddress = 'some@example.com'; this.let_('email', function() { return new Email(this.emailAddress); }); }); describe('.validate', function() { describe('when emailAddress is missing the "@" symbol', function() { beforeEach(function() { this.emailAddress = 'someexample.com'; }); it('returns false', function() { expect(this.email.validate()).toBe(false); }); }); describe('when emailAddress is missing a domain after the "@" symbol', function() { beforeEach(function() { this.emailAddress = 'some@.org'; }); it('returns false', function() { expect(this.email.validate()).toBe(false); }); }); }); });
  • 24. Patterns Behaves Like Shared Behavior ● Thorough, but lots of copy-pasta code ● If API changes, all tests have to be updated ○ e.g. what if validate changes to return a string? ● RSpec solves this with “shared examples” ● Need something like that here...
  • 25. Patterns Behaves Like describe('Email', function() { beforeEach(function() { this.emailAddress = 'some@example.com'; this.let_('email', function() { return new Email(this.emailAddress); }); }); describe('.validate', function() { shouldInvalidate('an email without an "@" sign', 'someexample.org'); shouldInvalidate('missing a domain', 'some@.org'); //.. function shouldInvalidate(desc, example) { describe('when emailAddress is ' + desc, function() { beforeEach(function() { this.emailAddress = example; }); it('returns false', function() { expect(this.email.validate()).toBe(false); }); }); } }); });
  • 26. Patterns Behaves Like Shared Behavior ● No more duplication of testing logic ● Extremely readable ● Leverage this along with javascripts metaprogramming techniques to create dynamically built suites ● Looks great in the console ● Completely DRY ● Huge win for something like email validation, where there are tons of cases to test ● Prevents having one test with a large amount of assertions
  • 27. Resources We’re Hiring Front-End Engineer | Full Time QA Engineer | Full Time Developer Tools & Automation Engineer | Full Time Junior DevOps Engineer | Full Time Desktop Support Engineer | Full Time Back-End Engineer | Full Time
  • 28. Resources Links ● Our Blog: http://j.mp/R29MobileBlog ● Travis’ Gist on userContext: http://bit.ly/BetterJasmine ● Slides: http://bit.ly/JSStackUp Thanks!