SlideShare a Scribd company logo
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!

More Related Content

PDF
Ember testing internals with ember cli
PPTX
Test like a pro with Ember.js
PDF
Factory Girl
PDF
Testing Ruby with Rspec (a beginner's guide)
PDF
Test-Driven Development of AngularJS Applications
PDF
Workshop 17: EmberJS parte II
PPTX
Unit testing of java script and angularjs application using Karma Jasmine Fra...
PPT
Testing Javascript with Jasmine
Ember testing internals with ember cli
Test like a pro with Ember.js
Factory Girl
Testing Ruby with Rspec (a beginner's guide)
Test-Driven Development of AngularJS Applications
Workshop 17: EmberJS parte II
Unit testing of java script and angularjs application using Karma Jasmine Fra...
Testing Javascript with Jasmine

What's hot (20)

PDF
AngularJS Unit Testing w/Karma and Jasmine
PDF
Intro to Unit Testing in AngularJS
PPTX
Rspec presentation
PPT
Ruby on Rails testing with Rspec
PPTX
Unit testing JavaScript: Jasmine & karma intro
PDF
Testing Ember Apps: Managing Dependency
PDF
Angular Unit Testing NDC Minn 2018
PPTX
Good karma: UX Patterns and Unit Testing in Angular with Karma
PDF
RSpec 3: The new, the old, the good
PPTX
Unit testing in JavaScript with Jasmine and Karma
PDF
Automated Testing in EmberJS
PPTX
Angular Unit Testing
PDF
Angularjs - Unit testing introduction
PDF
JavaScript Test-Driven Development with Jasmine 2.0 and Karma
PPTX
Javascript Testing with Jasmine 101
PPT
Spring talk111204
PDF
Client side unit tests - using jasmine & karma
PDF
Describe's Full of It's
PDF
Testing most things in JavaScript - LeedsJS 31/05/2017
PDF
Intro to testing Javascript with jasmine
AngularJS Unit Testing w/Karma and Jasmine
Intro to Unit Testing in AngularJS
Rspec presentation
Ruby on Rails testing with Rspec
Unit testing JavaScript: Jasmine & karma intro
Testing Ember Apps: Managing Dependency
Angular Unit Testing NDC Minn 2018
Good karma: UX Patterns and Unit Testing in Angular with Karma
RSpec 3: The new, the old, the good
Unit testing in JavaScript with Jasmine and Karma
Automated Testing in EmberJS
Angular Unit Testing
Angularjs - Unit testing introduction
JavaScript Test-Driven Development with Jasmine 2.0 and Karma
Javascript Testing with Jasmine 101
Spring talk111204
Client side unit tests - using jasmine & karma
Describe's Full of It's
Testing most things in JavaScript - LeedsJS 31/05/2017
Intro to testing Javascript with jasmine
Ad

Similar to Painless Javascript Unit Testing (20)

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

Recently uploaded (20)

PPTX
MYSQL Presentation for SQL database connectivity
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PPTX
Spectroscopy.pptx food analysis technology
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PPTX
sap open course for s4hana steps from ECC to s4
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
KodekX | Application Modernization Development
PDF
Approach and Philosophy of On baking technology
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Review of recent advances in non-invasive hemoglobin estimation
DOCX
The AUB Centre for AI in Media Proposal.docx
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PPTX
Big Data Technologies - Introduction.pptx
PPT
Teaching material agriculture food technology
PDF
Encapsulation theory and applications.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
MYSQL Presentation for SQL database connectivity
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Spectroscopy.pptx food analysis technology
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
sap open course for s4hana steps from ECC to s4
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Advanced methodologies resolving dimensionality complications for autism neur...
KodekX | Application Modernization Development
Approach and Philosophy of On baking technology
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Review of recent advances in non-invasive hemoglobin estimation
The AUB Centre for AI in Media Proposal.docx
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Understanding_Digital_Forensics_Presentation.pptx
Big Data Technologies - Introduction.pptx
Teaching material agriculture food technology
Encapsulation theory and applications.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...

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 = '[email protected]'; 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 = '[email protected]'; }); 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 = '[email protected]'; this.let_('email', function() { return new Email(this.emailAddress); }); }); describe('.validate', function() { shouldInvalidate('an email without an "@" sign', 'someexample.org'); shouldInvalidate('missing a domain', '[email protected]'); //.. 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!