2. 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
3. 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
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 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
7. 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
8. Test-Driven Development To The Recuse!
Methodology for using automated unit
tests to drive software design, quality
and stability.
Saturday, April 27, 13
9. 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
10. 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
11. 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
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 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
19. 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
20. 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
21. 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
22. 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
24. 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
25. 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
26. 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
30. 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
31. 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