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

Más contenido relacionado

La actualidad más candente

AngularJS Testing Strategies
AngularJS Testing StrategiesAngularJS Testing Strategies
AngularJS Testing Strategiesnjpst8
 
Adventures In JavaScript Testing
Adventures In JavaScript TestingAdventures In JavaScript Testing
Adventures In JavaScript TestingThomas Fuchs
 
Unit testing with Easymock
Unit testing with EasymockUnit testing with Easymock
Unit testing with EasymockÜrgo Ringo
 
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 Jasminefoxp2code
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyIgor Napierala
 
Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Morris Singer
 
Writing and using Hamcrest Matchers
Writing and using Hamcrest MatchersWriting and using Hamcrest Matchers
Writing and using Hamcrest MatchersShai 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 TestingLars Thorup
 
Unit testing en iOS @ MobileCon Galicia
Unit testing en iOS @ MobileCon GaliciaUnit testing en iOS @ MobileCon Galicia
Unit testing en iOS @ MobileCon GaliciaRobot Media
 
Unit testing with mocha
Unit testing with mochaUnit testing with mocha
Unit testing with mochaRevath S Kumar
 
Testing JavaScript Applications
Testing JavaScript ApplicationsTesting JavaScript Applications
Testing JavaScript ApplicationsThe Rolling Scopes
 
Unit tests in node.js
Unit tests in node.jsUnit tests in node.js
Unit tests in node.jsRotem Tamir
 
Unit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSUnit Testing and Coverage for AngularJS
Unit Testing and Coverage for AngularJSKnoldus Inc.
 
Testing with VS2010 - A Bugs Life
Testing with VS2010 - A Bugs LifeTesting with VS2010 - A Bugs Life
Testing with VS2010 - A Bugs LifePeter Gfader
 
JavaScript TDD with Jasmine and Karma
JavaScript TDD with Jasmine and KarmaJavaScript TDD with Jasmine and Karma
JavaScript TDD with Jasmine and KarmaChristopher Bartling
 
Unit testing in JavaScript with Jasmine and Karma
Unit testing in JavaScript with Jasmine and KarmaUnit testing in JavaScript with Jasmine and Karma
Unit testing in JavaScript with Jasmine and KarmaAndrey Kolodnitsky
 

La actualidad más candente (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 a Stop Making Excuses and Start Testing Your JavaScript

Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testingVisual 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 2014Guillaume POTIER
 
Build Lightweight Web Module
Build Lightweight Web ModuleBuild Lightweight Web Module
Build Lightweight Web ModuleMorgan Cheng
 
JavaScript Unit Testing with Jasmine
JavaScript Unit Testing with JasmineJavaScript Unit Testing with Jasmine
JavaScript Unit Testing with JasmineRaimonds Simanovskis
 
Unit Testing Front End JavaScript
Unit Testing Front End JavaScriptUnit Testing Front End JavaScript
Unit Testing Front End JavaScriptFITC
 
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___________________________________.pdfcalderoncasto9163
 
04 Advanced Javascript
04 Advanced Javascript04 Advanced Javascript
04 Advanced Javascriptcrgwbr
 
Introduction to Protractor
Introduction to ProtractorIntroduction to Protractor
Introduction to ProtractorJie-Wei Wu
 
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çãoVinícius Pretto da Silva
 
JavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and LodashJavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and LodashBret Little
 
Expert JavaScript tricks of the masters
Expert JavaScript  tricks of the mastersExpert JavaScript  tricks of the masters
Expert JavaScript tricks of the mastersAra Pehlivanian
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & RESTHugo 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 a 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
 

Último

The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...ranjana rawat
 
MANUFACTURING PROCESS-II UNIT-5 NC MACHINE TOOLS
MANUFACTURING PROCESS-II UNIT-5 NC MACHINE TOOLSMANUFACTURING PROCESS-II UNIT-5 NC MACHINE TOOLS
MANUFACTURING PROCESS-II UNIT-5 NC MACHINE TOOLSSIVASHANKAR N
 
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...ranjana rawat
 
AKTU Computer Networks notes --- Unit 3.pdf
AKTU Computer Networks notes ---  Unit 3.pdfAKTU Computer Networks notes ---  Unit 3.pdf
AKTU Computer Networks notes --- Unit 3.pdfankushspencer015
 
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Serviceranjana rawat
 
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...Soham Mondal
 
Java Programming :Event Handling(Types of Events)
Java Programming :Event Handling(Types of Events)Java Programming :Event Handling(Types of Events)
Java Programming :Event Handling(Types of Events)simmis5
 
APPLICATIONS-AC/DC DRIVES-OPERATING CHARACTERISTICS
APPLICATIONS-AC/DC DRIVES-OPERATING CHARACTERISTICSAPPLICATIONS-AC/DC DRIVES-OPERATING CHARACTERISTICS
APPLICATIONS-AC/DC DRIVES-OPERATING CHARACTERISTICSKurinjimalarL3
 
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur High Profile
 
Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur Escorts
Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur EscortsCall Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur Escorts
Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur High Profile
 
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete RecordCCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete RecordAsst.prof M.Gokilavani
 
UNIT-III FMM. DIMENSIONAL ANALYSIS
UNIT-III FMM.        DIMENSIONAL ANALYSISUNIT-III FMM.        DIMENSIONAL ANALYSIS
UNIT-III FMM. DIMENSIONAL ANALYSISrknatarajan
 
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...Dr.Costas Sachpazis
 
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...ranjana rawat
 
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...Dr.Costas Sachpazis
 
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur EscortsHigh Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur High Profile
 
UNIT-II FMM-Flow Through Circular Conduits
UNIT-II FMM-Flow Through Circular ConduitsUNIT-II FMM-Flow Through Circular Conduits
UNIT-II FMM-Flow Through Circular Conduitsrknatarajan
 
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...ranjana rawat
 

Último (20)

DJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINE
DJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINEDJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINE
DJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINE
 
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
 
MANUFACTURING PROCESS-II UNIT-5 NC MACHINE TOOLS
MANUFACTURING PROCESS-II UNIT-5 NC MACHINE TOOLSMANUFACTURING PROCESS-II UNIT-5 NC MACHINE TOOLS
MANUFACTURING PROCESS-II UNIT-5 NC MACHINE TOOLS
 
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
The Most Attractive Pune Call Girls Budhwar Peth 8250192130 Will You Miss Thi...
 
AKTU Computer Networks notes --- Unit 3.pdf
AKTU Computer Networks notes ---  Unit 3.pdfAKTU Computer Networks notes ---  Unit 3.pdf
AKTU Computer Networks notes --- Unit 3.pdf
 
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
 
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...
 
Java Programming :Event Handling(Types of Events)
Java Programming :Event Handling(Types of Events)Java Programming :Event Handling(Types of Events)
Java Programming :Event Handling(Types of Events)
 
APPLICATIONS-AC/DC DRIVES-OPERATING CHARACTERISTICS
APPLICATIONS-AC/DC DRIVES-OPERATING CHARACTERISTICSAPPLICATIONS-AC/DC DRIVES-OPERATING CHARACTERISTICS
APPLICATIONS-AC/DC DRIVES-OPERATING CHARACTERISTICS
 
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
 
Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur Escorts
Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur EscortsCall Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur Escorts
Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur Escorts
 
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete RecordCCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
CCS335 _ Neural Networks and Deep Learning Laboratory_Lab Complete Record
 
UNIT-III FMM. DIMENSIONAL ANALYSIS
UNIT-III FMM.        DIMENSIONAL ANALYSISUNIT-III FMM.        DIMENSIONAL ANALYSIS
UNIT-III FMM. DIMENSIONAL ANALYSIS
 
Water Industry Process Automation & Control Monthly - April 2024
Water Industry Process Automation & Control Monthly - April 2024Water Industry Process Automation & Control Monthly - April 2024
Water Industry Process Automation & Control Monthly - April 2024
 
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
Structural Analysis and Design of Foundations: A Comprehensive Handbook for S...
 
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(ANVI) Koregaon Park Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
 
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
Sheet Pile Wall Design and Construction: A Practical Guide for Civil Engineer...
 
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur EscortsHigh Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Meera Call 7001035870 Meet With Nagpur Escorts
 
UNIT-II FMM-Flow Through Circular Conduits
UNIT-II FMM-Flow Through Circular ConduitsUNIT-II FMM-Flow Through Circular Conduits
UNIT-II FMM-Flow Through Circular Conduits
 
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
 

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" }