SlideShare ist ein Scribd-Unternehmen logo
1 von 84
Downloaden Sie, um offline zu lesen
Stop Making Excuses and
Start Testing Your JavaScript!
Stop Making!
Excuses and!
Start Testing !
Your !
JavaScript!
‱ Senior UI Engineer at NetïŹ‚ix
‱ 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 ïŹles
Load ïŹxtures 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 conïŹgure
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 conïŹgure
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
gruntïŹle.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

Weitere Àhnliche Inhalte

Was ist angesagt?

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
 
Tdd iPhone For Dummies
Tdd iPhone For DummiesTdd iPhone For Dummies
Tdd iPhone For DummiesGiordano Scalzo
 
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
 

Was ist angesagt? (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
 

Ähnlich wie 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
 
Rails is not just Ruby
Rails is not just RubyRails is not just Ruby
Rails is not just RubyMarco Otte-Witte
 
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
 
Pengenalan blaast platform sdk
Pengenalan blaast platform sdkPengenalan blaast platform sdk
Pengenalan blaast platform sdkArief Bayu Purwanto
 
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
 

Ähnlich wie 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
 

KĂŒrzlich hochgeladen

Hospital management system project report.pdf
Hospital management system project report.pdfHospital management system project report.pdf
Hospital management system project report.pdfKamal Acharya
 
BhubaneswarđŸŒčCall Girls Bhubaneswar ❀Komal 9777949614 💟 Full Trusted CALL GIRL...
BhubaneswarđŸŒčCall Girls Bhubaneswar ❀Komal 9777949614 💟 Full Trusted CALL GIRL...BhubaneswarđŸŒčCall Girls Bhubaneswar ❀Komal 9777949614 💟 Full Trusted CALL GIRL...
BhubaneswarđŸŒčCall Girls Bhubaneswar ❀Komal 9777949614 💟 Full Trusted CALL GIRL...Call Girls Mumbai
 
Online food ordering system project report.pdf
Online food ordering system project report.pdfOnline food ordering system project report.pdf
Online food ordering system project report.pdfKamal Acharya
 
DeepFakes presentation : brief idea of DeepFakes
DeepFakes presentation : brief idea of DeepFakesDeepFakes presentation : brief idea of DeepFakes
DeepFakes presentation : brief idea of DeepFakesMayuraD1
 
FEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced Loads
FEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced LoadsFEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced Loads
FEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced LoadsArindam Chakraborty, Ph.D., P.E. (CA, TX)
 
Moment Distribution Method For Btech Civil
Moment Distribution Method For Btech CivilMoment Distribution Method For Btech Civil
Moment Distribution Method For Btech CivilVinayVitekari
 
Thermal Engineering-R & A / C - unit - V
Thermal Engineering-R & A / C - unit - VThermal Engineering-R & A / C - unit - V
Thermal Engineering-R & A / C - unit - VDineshKumar4165
 
Online electricity billing project report..pdf
Online electricity billing project report..pdfOnline electricity billing project report..pdf
Online electricity billing project report..pdfKamal Acharya
 
A CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptx
A CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptxA CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptx
A CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptxmaisarahman1
 
Computer Networks Basics of Network Devices
Computer Networks  Basics of Network DevicesComputer Networks  Basics of Network Devices
Computer Networks Basics of Network DevicesChandrakantDivate1
 
Hostel management system project report..pdf
Hostel management system project report..pdfHostel management system project report..pdf
Hostel management system project report..pdfKamal Acharya
 
Introduction to Serverless with AWS Lambda
Introduction to Serverless with AWS LambdaIntroduction to Serverless with AWS Lambda
Introduction to Serverless with AWS LambdaOmar Fathy
 
Unit 4_Part 1 CSE2001 Exception Handling and Function Template and Class Temp...
Unit 4_Part 1 CSE2001 Exception Handling and Function Template and Class Temp...Unit 4_Part 1 CSE2001 Exception Handling and Function Template and Class Temp...
Unit 4_Part 1 CSE2001 Exception Handling and Function Template and Class Temp...drmkjayanthikannan
 
Standard vs Custom Battery Packs - Decoding the Power Play
Standard vs Custom Battery Packs - Decoding the Power PlayStandard vs Custom Battery Packs - Decoding the Power Play
Standard vs Custom Battery Packs - Decoding the Power PlayEpec Engineered Technologies
 
Orlando’s Arnold Palmer Hospital Layout Strategy-1.pptx
Orlando’s Arnold Palmer Hospital Layout Strategy-1.pptxOrlando’s Arnold Palmer Hospital Layout Strategy-1.pptx
Orlando’s Arnold Palmer Hospital Layout Strategy-1.pptxMuhammadAsimMuhammad6
 
data_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdfdata_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdfJiananWang21
 
GEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLE
GEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLEGEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLE
GEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLEselvakumar948
 
A Study of Urban Area Plan for Pabna Municipality
A Study of Urban Area Plan for Pabna MunicipalityA Study of Urban Area Plan for Pabna Municipality
A Study of Urban Area Plan for Pabna MunicipalityMorshed Ahmed Rahath
 
Wadi Rum luxhotel lodge Analysis case study.pptx
Wadi Rum luxhotel lodge Analysis case study.pptxWadi Rum luxhotel lodge Analysis case study.pptx
Wadi Rum luxhotel lodge Analysis case study.pptxNadaHaitham1
 

KĂŒrzlich hochgeladen (20)

Hospital management system project report.pdf
Hospital management system project report.pdfHospital management system project report.pdf
Hospital management system project report.pdf
 
BhubaneswarđŸŒčCall Girls Bhubaneswar ❀Komal 9777949614 💟 Full Trusted CALL GIRL...
BhubaneswarđŸŒčCall Girls Bhubaneswar ❀Komal 9777949614 💟 Full Trusted CALL GIRL...BhubaneswarđŸŒčCall Girls Bhubaneswar ❀Komal 9777949614 💟 Full Trusted CALL GIRL...
BhubaneswarđŸŒčCall Girls Bhubaneswar ❀Komal 9777949614 💟 Full Trusted CALL GIRL...
 
Online food ordering system project report.pdf
Online food ordering system project report.pdfOnline food ordering system project report.pdf
Online food ordering system project report.pdf
 
DeepFakes presentation : brief idea of DeepFakes
DeepFakes presentation : brief idea of DeepFakesDeepFakes presentation : brief idea of DeepFakes
DeepFakes presentation : brief idea of DeepFakes
 
FEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced Loads
FEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced LoadsFEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced Loads
FEA Based Level 3 Assessment of Deformed Tanks with Fluid Induced Loads
 
Moment Distribution Method For Btech Civil
Moment Distribution Method For Btech CivilMoment Distribution Method For Btech Civil
Moment Distribution Method For Btech Civil
 
Thermal Engineering-R & A / C - unit - V
Thermal Engineering-R & A / C - unit - VThermal Engineering-R & A / C - unit - V
Thermal Engineering-R & A / C - unit - V
 
Online electricity billing project report..pdf
Online electricity billing project report..pdfOnline electricity billing project report..pdf
Online electricity billing project report..pdf
 
A CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptx
A CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptxA CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptx
A CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptx
 
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak HamilCara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
 
Computer Networks Basics of Network Devices
Computer Networks  Basics of Network DevicesComputer Networks  Basics of Network Devices
Computer Networks Basics of Network Devices
 
Hostel management system project report..pdf
Hostel management system project report..pdfHostel management system project report..pdf
Hostel management system project report..pdf
 
Introduction to Serverless with AWS Lambda
Introduction to Serverless with AWS LambdaIntroduction to Serverless with AWS Lambda
Introduction to Serverless with AWS Lambda
 
Unit 4_Part 1 CSE2001 Exception Handling and Function Template and Class Temp...
Unit 4_Part 1 CSE2001 Exception Handling and Function Template and Class Temp...Unit 4_Part 1 CSE2001 Exception Handling and Function Template and Class Temp...
Unit 4_Part 1 CSE2001 Exception Handling and Function Template and Class Temp...
 
Standard vs Custom Battery Packs - Decoding the Power Play
Standard vs Custom Battery Packs - Decoding the Power PlayStandard vs Custom Battery Packs - Decoding the Power Play
Standard vs Custom Battery Packs - Decoding the Power Play
 
Orlando’s Arnold Palmer Hospital Layout Strategy-1.pptx
Orlando’s Arnold Palmer Hospital Layout Strategy-1.pptxOrlando’s Arnold Palmer Hospital Layout Strategy-1.pptx
Orlando’s Arnold Palmer Hospital Layout Strategy-1.pptx
 
data_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdfdata_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdf
 
GEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLE
GEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLEGEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLE
GEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLE
 
A Study of Urban Area Plan for Pabna Municipality
A Study of Urban Area Plan for Pabna MunicipalityA Study of Urban Area Plan for Pabna Municipality
A Study of Urban Area Plan for Pabna Municipality
 
Wadi Rum luxhotel lodge Analysis case study.pptx
Wadi Rum luxhotel lodge Analysis case study.pptxWadi Rum luxhotel lodge Analysis case study.pptx
Wadi Rum luxhotel lodge Analysis case study.pptx
 

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 NetïŹ‚ix ‱ 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 ïŹles Load ïŹxtures 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 conïŹgure 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 conïŹgure
  • 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. gruntïŹle.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" }