KEMBAR78
Intro to JavaScript Testing | PDF
Introduction to
JavaScript Testing
Ran Mizrahi (@ranm8)
Open Source Dpt. Leader @ CodeOasis
Saturday, April 27, 13
About CodeOasis
• CodeOasis specializes in cutting-edge web solutions.
• Large variety of customers (from startups to enterprises).
• Technologies we love:
• PHP - Symfony2 and Drupal
• node.js
• HTML5
• CSS3
• AngularJS
• Our Microsoft department works with C#, WPF, etc.
Saturday, April 27, 13
Why Do Software Projects Fail?!
• Deliver late or over budget.
• Deliver the wrong thing.
• Unstable in production.
Production Maintenance
• Expensive maintenance.
• Long adjustment to market
needs.
• Long development cycles.
Saturday, April 27, 13
Why Do Software Projects Fail?!
Saturday, April 27, 13
Untestable code...
function createUser(properties) {
var user = {
firstName: properties.firstName,
lastName: properties.lastName,
username: properties.username,
mail: properties.mail
};
var fullName = User.firstName + ' ' + User.lastName;
// Make sure user is valid
if (!user.firstName || !user.lastName) {
throw new Error('First or last name are not valid!');
} else if(typeof user.mail === 'string' && user.mail.match(new RegExp(/^w+@[a-zA-Z_]+?.[a-zA-
Z]{2,3}$/)) === null) {
throw new Error('Mail is not valid');
} else if (!user.username) {
throw new Error('Username is not valid');
}
$.post('/user', {
fullName: fullName,
userName: user.username,
mail: user.mail
}, function(data) {
var message;
if (data.code === 200) {
message = 'User saved successfully!';
} else {
message = 'Operation was failed!';
}
$('#some-div').animate({
'margin-left': $(window).width()
}, 1000, function() {
$(this).html(message);
});
});
}
Saturday, April 27, 13
Why Test Your Code???
The problems with untestable code:
• Tightly coupled.
• No separation of concerns.
• Not readable.
• Not predictable.
• Global states.
• Long methods.
• Large classes/objects.
Saturday, April 27, 13
Why Test Your Code???
The problems with untestable code:
• Tightly coupled.
• No separation of concerns.
• Not readable.
• Not predictable.
• Global states.
• Long methods.
• Large classes/objects.
>
• Hard to maintain.
• High learning curve.
• Stability issues.
• You can never expect
problems before they
occur
Saturday, April 27, 13
Test-Driven Development To The Recuse!
Methodology for using automated unit
tests to drive software design, quality
and stability.
Saturday, April 27, 13
Test-Driven Development To The Recuse!
How it’s done :
• First the developer writes
a failing test case that
defines a desired
functionality to the
software.
• Makes the code pass
those tests.
• Refactor the code to meet
standards.
Saturday, April 27, 13
Seems Great But How Much Longer Does TDD Takes???
My experience:
• Initial progress will be slower.
• Greater consistency.
• Long tern cost is drastically
lower
• After getting used to it, you
can write TDD faster (-:
Studies:
• Takes 15-30% longer.
• 45-80% less bugs.
• Fixing bugs later on is
dramatically faster.
Saturday, April 27, 13
The Three Rules of TDD
Rule #1
Your code should always fail before you implement the code
Rule #2
Implement the simplest code possible to pass your tests.
Rule #3
Refactor, refactor and refractor - There is no shame in refactoring.
Saturday, April 27, 13
BDD (Behavior-Driven Development)
Test-Driven Development
Saturday, April 27, 13
BDD (Behavior-Driven Development)
Test-Driven Development
What exactly are we testing?!
Saturday, April 27, 13
BDD (Behavior-Driven Development)
• Originally started in 2003 by Dan North, author of JBehave, the
first BDD tool.
• Based on the TDD methodology.
• Aims to provide tools for both developers and business (e.g.
product manager, etc.) to share development process together.
• The steps of BDD :
• Developers and business personas write specification together.
• Developer writes tests based on specs and make them fail.
• Write code to pass those tests.
• Refactor, refactor, refactor...
Saturday, April 27, 13
BDD (Behavior-Driven Development)
Feature: ls
In order to see the directory structure
As a UNIX user
I need to be able to list the current directory's contents
Scenario: List 2 files in a directory
Given I am in a directory "test"
And I have a file named "foo"
And I have a file named "bar"
When I run "ls"
Then I should get:
"""
bar
foo
"""
Saturday, April 27, 13
Main Test Types
• Unit Testing
• Integration Testing
• Functional Testing
Saturday, April 27, 13
Challenges Testing JavaScript
• Async tests:
• Testing async methods can be tricky.
• Define tests timeout.
• Indicate when test is completed in callback.
• Assert on callback.
• DOM:
• Testing DOM is a difficult task.
• The key is to separate your controller and model logic from
DOM and test those only.
• Testing DOM is done using functional testing (e.g. WebDriver,
etc.)
Saturday, April 27, 13
TDD/BDD using Mocha and Expect.js
Mocha is a feature-rich JavaScript test frameworks running on
node and the browser, making asynchronies tests easy.
Mocha
Main features:
• Supports both TDD and BDD styles.
• Both browser and node support.
• Proper exit status for CI support.
• node.js debugger support.
• Highly flexible, choose and join the pieces yourself (spy library,
assertion library, etc.).
Saturday, April 27, 13
TDD/BDD using Mocha and Expect.js
Expect.js is a minimalistic assertion library based on should.js
Expect.js
Main features:
• BDD style.
• Compatible with all test frameworks.
• Both node.js and browser compatible.
• Standalone assertion library.
Saturday, April 27, 13
TDD/BDD using Mocha and Expect.js
Installing Mocha and Expect.js
$ npm install mocha -g
$ npm install expect.js -g
Install mocha globally using npm:
Install Expect.js:
Saturday, April 27, 13
TDD/BDD using Mocha and Expect.js
var expect = require('expect.js');
describe('Array', function() {
describe('#indexOf()', function() {
it('Expect -1 when the value is not present', function() {
var array = [1, 2, 3];
expect(array.indexOf(4)).to.be(-1);
});
});
});
“Normal” test:
Run it..
$ mocha --reporter spec
Array
#indexOf()
✓ Expect -1 when the value is not present
1 test complete (5 ms)
Saturday, April 27, 13
TDD/BDD using Mocha and Expect.js
“Async” test:
var expect = require('expect.js');
function asyncCall(val ,callback) {
var prefix = ' - ';
setTimeout(function() {
var newString = val + prefix + 'OK';
callback(newString);
}, 500);
}
describe('asyncCall', function() {
it('Add suffix that prefixed with - to the given string', function(done) {
var testVal = 'Foo';
asyncCall(testVal, function(response) {
expect(response).to.contain(testVal + ' - OK');
done();
});
});
});
Let’s run it...
Saturday, April 27, 13
Back to our code
Saturday, April 27, 13
First, Let’s Write The Tests!
function createUser(properties) {
var user = {
firstName: properties.firstName,
lastName: properties.lastName,
username: properties.username,
mail: properties.mail
};
var fullName = User.firstName + ' ' + User.lastName;
// Make sure user is valid
if (!user.firstName || !user.lastName) {
throw new Error('First or last name are not valid!');
} else if(typeof user.mail === 'string' && user.mail.match(new RegExp(/^w+@[a-zA-Z_]+?.[a-zA-
Z]{2,3}$/)) === null) {
throw new Error('Mail is not valid');
} else if (!user.username) {
throw new Error('Username is not valid');
}
$.post('/user', {
fullName: fullName,
userName: user.username,
mail: user.mail
}, function(data) {
var message;
if (data.code === 200) {
message = 'User saved successfully!';
} else {
message = 'Operation was failed!';
}
$('#some-div').animate({
'margin-left': $(window).width()
}, 1000, function() {
$(this).html(message);
});
});
}
Saturday, April 27, 13
First, Let’s Write The Tests!
What to test in our case:
• Validations.
• Full name getter.
• User save callback
What not to test :
• DOM manipulations - for that, we should use functional testing for
that cause (e.g. WebDriver)
• AJAX requests - Leaving integration testing aside.
Saturday, April 27, 13
First, Let’s Write The Tests!
describe('User', function() {
var user;
beforeEach(function() {
// Create the user obj
user = new User({
firstName: 'Ran',
lastName: 'Mizrahi',
mail: 'ranm@codeoasis.com',
username: 'ranm'
});
});
afterEach(function() {
// clean-up
user = {};
});
describe('#fullName()', function() {
it('Should return firstName and lastName separated by space', function() {
expect(user.fullName).to.be('Ran Mizrahi');
});
});
describe('#save()', function() {
it('Should save user without any errors', function(done) {
user.save(function(error) {
if (error) {
throw error;
} else {
done();
}
});
});
});
describe('#mail()', function() {
it('Should set mail correctly after mail validation', function() {
expect(user.mail).to.be('ranm@codeoasis.com');
});
});
});
Saturday, April 27, 13
Run those tests
Saturday, April 27, 13
Now, Let’s Write The Code
var User = (function() {
'use strict';
var User = function(properties) {
// Set some data
this.firstName = properties.firstName || '';
this.lastName = properties.lastName || '';
this.username = properties.username || '';
this.mail = properties.mail || '';
};
User.__defineGetter__('fullName', function() {
return this.firstName + ' ' + this.lastName;
});
User.__defineSetter__('mail', function(mail) {
var matcher = new RegExp(/^w+@[a-zA-Z_]+?.[a-zA-Z]{2,3}$/);
if (mail.match(matcher) === null) {
throw 'Mail is not valid';
}
this._mail = mail;
});
User.prototype.save = function(callback) {
setTimeout(function() {
callback();
}, 1000);
return this;
};
return User;
}());
Saturday, April 27, 13
Run those tests,
again!
Saturday, April 27, 13
Running The Tests
mocha tests can run in different environments and formats:
• Browser - using mocha.js (see example)
• For CI automation use JSTestDriver.
• CLI - as demonstrated before using the “mocha” command.
• CI (e.g. xunit) - $ mocha test/asyncTest.js --reporter xunit.
• Many other formats (JSON, HTML, list, Spec, etc.)
Saturday, April 27, 13
Benefits of Testing Your Code
• Short feedback/testing cycle.
• High code coverage of tests that can be at run any time to
provide feedback that the software is functioning.
• Provides detailed spec/docs of the application.
• Less time spent on debugging and refactoring.
• Know what breaks early on.
• Enforces code quality (decoupled) and simplicity.
• Help you focus on writing one job code units.
Saturday, April 27, 13
Questions?
Thank you!
Saturday, April 27, 13

Intro to JavaScript Testing

  • 1.
    Introduction to JavaScript Testing RanMizrahi (@ranm8) Open Source Dpt. Leader @ CodeOasis Saturday, April 27, 13
  • 2.
    About CodeOasis • CodeOasisspecializes in cutting-edge web solutions. • Large variety of customers (from startups to enterprises). • Technologies we love: • PHP - Symfony2 and Drupal • node.js • HTML5 • CSS3 • AngularJS • Our Microsoft department works with C#, WPF, etc. Saturday, April 27, 13
  • 3.
    Why Do SoftwareProjects Fail?! • Deliver late or over budget. • Deliver the wrong thing. • Unstable in production. Production Maintenance • Expensive maintenance. • Long adjustment to market needs. • Long development cycles. Saturday, April 27, 13
  • 4.
    Why Do SoftwareProjects Fail?! Saturday, April 27, 13
  • 5.
    Untestable code... function createUser(properties){ var user = { firstName: properties.firstName, lastName: properties.lastName, username: properties.username, mail: properties.mail }; var fullName = User.firstName + ' ' + User.lastName; // Make sure user is valid if (!user.firstName || !user.lastName) { throw new Error('First or last name are not valid!'); } else if(typeof user.mail === 'string' && user.mail.match(new RegExp(/^w+@[a-zA-Z_]+?.[a-zA- Z]{2,3}$/)) === null) { throw new Error('Mail is not valid'); } else if (!user.username) { throw new Error('Username is not valid'); } $.post('/user', { fullName: fullName, userName: user.username, mail: user.mail }, function(data) { var message; if (data.code === 200) { message = 'User saved successfully!'; } else { message = 'Operation was failed!'; } $('#some-div').animate({ 'margin-left': $(window).width() }, 1000, function() { $(this).html(message); }); }); } Saturday, April 27, 13
  • 6.
    Why Test YourCode??? The problems with untestable code: • Tightly coupled. • No separation of concerns. • Not readable. • Not predictable. • Global states. • Long methods. • Large classes/objects. Saturday, April 27, 13
  • 7.
    Why Test YourCode??? The problems with untestable code: • Tightly coupled. • No separation of concerns. • Not readable. • Not predictable. • Global states. • Long methods. • Large classes/objects. > • Hard to maintain. • High learning curve. • Stability issues. • You can never expect problems before they occur Saturday, April 27, 13
  • 8.
    Test-Driven Development ToThe Recuse! Methodology for using automated unit tests to drive software design, quality and stability. Saturday, April 27, 13
  • 9.
    Test-Driven Development ToThe Recuse! How it’s done : • First the developer writes a failing test case that defines a desired functionality to the software. • Makes the code pass those tests. • Refactor the code to meet standards. Saturday, April 27, 13
  • 10.
    Seems Great ButHow Much Longer Does TDD Takes??? My experience: • Initial progress will be slower. • Greater consistency. • Long tern cost is drastically lower • After getting used to it, you can write TDD faster (-: Studies: • Takes 15-30% longer. • 45-80% less bugs. • Fixing bugs later on is dramatically faster. Saturday, April 27, 13
  • 11.
    The Three Rulesof TDD Rule #1 Your code should always fail before you implement the code Rule #2 Implement the simplest code possible to pass your tests. Rule #3 Refactor, refactor and refractor - There is no shame in refactoring. Saturday, April 27, 13
  • 12.
    BDD (Behavior-Driven Development) Test-DrivenDevelopment Saturday, April 27, 13
  • 13.
    BDD (Behavior-Driven Development) Test-DrivenDevelopment What exactly are we testing?! Saturday, April 27, 13
  • 14.
    BDD (Behavior-Driven Development) •Originally started in 2003 by Dan North, author of JBehave, the first BDD tool. • Based on the TDD methodology. • Aims to provide tools for both developers and business (e.g. product manager, etc.) to share development process together. • The steps of BDD : • Developers and business personas write specification together. • Developer writes tests based on specs and make them fail. • Write code to pass those tests. • Refactor, refactor, refactor... Saturday, April 27, 13
  • 15.
    BDD (Behavior-Driven Development) Feature:ls In order to see the directory structure As a UNIX user I need to be able to list the current directory's contents Scenario: List 2 files in a directory Given I am in a directory "test" And I have a file named "foo" And I have a file named "bar" When I run "ls" Then I should get: """ bar foo """ Saturday, April 27, 13
  • 16.
    Main Test Types •Unit Testing • Integration Testing • Functional Testing Saturday, April 27, 13
  • 17.
    Challenges Testing JavaScript •Async tests: • Testing async methods can be tricky. • Define tests timeout. • Indicate when test is completed in callback. • Assert on callback. • DOM: • Testing DOM is a difficult task. • The key is to separate your controller and model logic from DOM and test those only. • Testing DOM is done using functional testing (e.g. WebDriver, etc.) Saturday, April 27, 13
  • 18.
    TDD/BDD using Mochaand Expect.js Mocha is a feature-rich JavaScript test frameworks running on node and the browser, making asynchronies tests easy. Mocha Main features: • Supports both TDD and BDD styles. • Both browser and node support. • Proper exit status for CI support. • node.js debugger support. • Highly flexible, choose and join the pieces yourself (spy library, assertion library, etc.). Saturday, April 27, 13
  • 19.
    TDD/BDD using Mochaand Expect.js Expect.js is a minimalistic assertion library based on should.js Expect.js Main features: • BDD style. • Compatible with all test frameworks. • Both node.js and browser compatible. • Standalone assertion library. Saturday, April 27, 13
  • 20.
    TDD/BDD using Mochaand Expect.js Installing Mocha and Expect.js $ npm install mocha -g $ npm install expect.js -g Install mocha globally using npm: Install Expect.js: Saturday, April 27, 13
  • 21.
    TDD/BDD using Mochaand Expect.js var expect = require('expect.js'); describe('Array', function() { describe('#indexOf()', function() { it('Expect -1 when the value is not present', function() { var array = [1, 2, 3]; expect(array.indexOf(4)).to.be(-1); }); }); }); “Normal” test: Run it.. $ mocha --reporter spec Array #indexOf() ✓ Expect -1 when the value is not present 1 test complete (5 ms) Saturday, April 27, 13
  • 22.
    TDD/BDD using Mochaand Expect.js “Async” test: var expect = require('expect.js'); function asyncCall(val ,callback) { var prefix = ' - '; setTimeout(function() { var newString = val + prefix + 'OK'; callback(newString); }, 500); } describe('asyncCall', function() { it('Add suffix that prefixed with - to the given string', function(done) { var testVal = 'Foo'; asyncCall(testVal, function(response) { expect(response).to.contain(testVal + ' - OK'); done(); }); }); }); Let’s run it... Saturday, April 27, 13
  • 23.
    Back to ourcode Saturday, April 27, 13
  • 24.
    First, Let’s WriteThe Tests! function createUser(properties) { var user = { firstName: properties.firstName, lastName: properties.lastName, username: properties.username, mail: properties.mail }; var fullName = User.firstName + ' ' + User.lastName; // Make sure user is valid if (!user.firstName || !user.lastName) { throw new Error('First or last name are not valid!'); } else if(typeof user.mail === 'string' && user.mail.match(new RegExp(/^w+@[a-zA-Z_]+?.[a-zA- Z]{2,3}$/)) === null) { throw new Error('Mail is not valid'); } else if (!user.username) { throw new Error('Username is not valid'); } $.post('/user', { fullName: fullName, userName: user.username, mail: user.mail }, function(data) { var message; if (data.code === 200) { message = 'User saved successfully!'; } else { message = 'Operation was failed!'; } $('#some-div').animate({ 'margin-left': $(window).width() }, 1000, function() { $(this).html(message); }); }); } Saturday, April 27, 13
  • 25.
    First, Let’s WriteThe Tests! What to test in our case: • Validations. • Full name getter. • User save callback What not to test : • DOM manipulations - for that, we should use functional testing for that cause (e.g. WebDriver) • AJAX requests - Leaving integration testing aside. Saturday, April 27, 13
  • 26.
    First, Let’s WriteThe Tests! describe('User', function() { var user; beforeEach(function() { // Create the user obj user = new User({ firstName: 'Ran', lastName: 'Mizrahi', mail: 'ranm@codeoasis.com', username: 'ranm' }); }); afterEach(function() { // clean-up user = {}; }); describe('#fullName()', function() { it('Should return firstName and lastName separated by space', function() { expect(user.fullName).to.be('Ran Mizrahi'); }); }); describe('#save()', function() { it('Should save user without any errors', function(done) { user.save(function(error) { if (error) { throw error; } else { done(); } }); }); }); describe('#mail()', function() { it('Should set mail correctly after mail validation', function() { expect(user.mail).to.be('ranm@codeoasis.com'); }); }); }); Saturday, April 27, 13
  • 27.
  • 28.
    Now, Let’s WriteThe Code var User = (function() { 'use strict'; var User = function(properties) { // Set some data this.firstName = properties.firstName || ''; this.lastName = properties.lastName || ''; this.username = properties.username || ''; this.mail = properties.mail || ''; }; User.__defineGetter__('fullName', function() { return this.firstName + ' ' + this.lastName; }); User.__defineSetter__('mail', function(mail) { var matcher = new RegExp(/^w+@[a-zA-Z_]+?.[a-zA-Z]{2,3}$/); if (mail.match(matcher) === null) { throw 'Mail is not valid'; } this._mail = mail; }); User.prototype.save = function(callback) { setTimeout(function() { callback(); }, 1000); return this; }; return User; }()); Saturday, April 27, 13
  • 29.
  • 30.
    Running The Tests mochatests can run in different environments and formats: • Browser - using mocha.js (see example) • For CI automation use JSTestDriver. • CLI - as demonstrated before using the “mocha” command. • CI (e.g. xunit) - $ mocha test/asyncTest.js --reporter xunit. • Many other formats (JSON, HTML, list, Spec, etc.) Saturday, April 27, 13
  • 31.
    Benefits of TestingYour Code • Short feedback/testing cycle. • High code coverage of tests that can be at run any time to provide feedback that the software is functioning. • Provides detailed spec/docs of the application. • Less time spent on debugging and refactoring. • Know what breaks early on. • Enforces code quality (decoupled) and simplicity. • Help you focus on writing one job code units. Saturday, April 27, 13
  • 32.