SlideShare una empresa de Scribd logo
1 de 28
Descargar para leer sin conexión
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!

Más contenido relacionado

La actualidad más candente

La actualidad más candente (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
 

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

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

Último

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 

Último (20)

🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
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...
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
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
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
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
 
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...
 
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
 
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?
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
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
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
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
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
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
 

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!