SlideShare a Scribd company logo
Stop Making Excuses and
Start Testing Your JavaScript!
Stop Making!
Excuses and!
Start Testing !
Your !
JavaScript!
• Senior UI Engineer at Netflix
• ryan.anklam@gmail.com
• @bittersweeryan
About Me
Today’s Agenda
• Adding Testing to Your Project
• Solving Common Testing Issues
• Writing Testable JavaScript
• Automating Testing
Adding Testing To Your
Project
Step 1: Pick Your Environment
TapeJest
Browser
Jest
Step 2: Pick Your Dialect
expect
describe( 'adder', function(){
it( 'should return zero when no args...',
function(){
expect( adder() ).to.equal( 0 );
} );
} );
+
should
describe( 'adder', function(){
it( 'should return zero when no args...',
function(){
adder().should.equal( 0 );
} );
} );
+
assert
+
//qunit
test( 'adder', function(){
ok( adder() === 0, 'should return 0');
} );
!
//chai
describe( 'adder', function(){
assert.equal( adder() , 0, 'should return 0' );
});
Step 3: Setup
$> npm install -g mocha
$> npm install mocha —save-dev
$> npm install chai --save-dev
module.exports = var adder = function(){
var args = Array.prototype.slice.call( arguments );
return args.reduce( function( previous, curr ){
if( !isNaN( Number( curr ) ) ){
return previous + curr;
}
else{
return curr;
}
}, 0 );
};
adder.js
var expect = require( 'chai' ).expect;
var adder = require( '../../src/adder' );
!
describe( 'adder', function(){
it( 'should add an array of numbers',
function(){
expect( adder( 1,2,3) ).to.equal( 6 );
});
});
adderSpec.js
...
describe( 'adder', function(){
it( 'should return zero if no args are passed
in', function(){
expect( adder( ) ).to.equal(0);
});
})
...
adderSpec.js
...
it( 'should skip non numbers', function(){
expect( adder( 1,2,'a' ) ).to.equal( 3 );
});
...
adderSpec.js
$> mocha tests/spec
Name of your spec directory
Browser
Browser
<html>
<head>
<title>Jasmine Spec Runner v2.0.0</title>
<link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.0/
jasmine.css">
!
<script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></
script>
<script type="text/javascript" src="lib/jasmine-2.0.0/jasmine-
html.js"></script>
<script type="text/javascript" src="lib/jasmine-2.0.0/boot.js"></
script>
!
<!-- include source files here... -->
<script type="text/javascript" src="../src/adder.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/adderSpec-jasmine.js"></
script>
</head>
<body>
</body>
</html>
SpecRunner.html
Source Files
Spec Files
Browser
Solving Common
Testing Issues
I Have Methods that Call Other
Methods.
Spies/Stubs/Mocks
Spies
User.prototype.addUser = function(){
if(validateUser()){
saveUser();
}
else{
addError( 'user not valid' );
}
};
Spies
it('should save a valid user', function(){
var user = new User( 'Ryan','Anklam');
spyOn(User, ‘validate’).and.returnValue( true );
spyOn(User, 'save');
user.add();
expect(user.validate).toHaveBeenCalled();
expect(user.save).toHaveBeenCalled();
});
Spies
it('should error for an invalid user', function(){
var user = new User( 'Ryan','Anklam');
spyOn(User, ‘validate’).andReturn( false );
spyOn(User, 'addError');
user.add();
expect(user.validate).toHaveBeenCalled();
expect(user.addError).toHaveBeenCalled();
});
Spies That Return Data
User.prototype.listUsers = function(){
var users = this.getUsers();
users.forEach( function( user ){
this.addUserToList( user );
});
};
Spies That Return Data
it('should get a list of users and add them',
function(){
spyOn(User, 'getUsers').and.returnValue(
[
{
firstName : 'Ryan',
lastName : 'Anklam'
}
]
);
spyOn(User, 'addUserToList');
user.listUsers();
expect(user.getUsers).toHaveBeenCalled();
expect(user.addUserToList).toHaveBeenCalled();
});
Promises
Returns A Promise
getStringValue: function( req, bundle, key, context ){
var i18n = require("./index"),
bundlePromise = this.getBundle( req, bundle ),
deferred = q.defer();
!
bundlePromise.then(
function( bundle ){
deferred.resolve(
bundle.get( i18n.get(key,context) ) );
},
function( err ){
deferred.reject( err );
}
);
!
return deferred.promise;
}
chai-as-promised FTW!
describe( 'getStringValue function', function(){
!
it('should return a string when it finds a key',
function ( ) {
return expect( utils.getStringValue( 'foo.bar.baz' ) )
.to.eventually.equal( 'Foo bar baz'] );
});
!
it('should return the key when no key is found',
function () {
return expect( utils.getStringValue( 'does.not.exist' ) )
.to.eventually.equal( 'does.not.exist' );
});
} );
I Need To Test the DOM
Do you REALLY need to?
Spy On The DOM
function addItems( $el, items ){
items.forEach( function( item ){
$el.append( item );
} );
}
Spy On The DOM
it( 'should add items', function(){
var $el = $( '<ul/>' ),
items = [
{ name : 'test' }
];
spyOn( jQuery.fn, 'append' );
!
addItems( $el, items );
expect( jQuery.fn.append )
.toHaveBeenCalled();
} );
Jasmine jQuery
https://github.com/velesin/jasmine-jquery
Create Fixtures as .html files
Load fixtures into a test
Make assertions
Jasmine jQuery
beforeEach( function(){
//reset the fixture before each test
loadFixtures('myfixture.html');
});
!
it( 'should convert a select to a ul', function(){
$( '.select' ).dropdown();
expect( '.select-list' ).toExist();
});
qUnit
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>QUnit Example</title>
<link rel="stylesheet" href="qunit.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="qunit.js"></script>
<script src="tests.js"></script>
</body>
</html>
Gets reset after
every test
I Have to Test Async Code
Async Test
function readConfig( callback ){
!
fs.readFile( config.fileLocation, callback );
}
Async Test
it( 'should read the config file' ,
function( done ){
var callback = function( readystate ){
expect( readystate )
.to.equal( 'config ready' );
done();
}
readConfig( callback );
} );
Async Test
asyncTest( 'should read the config file' ,
function( ){
var callback = function( readystate ){
ok( readystate === 'config ready' );
start();
}
readConfig( callback );
} );
Writing Testable
JavaScript
Not Testable JavaScript
$(function(){
var $list = $( '.todo-list' ),
$input = $( '.new-todo' ),
todos = $.ajax(...);
todos.forEach( function( item, index){
$list.append( '<li class="todo-item">' + item.name +
'</li>');
} );
$input.on( 'keyup', function( e ){
if( e.which === 13 ){
$list.append( '<li class="todo-item">' +
$(this).val() + '</li>');
$(this).val( '' );
}
} );
Repeated Code
Locked in callback
Magic Data
Can’t configure
One and doneAnonymous Function
Anonymous Function
Refactoring
$(function(){
var $list = $( '.todo-list' ),
$input = $( '.new-todo' ),
todos = $.ajax(...);
todos.forEach( function( item, index){
$list.append( '<li class="todo-item">' + item.name +
'</li>');
} );
$input.on( 'keyup', function( e ){
if( e.which === 13 ){
$list.append( '<li class="todo-item">' +
$(this).val() + '</li>');
$(this).val( '' );
}
} );
Can’t configure
Make It A Constructor
var Todos = function( $list, $input ){
this.$list = $list;
this.$input = $input;
};
A Little Setup & First Test
describe( 'todos', function(){
var todos;
beforeEach( function(){
todos = new Todos( $('<ul/>'),
$('<input type="text" value="foobar" />')
);
});
} );
it('should create an instance', function(){
expect( typeof todos).toEqual( 'object' );
});
Not Testable JavaScript
$(function(){
var $list = $( '.todo-list' ),
$input = $( '.new-todo' ),
todos = $.ajax(...);
todos.forEach( function( item, index){
$list.append( '<li class="todo-item">' + item.name +
'</li>');
} );
$input.on( 'keyup', function( e ){
if( e.which === 13 ){
$list.append( '<li class="todo-item">' +
$(this).val() + '</li>');
$(this).val( '' );
}
} );
Magic Data
getData
Todos.prototype.getData = function( success, error ){
$.ajax({
...
success : success,
error : error
...
});
}
Test getData
it(‘should have a getData fn that returns an array',
function( done ){
var callback = function( items ){
expect(items instanceof Array ).toBeTruthy();
done();
};
todos.getData( callback );
});
**Note: more tests would be to properly test this!**
Refactoring
$(function(){
var $list = $( '.todo-list' ),
$input = $( '.new-todo' ),
todos = $.ajax(...);
todos.forEach( function( item, index){
$list.append( '<li class="todo-item">' + item.name +
'</li>');
} );
$input.on( 'keyup', function( e ){
if( e.which === 13 ){
$list.append( '<li class="todo-item">' +
$(this).val() + '</li>');
$(this).val( '' );
}
} );
Anonymous Function
Refactoring
$(function(){
var $list = $( '.todo-list' ),
$input = $( '.new-todo' ),
todos = $.ajax(...);
todos.forEach( function( item, index){
$list.append( '<li class="todo-item">' + item.name +
'</li>');
} );
$input.on( 'keyup', function( e ){
if( e.which === 13 ){
$list.append( '<li class="todo-item">' +
$(this).val() + '</li>');
$(this).val( '' );
}
} );
One and done
Refactoring
$(function(){
var $list = $( '.todo-list' ),
$input = $( '.new-todo' ),
todos = $.ajax(...);
todos.forEach( function( item, index){
$list.append( '<li class="todo-item">' + item.name +
'</li>');
} );
$input.on( 'keyup', function( e ){
if( e.which === 13 ){
$list.append( '<li class="todo-item">' +
$(this).val() + '</li>');
$(this).val( '' );
}
} );
Repeated Code
addItem
Todos.prototype.addItem = function( name ){
this.$list.append(
’<li class="todo-item">' + name + ‘</li>'
);
};
Test addItem
it('should have a addItem function', function(){
expect( typeof todos.addItem ).toEqual( 'function' );
});
!
it('should try to add an item to the DOM', function(){
spyOn( jQuery.fn, 'append' );
todos.addItem( 'Learn JS Testing' );
expect( jQuery.fn.append )
.toHaveBeenCalledWith(
'<li class="todo-item">Learn JS Testing</li>'
);
});
addItems
Todos.prototype.addItems = function( items ){
items.forEach( function( item, index){
this.addItem( item.name );
}, this );
};
Test addItems
it('should have a addItems function', function(){
spyOn( todos, 'addItem' );
todos.addItems( [{name:'foo'},{name:'bar'},{name:'baz'}] );
expect( todos.addItem ).toHaveBeenCalledWith( 'foo' );
expect( todos.addItem ).toHaveBeenCalledWith( 'bar' );
expect( todos.addItem ).toHaveBeenCalledWith( 'baz' );
});
Not Testable JavaScript
$(function(){
var $list = $( '.todo-list' ),
$input = $( '.new-todo' ),
todos = $.ajax(...);
todos.forEach( function( item, index){
$list.append( '<li class="todo-item">' + item.name +
'</li>');
} );
$input.on( 'keyup', function( e ){
if( e.which === 13 ){
$list.append( '<li class="todo-item">' +
$(this).val() + '</li>');
$(this).val( '' );
}
} );
Anonymous Function
handleKeyPress
Todos.prototype.handleKeypress = function( e ){
if( e.which === 13 ){
this.addItem( this.$input.val() );
this.$input.val( '' );
}
};
Test handleKeypress
it('should have a handleKeypress function', function(){
expect( typeof todos.handleKeypress )
.toEqual( 'function' );
});
!
it('should handle a keypress', function(){
spyOn( todos, 'addItem' );
todos.handleKeypress( { which: 13 } );
expect( todos.addItem )
.toHaveBeenCalledWith( 'foobar' );
});
Refactoring
$(function(){
var $list = $( '.todo-list' ),
$input = $( '.new-todo' ),
todos = $.ajax(...);
todos.forEach( function( item, index){
$list.append( '<li class="todo-item">' + item.name +
'</li>');
} );
$input.on( 'keyup', function( e ){
if( e.which === 13 ){
$list.append( '<li class="todo-item">' +
$(this).val() + '</li>');
$(this).val( '' );
}
} );
Locked in callback
Init function
Todos.prototype.init = function(){
this.$input.on( 'keyup',
$.proxy( this.handleKeypress, this ) );
this.getData( this.addItems );
};
Test Init
it('should have an init method', function(){
expect( typeof todos.init ).toEqual( 'function' );
});
!
it('should setup a handler and additems', function(){
spyOn( jQuery.fn, 'on' );
spyOn( todos, 'getData' );
todos.init();
expect( jQuery.fn.on ).toHaveBeenCalled();
expect( todos.getData )
.toHaveBeenCalledWith( todos.addItems );
} );
A Few More Tips
• Write maintainable tests
• Avoid Singletons
• Use named functions instead of anonymous

callbacks
• Keep your functions small
Automating Testing
The key to successful testing is
making writing and running tests
easy.
The key to successful testing is
making writing and running tests
easy.
The key to successful testing is
making writing and running tests
easy.
The key to successful testing is
making writing and running tests
easy.
Using NPM
{
...
"scripts": {
"test": "mocha tests/spec"
},
...
}
Package.json
Running Build Tests
$> npm test
Using Your Build Tool
npm install -g grunt-cli
npm install grunt-contrib-jasmine
npm install grunt
gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
jasmine : {
tests: {
src: ['jquery.js','todos-better.js'],
options: { specs: 'tests/spec/todoSpec.js' }
}
}
watch: {
tests: {
files: ['**/*.js'],
tasks: ['jasmine'],
}
}
});
grunt.loadNpmTasks('grunt-contrib-jasmine', ‘watch' );
};
Running Build Tests
$> grunt jasmine
Testem
npm install -g testem
testem.json
{
"src_files" : [
"jquery.js",
"todos-better.js",
"tests/spec/todoSpec.js"
],
"launch_in_dev" : [ "PhantomJS", “chrome”, “firefox” ],
"framework" : "jasmine2"
}
Testem
$> testem
Questions?
Thank You
ryan.anklam@gmail.com
@bittersweetryan

More Related Content

What's hot

AngularJS Testing Strategies
AngularJS Testing StrategiesAngularJS Testing Strategies
AngularJS Testing Strategies
njpst8
 
Adventures In JavaScript Testing
Adventures In JavaScript TestingAdventures In JavaScript Testing
Adventures In JavaScript Testing
Thomas Fuchs
 
Unit testing with Easymock
Unit testing with EasymockUnit testing with Easymock
Unit testing with EasymockÜrgo Ringo
 
Angular testing
Angular testingAngular testing
Angular testing
Raissa Ferreira
 
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
foxp2code
 
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
Igor Napierala
 
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
Morris Singer
 
Writing and using Hamcrest Matchers
Writing and using Hamcrest MatchersWriting and using Hamcrest Matchers
Writing and using Hamcrest Matchers
Shai Yallin
 
Advanced Jasmine - Front-End JavaScript Unit Testing
Advanced Jasmine - Front-End JavaScript Unit TestingAdvanced Jasmine - Front-End JavaScript Unit Testing
Advanced Jasmine - Front-End JavaScript Unit Testing
Lars Thorup
 
Unit testing en iOS @ MobileCon Galicia
Unit testing en iOS @ MobileCon GaliciaUnit testing en iOS @ MobileCon Galicia
Unit testing en iOS @ MobileCon Galicia
Robot Media
 
Unit testing with mocha
Unit testing with mochaUnit testing with mocha
Unit testing with mocha
Revath S Kumar
 
Testing JavaScript Applications
Testing JavaScript ApplicationsTesting JavaScript Applications
Testing JavaScript Applications
The Rolling Scopes
 
Unit tests in node.js
Unit tests in node.jsUnit tests in node.js
Unit tests in node.js
Rotem Tamir
 
Tdd iPhone For Dummies
Tdd iPhone For DummiesTdd iPhone For Dummies
Tdd iPhone For Dummies
Giordano Scalzo
 
Unit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSUnit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJS
Knoldus Inc.
 
Testing with VS2010 - A Bugs Life
Testing with VS2010 - A Bugs LifeTesting with VS2010 - A Bugs Life
Testing with VS2010 - A Bugs Life
Peter Gfader
 
JavaScript TDD with Jasmine and Karma
JavaScript TDD with Jasmine and KarmaJavaScript TDD with Jasmine and Karma
JavaScript TDD with Jasmine and Karma
Christopher Bartling
 
Mockito intro
Mockito introMockito intro
Mockito intro
Jonathan Holloway
 
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
Andrey Kolodnitsky
 

What's hot (20)

AngularJS Testing Strategies
AngularJS Testing StrategiesAngularJS Testing Strategies
AngularJS Testing Strategies
 
Adventures In JavaScript Testing
Adventures In JavaScript TestingAdventures In JavaScript Testing
Adventures In JavaScript Testing
 
Unit testing with Easymock
Unit testing with EasymockUnit testing with Easymock
Unit testing with Easymock
 
Angular testing
Angular testingAngular testing
Angular testing
 
Jasmine BDD for Javascript
Jasmine BDD for JavascriptJasmine BDD for Javascript
Jasmine BDD for Javascript
 
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
 
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 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
 
Writing and using Hamcrest Matchers
Writing and using Hamcrest MatchersWriting and using Hamcrest Matchers
Writing and using Hamcrest Matchers
 
Advanced Jasmine - Front-End JavaScript Unit Testing
Advanced Jasmine - Front-End JavaScript Unit TestingAdvanced Jasmine - Front-End JavaScript Unit Testing
Advanced Jasmine - Front-End JavaScript Unit Testing
 
Unit testing en iOS @ MobileCon Galicia
Unit testing en iOS @ MobileCon GaliciaUnit testing en iOS @ MobileCon Galicia
Unit testing en iOS @ MobileCon Galicia
 
Unit testing with mocha
Unit testing with mochaUnit testing with mocha
Unit testing with mocha
 
Testing JavaScript Applications
Testing JavaScript ApplicationsTesting JavaScript Applications
Testing JavaScript Applications
 
Unit tests in node.js
Unit tests in node.jsUnit tests in node.js
Unit tests in node.js
 
Tdd iPhone For Dummies
Tdd iPhone For DummiesTdd iPhone For Dummies
Tdd iPhone For Dummies
 
Unit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSUnit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJS
 
Testing with VS2010 - A Bugs Life
Testing with VS2010 - A Bugs LifeTesting with VS2010 - A Bugs Life
Testing with VS2010 - A Bugs Life
 
JavaScript TDD with Jasmine and Karma
JavaScript TDD with Jasmine and KarmaJavaScript TDD with Jasmine and Karma
JavaScript TDD with Jasmine and Karma
 
Mockito intro
Mockito introMockito intro
Mockito intro
 
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
 

Similar to Stop Making Excuses and Start Testing Your JavaScript

Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
Visual Engineering
 
How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014
Guillaume POTIER
 
Build Lightweight Web Module
Build Lightweight Web ModuleBuild Lightweight Web Module
Build Lightweight Web ModuleMorgan Cheng
 
Rails is not just Ruby
Rails is not just RubyRails is not just Ruby
Rails is not just Ruby
Marco Otte-Witte
 
JavaScript Unit Testing with Jasmine
JavaScript Unit Testing with JasmineJavaScript Unit Testing with Jasmine
JavaScript Unit Testing with Jasmine
Raimonds Simanovskis
 
Unit Testing Front End JavaScript
Unit Testing Front End JavaScriptUnit Testing Front End JavaScript
Unit Testing Front End JavaScript
FITC
 
Jason parsing
Jason parsingJason parsing
Jason parsing
parallelminder
 
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdfJAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
calderoncasto9163
 
04 Advanced Javascript
04 Advanced Javascript04 Advanced Javascript
04 Advanced Javascript
crgwbr
 
Introduction to Protractor
Introduction to ProtractorIntroduction to Protractor
Introduction to Protractor
Jie-Wei Wu
 
Say It With Javascript
Say It With JavascriptSay It With Javascript
Say It With Javascript
Giovanni Scerra ☃
 
Nodejs do teste de unidade ao de integração
Nodejs  do teste de unidade ao de integraçãoNodejs  do teste de unidade ao de integração
Nodejs do teste de unidade ao de integração
Vinícius Pretto da Silva
 
ES6 Overview
ES6 OverviewES6 Overview
ES6 Overview
Bruno Scopelliti
 
JavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and LodashJavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and Lodash
Bret Little
 
Expert JavaScript tricks of the masters
Expert JavaScript  tricks of the mastersExpert JavaScript  tricks of the masters
Expert JavaScript tricks of the masters
Ara Pehlivanian
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
Hugo Hamon
 
JavaScript Testing for Rubyists
JavaScript Testing for RubyistsJavaScript Testing for Rubyists
JavaScript Testing for RubyistsJamie Dyer
 
Modern JavaScript Engine Performance
Modern JavaScript Engine PerformanceModern JavaScript Engine Performance
Modern JavaScript Engine PerformanceCatalin Dumitru
 

Similar to Stop Making Excuses and Start Testing Your JavaScript (20)

Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
 
How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014
 
Build Lightweight Web Module
Build Lightweight Web ModuleBuild Lightweight Web Module
Build Lightweight Web Module
 
Introduccion a Jasmin
Introduccion a JasminIntroduccion a Jasmin
Introduccion a Jasmin
 
Rails is not just Ruby
Rails is not just RubyRails is not just Ruby
Rails is not just Ruby
 
JavaScript Unit Testing with Jasmine
JavaScript Unit Testing with JasmineJavaScript Unit Testing with Jasmine
JavaScript Unit Testing with Jasmine
 
Unit Testing Front End JavaScript
Unit Testing Front End JavaScriptUnit Testing Front End JavaScript
Unit Testing Front End JavaScript
 
Pengenalan blaast platform sdk
Pengenalan blaast platform sdkPengenalan blaast platform sdk
Pengenalan blaast platform sdk
 
Jason parsing
Jason parsingJason parsing
Jason parsing
 
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdfJAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
 
04 Advanced Javascript
04 Advanced Javascript04 Advanced Javascript
04 Advanced Javascript
 
Introduction to Protractor
Introduction to ProtractorIntroduction to Protractor
Introduction to Protractor
 
Say It With Javascript
Say It With JavascriptSay It With Javascript
Say It With Javascript
 
Nodejs do teste de unidade ao de integração
Nodejs  do teste de unidade ao de integraçãoNodejs  do teste de unidade ao de integração
Nodejs do teste de unidade ao de integração
 
ES6 Overview
ES6 OverviewES6 Overview
ES6 Overview
 
JavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and LodashJavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and Lodash
 
Expert JavaScript tricks of the masters
Expert JavaScript  tricks of the mastersExpert JavaScript  tricks of the masters
Expert JavaScript tricks of the masters
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
JavaScript Testing for Rubyists
JavaScript Testing for RubyistsJavaScript Testing for Rubyists
JavaScript Testing for Rubyists
 
Modern JavaScript Engine Performance
Modern JavaScript Engine PerformanceModern JavaScript Engine Performance
Modern JavaScript Engine Performance
 

Recently uploaded

Student information management system project report ii.pdf
Student information management system project report ii.pdfStudent information management system project report ii.pdf
Student information management system project report ii.pdf
Kamal Acharya
 
space technology lecture notes on satellite
space technology lecture notes on satellitespace technology lecture notes on satellite
space technology lecture notes on satellite
ongomchris
 
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
thanhdowork
 
ethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.pptethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.ppt
Jayaprasanna4
 
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdfAKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
SamSarthak3
 
J.Yang, ICLR 2024, MLILAB, KAIST AI.pdf
J.Yang,  ICLR 2024, MLILAB, KAIST AI.pdfJ.Yang,  ICLR 2024, MLILAB, KAIST AI.pdf
J.Yang, ICLR 2024, MLILAB, KAIST AI.pdf
MLILAB
 
DESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docxDESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docx
FluxPrime1
 
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
obonagu
 
ASME IX(9) 2007 Full Version .pdf
ASME IX(9)  2007 Full Version       .pdfASME IX(9)  2007 Full Version       .pdf
ASME IX(9) 2007 Full Version .pdf
AhmedHussein950959
 
一比一原版(UofT毕业证)多伦多大学毕业证成绩单如何办理
一比一原版(UofT毕业证)多伦多大学毕业证成绩单如何办理一比一原版(UofT毕业证)多伦多大学毕业证成绩单如何办理
一比一原版(UofT毕业证)多伦多大学毕业证成绩单如何办理
ydteq
 
road safety engineering r s e unit 3.pdf
road safety engineering  r s e unit 3.pdfroad safety engineering  r s e unit 3.pdf
road safety engineering r s e unit 3.pdf
VENKATESHvenky89705
 
Investor-Presentation-Q1FY2024 investor presentation document.pptx
Investor-Presentation-Q1FY2024 investor presentation document.pptxInvestor-Presentation-Q1FY2024 investor presentation document.pptx
Investor-Presentation-Q1FY2024 investor presentation document.pptx
AmarGB2
 
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
Amil Baba Dawood bangali
 
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
AJAYKUMARPUND1
 
Runway Orientation Based on the Wind Rose Diagram.pptx
Runway Orientation Based on the Wind Rose Diagram.pptxRunway Orientation Based on the Wind Rose Diagram.pptx
Runway Orientation Based on the Wind Rose Diagram.pptx
SupreethSP4
 
CME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional ElectiveCME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional Elective
karthi keyan
 
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdfHybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
fxintegritypublishin
 
English lab ppt no titlespecENG PPTt.pdf
English lab ppt no titlespecENG PPTt.pdfEnglish lab ppt no titlespecENG PPTt.pdf
English lab ppt no titlespecENG PPTt.pdf
BrazilAccount1
 
Nuclear Power Economics and Structuring 2024
Nuclear Power Economics and Structuring 2024Nuclear Power Economics and Structuring 2024
Nuclear Power Economics and Structuring 2024
Massimo Talia
 
Architectural Portfolio Sean Lockwood
Architectural Portfolio Sean LockwoodArchitectural Portfolio Sean Lockwood
Architectural Portfolio Sean Lockwood
seandesed
 

Recently uploaded (20)

Student information management system project report ii.pdf
Student information management system project report ii.pdfStudent information management system project report ii.pdf
Student information management system project report ii.pdf
 
space technology lecture notes on satellite
space technology lecture notes on satellitespace technology lecture notes on satellite
space technology lecture notes on satellite
 
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
 
ethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.pptethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.ppt
 
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdfAKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
 
J.Yang, ICLR 2024, MLILAB, KAIST AI.pdf
J.Yang,  ICLR 2024, MLILAB, KAIST AI.pdfJ.Yang,  ICLR 2024, MLILAB, KAIST AI.pdf
J.Yang, ICLR 2024, MLILAB, KAIST AI.pdf
 
DESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docxDESIGN A COTTON SEED SEPARATION MACHINE.docx
DESIGN A COTTON SEED SEPARATION MACHINE.docx
 
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
 
ASME IX(9) 2007 Full Version .pdf
ASME IX(9)  2007 Full Version       .pdfASME IX(9)  2007 Full Version       .pdf
ASME IX(9) 2007 Full Version .pdf
 
一比一原版(UofT毕业证)多伦多大学毕业证成绩单如何办理
一比一原版(UofT毕业证)多伦多大学毕业证成绩单如何办理一比一原版(UofT毕业证)多伦多大学毕业证成绩单如何办理
一比一原版(UofT毕业证)多伦多大学毕业证成绩单如何办理
 
road safety engineering r s e unit 3.pdf
road safety engineering  r s e unit 3.pdfroad safety engineering  r s e unit 3.pdf
road safety engineering r s e unit 3.pdf
 
Investor-Presentation-Q1FY2024 investor presentation document.pptx
Investor-Presentation-Q1FY2024 investor presentation document.pptxInvestor-Presentation-Q1FY2024 investor presentation document.pptx
Investor-Presentation-Q1FY2024 investor presentation document.pptx
 
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
NO1 Uk best vashikaran specialist in delhi vashikaran baba near me online vas...
 
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
Pile Foundation by Venkatesh Taduvai (Sub Geotechnical Engineering II)-conver...
 
Runway Orientation Based on the Wind Rose Diagram.pptx
Runway Orientation Based on the Wind Rose Diagram.pptxRunway Orientation Based on the Wind Rose Diagram.pptx
Runway Orientation Based on the Wind Rose Diagram.pptx
 
CME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional ElectiveCME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional Elective
 
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdfHybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
 
English lab ppt no titlespecENG PPTt.pdf
English lab ppt no titlespecENG PPTt.pdfEnglish lab ppt no titlespecENG PPTt.pdf
English lab ppt no titlespecENG PPTt.pdf
 
Nuclear Power Economics and Structuring 2024
Nuclear Power Economics and Structuring 2024Nuclear Power Economics and Structuring 2024
Nuclear Power Economics and Structuring 2024
 
Architectural Portfolio Sean Lockwood
Architectural Portfolio Sean LockwoodArchitectural Portfolio Sean Lockwood
Architectural Portfolio Sean Lockwood
 

Stop Making Excuses and Start Testing Your JavaScript

  • 1. Stop Making Excuses and Start Testing Your JavaScript! Stop Making! Excuses and! Start Testing ! Your ! JavaScript!
  • 2. • Senior UI Engineer at Netflix • ryan.anklam@gmail.com • @bittersweeryan About Me
  • 3. Today’s Agenda • Adding Testing to Your Project • Solving Common Testing Issues • Writing Testable JavaScript • Automating Testing
  • 4. Adding Testing To Your Project
  • 5. Step 1: Pick Your Environment
  • 8. Step 2: Pick Your Dialect
  • 9. expect describe( 'adder', function(){ it( 'should return zero when no args...', function(){ expect( adder() ).to.equal( 0 ); } ); } ); +
  • 10. should describe( 'adder', function(){ it( 'should return zero when no args...', function(){ adder().should.equal( 0 ); } ); } ); +
  • 11. assert + //qunit test( 'adder', function(){ ok( adder() === 0, 'should return 0'); } ); ! //chai describe( 'adder', function(){ assert.equal( adder() , 0, 'should return 0' ); });
  • 13.
  • 14. $> npm install -g mocha $> npm install mocha —save-dev $> npm install chai --save-dev
  • 15. module.exports = var adder = function(){ var args = Array.prototype.slice.call( arguments ); return args.reduce( function( previous, curr ){ if( !isNaN( Number( curr ) ) ){ return previous + curr; } else{ return curr; } }, 0 ); }; adder.js
  • 16. var expect = require( 'chai' ).expect; var adder = require( '../../src/adder' ); ! describe( 'adder', function(){ it( 'should add an array of numbers', function(){ expect( adder( 1,2,3) ).to.equal( 6 ); }); }); adderSpec.js
  • 17. ... describe( 'adder', function(){ it( 'should return zero if no args are passed in', function(){ expect( adder( ) ).to.equal(0); }); }) ... adderSpec.js
  • 18. ... it( 'should skip non numbers', function(){ expect( adder( 1,2,'a' ) ).to.equal( 3 ); }); ... adderSpec.js
  • 19. $> mocha tests/spec Name of your spec directory
  • 20.
  • 22. Browser <html> <head> <title>Jasmine Spec Runner v2.0.0</title> <link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.0/ jasmine.css"> ! <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></ script> <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine- html.js"></script> <script type="text/javascript" src="lib/jasmine-2.0.0/boot.js"></ script> ! <!-- include source files here... --> <script type="text/javascript" src="../src/adder.js"></script> <!-- include spec files here... --> <script type="text/javascript" src="spec/adderSpec-jasmine.js"></ script> </head> <body> </body> </html> SpecRunner.html Source Files Spec Files
  • 25. I Have Methods that Call Other Methods.
  • 27.
  • 29. Spies it('should save a valid user', function(){ var user = new User( 'Ryan','Anklam'); spyOn(User, ‘validate’).and.returnValue( true ); spyOn(User, 'save'); user.add(); expect(user.validate).toHaveBeenCalled(); expect(user.save).toHaveBeenCalled(); });
  • 30. Spies it('should error for an invalid user', function(){ var user = new User( 'Ryan','Anklam'); spyOn(User, ‘validate’).andReturn( false ); spyOn(User, 'addError'); user.add(); expect(user.validate).toHaveBeenCalled(); expect(user.addError).toHaveBeenCalled(); });
  • 31. Spies That Return Data User.prototype.listUsers = function(){ var users = this.getUsers(); users.forEach( function( user ){ this.addUserToList( user ); }); };
  • 32. Spies That Return Data it('should get a list of users and add them', function(){ spyOn(User, 'getUsers').and.returnValue( [ { firstName : 'Ryan', lastName : 'Anklam' } ] ); spyOn(User, 'addUserToList'); user.listUsers(); expect(user.getUsers).toHaveBeenCalled(); expect(user.addUserToList).toHaveBeenCalled(); });
  • 34. Returns A Promise getStringValue: function( req, bundle, key, context ){ var i18n = require("./index"), bundlePromise = this.getBundle( req, bundle ), deferred = q.defer(); ! bundlePromise.then( function( bundle ){ deferred.resolve( bundle.get( i18n.get(key,context) ) ); }, function( err ){ deferred.reject( err ); } ); ! return deferred.promise; }
  • 35. chai-as-promised FTW! describe( 'getStringValue function', function(){ ! it('should return a string when it finds a key', function ( ) { return expect( utils.getStringValue( 'foo.bar.baz' ) ) .to.eventually.equal( 'Foo bar baz'] ); }); ! it('should return the key when no key is found', function () { return expect( utils.getStringValue( 'does.not.exist' ) ) .to.eventually.equal( 'does.not.exist' ); }); } );
  • 36. I Need To Test the DOM
  • 37. Do you REALLY need to?
  • 38.
  • 39. Spy On The DOM function addItems( $el, items ){ items.forEach( function( item ){ $el.append( item ); } ); }
  • 40. Spy On The DOM it( 'should add items', function(){ var $el = $( '<ul/>' ), items = [ { name : 'test' } ]; spyOn( jQuery.fn, 'append' ); ! addItems( $el, items ); expect( jQuery.fn.append ) .toHaveBeenCalled(); } );
  • 41. Jasmine jQuery https://github.com/velesin/jasmine-jquery Create Fixtures as .html files Load fixtures into a test Make assertions
  • 42. Jasmine jQuery beforeEach( function(){ //reset the fixture before each test loadFixtures('myfixture.html'); }); ! it( 'should convert a select to a ul', function(){ $( '.select' ).dropdown(); expect( '.select-list' ).toExist(); });
  • 43. qUnit <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>QUnit Example</title> <link rel="stylesheet" href="qunit.css"> </head> <body> <div id="qunit"></div> <div id="qunit-fixture"></div> <script src="qunit.js"></script> <script src="tests.js"></script> </body> </html> Gets reset after every test
  • 44. I Have to Test Async Code
  • 45. Async Test function readConfig( callback ){ ! fs.readFile( config.fileLocation, callback ); }
  • 46. Async Test it( 'should read the config file' , function( done ){ var callback = function( readystate ){ expect( readystate ) .to.equal( 'config ready' ); done(); } readConfig( callback ); } );
  • 47. Async Test asyncTest( 'should read the config file' , function( ){ var callback = function( readystate ){ ok( readystate === 'config ready' ); start(); } readConfig( callback ); } );
  • 49. Not Testable JavaScript $(function(){ var $list = $( '.todo-list' ), $input = $( '.new-todo' ), todos = $.ajax(...); todos.forEach( function( item, index){ $list.append( '<li class="todo-item">' + item.name + '</li>'); } ); $input.on( 'keyup', function( e ){ if( e.which === 13 ){ $list.append( '<li class="todo-item">' + $(this).val() + '</li>'); $(this).val( '' ); } } ); Repeated Code Locked in callback Magic Data Can’t configure One and doneAnonymous Function Anonymous Function
  • 50. Refactoring $(function(){ var $list = $( '.todo-list' ), $input = $( '.new-todo' ), todos = $.ajax(...); todos.forEach( function( item, index){ $list.append( '<li class="todo-item">' + item.name + '</li>'); } ); $input.on( 'keyup', function( e ){ if( e.which === 13 ){ $list.append( '<li class="todo-item">' + $(this).val() + '</li>'); $(this).val( '' ); } } ); Can’t configure
  • 51. Make It A Constructor var Todos = function( $list, $input ){ this.$list = $list; this.$input = $input; };
  • 52. A Little Setup & First Test describe( 'todos', function(){ var todos; beforeEach( function(){ todos = new Todos( $('<ul/>'), $('<input type="text" value="foobar" />') ); }); } ); it('should create an instance', function(){ expect( typeof todos).toEqual( 'object' ); });
  • 53. Not Testable JavaScript $(function(){ var $list = $( '.todo-list' ), $input = $( '.new-todo' ), todos = $.ajax(...); todos.forEach( function( item, index){ $list.append( '<li class="todo-item">' + item.name + '</li>'); } ); $input.on( 'keyup', function( e ){ if( e.which === 13 ){ $list.append( '<li class="todo-item">' + $(this).val() + '</li>'); $(this).val( '' ); } } ); Magic Data
  • 54. getData Todos.prototype.getData = function( success, error ){ $.ajax({ ... success : success, error : error ... }); }
  • 55. Test getData it(‘should have a getData fn that returns an array', function( done ){ var callback = function( items ){ expect(items instanceof Array ).toBeTruthy(); done(); }; todos.getData( callback ); }); **Note: more tests would be to properly test this!**
  • 56. Refactoring $(function(){ var $list = $( '.todo-list' ), $input = $( '.new-todo' ), todos = $.ajax(...); todos.forEach( function( item, index){ $list.append( '<li class="todo-item">' + item.name + '</li>'); } ); $input.on( 'keyup', function( e ){ if( e.which === 13 ){ $list.append( '<li class="todo-item">' + $(this).val() + '</li>'); $(this).val( '' ); } } ); Anonymous Function
  • 57. Refactoring $(function(){ var $list = $( '.todo-list' ), $input = $( '.new-todo' ), todos = $.ajax(...); todos.forEach( function( item, index){ $list.append( '<li class="todo-item">' + item.name + '</li>'); } ); $input.on( 'keyup', function( e ){ if( e.which === 13 ){ $list.append( '<li class="todo-item">' + $(this).val() + '</li>'); $(this).val( '' ); } } ); One and done
  • 58. Refactoring $(function(){ var $list = $( '.todo-list' ), $input = $( '.new-todo' ), todos = $.ajax(...); todos.forEach( function( item, index){ $list.append( '<li class="todo-item">' + item.name + '</li>'); } ); $input.on( 'keyup', function( e ){ if( e.which === 13 ){ $list.append( '<li class="todo-item">' + $(this).val() + '</li>'); $(this).val( '' ); } } ); Repeated Code
  • 59. addItem Todos.prototype.addItem = function( name ){ this.$list.append( ’<li class="todo-item">' + name + ‘</li>' ); };
  • 60. Test addItem it('should have a addItem function', function(){ expect( typeof todos.addItem ).toEqual( 'function' ); }); ! it('should try to add an item to the DOM', function(){ spyOn( jQuery.fn, 'append' ); todos.addItem( 'Learn JS Testing' ); expect( jQuery.fn.append ) .toHaveBeenCalledWith( '<li class="todo-item">Learn JS Testing</li>' ); });
  • 61. addItems Todos.prototype.addItems = function( items ){ items.forEach( function( item, index){ this.addItem( item.name ); }, this ); };
  • 62. Test addItems it('should have a addItems function', function(){ spyOn( todos, 'addItem' ); todos.addItems( [{name:'foo'},{name:'bar'},{name:'baz'}] ); expect( todos.addItem ).toHaveBeenCalledWith( 'foo' ); expect( todos.addItem ).toHaveBeenCalledWith( 'bar' ); expect( todos.addItem ).toHaveBeenCalledWith( 'baz' ); });
  • 63. Not Testable JavaScript $(function(){ var $list = $( '.todo-list' ), $input = $( '.new-todo' ), todos = $.ajax(...); todos.forEach( function( item, index){ $list.append( '<li class="todo-item">' + item.name + '</li>'); } ); $input.on( 'keyup', function( e ){ if( e.which === 13 ){ $list.append( '<li class="todo-item">' + $(this).val() + '</li>'); $(this).val( '' ); } } ); Anonymous Function
  • 64. handleKeyPress Todos.prototype.handleKeypress = function( e ){ if( e.which === 13 ){ this.addItem( this.$input.val() ); this.$input.val( '' ); } };
  • 65. Test handleKeypress it('should have a handleKeypress function', function(){ expect( typeof todos.handleKeypress ) .toEqual( 'function' ); }); ! it('should handle a keypress', function(){ spyOn( todos, 'addItem' ); todos.handleKeypress( { which: 13 } ); expect( todos.addItem ) .toHaveBeenCalledWith( 'foobar' ); });
  • 66. Refactoring $(function(){ var $list = $( '.todo-list' ), $input = $( '.new-todo' ), todos = $.ajax(...); todos.forEach( function( item, index){ $list.append( '<li class="todo-item">' + item.name + '</li>'); } ); $input.on( 'keyup', function( e ){ if( e.which === 13 ){ $list.append( '<li class="todo-item">' + $(this).val() + '</li>'); $(this).val( '' ); } } ); Locked in callback
  • 67. Init function Todos.prototype.init = function(){ this.$input.on( 'keyup', $.proxy( this.handleKeypress, this ) ); this.getData( this.addItems ); };
  • 68. Test Init it('should have an init method', function(){ expect( typeof todos.init ).toEqual( 'function' ); }); ! it('should setup a handler and additems', function(){ spyOn( jQuery.fn, 'on' ); spyOn( todos, 'getData' ); todos.init(); expect( jQuery.fn.on ).toHaveBeenCalled(); expect( todos.getData ) .toHaveBeenCalledWith( todos.addItems ); } );
  • 69. A Few More Tips • Write maintainable tests • Avoid Singletons • Use named functions instead of anonymous
 callbacks • Keep your functions small
  • 71. The key to successful testing is making writing and running tests easy.
  • 72. The key to successful testing is making writing and running tests easy.
  • 73. The key to successful testing is making writing and running tests easy.
  • 74. The key to successful testing is making writing and running tests easy.
  • 75. Using NPM { ... "scripts": { "test": "mocha tests/spec" }, ... } Package.json
  • 77. Using Your Build Tool npm install -g grunt-cli npm install grunt-contrib-jasmine npm install grunt
  • 78. gruntfile.js module.exports = function(grunt) { grunt.initConfig({ jasmine : { tests: { src: ['jquery.js','todos-better.js'], options: { specs: 'tests/spec/todoSpec.js' } } } watch: { tests: { files: ['**/*.js'], tasks: ['jasmine'], } } }); grunt.loadNpmTasks('grunt-contrib-jasmine', ‘watch' ); };
  • 79. Running Build Tests $> grunt jasmine
  • 81. testem.json { "src_files" : [ "jquery.js", "todos-better.js", "tests/spec/todoSpec.js" ], "launch_in_dev" : [ "PhantomJS", “chrome”, “firefox” ], "framework" : "jasmine2" }