TESTING JAVASCRIPT APPLICATIONS
by Alexander Gerasimov and Dmitrey Gerasimov
¿POR QUÉ?
¡PORQUE!
refactoring
organization, modularization, extensibility
documentation
defect prevention
collaboration
"Any feature without a test doesn’t exist"
Steve Loughran HP Laboratories
TDD & BDD
TESTS FIRST
RED/GREEN/REFACTOR
Red: Write a failing test
RED/GREEN/REFACTOR
Green: Make it pass
RED/GREEN/REFACTOR
Refactor: Eliminate redundancy
STRUCTURE
Setup: Put the Unit Under Test (UUT) or the overall test system in the state needed
to run the test.
Execution: Trigger/drive the UUT to perform the target behavior and capture all
output, such as return values and output parameters.
Validation: Ensure the results of the test are correct.
Cleanup: Restore the UUT or the overall test system to the pre-test state.
http://en.wikipedia.org/wiki/Test-driven_development#Test_structure
TDD ASSERTIONS
var assert = chai.assert;
assert.typeOf(foo, 'string');
assert.equal(foo, 'bar');
assert.lengthOf(foo, 3)
assert.property(tea, 'favors');
assert.lengthOf(tea.flavors, 3);
BDD
Behavior-driven development
GIVEN-WHEN-THEN
Story: Returns go to stock
In order to keep track of stock
As a store owner
I want to add items back to stock when they're returned
Scenario 1: Refunded items should be returned to stock
Given a customer previously bought a black sweater from me
And I currently have three black sweaters left in stock
When he returns the sweater for a refund
Then I should have four black sweaters in stock
CUCUMBER
Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two number
Scenario: Add two numbers
Given I have entered 50 into the calculator
And I have entered 70 into the calculator
When I press add
Then the result should be 120 on the screen
CUCUMBER
Given /I have entered (.*) into the calculator do
calculator = new Calculator();
calculator.push(n);
end
EXPECT
var expect = require("chai").expect
, foo = "bar"
, beverages = { tea: [ "chai", "matcha", "oolong" ] };
expect(foo).to.be.a("string");
expect(foo).to.equal("bar");
expect(foo).to.have.length(3);
expect(beverages).to.have.property("tea").with.length(3);
SHOULD
var should = require("chai").should()
, foo = "bar"
, beverages = { tea: [ "chai", "matcha", "oolong" ] };
foo.should.be.a("string");
foo.should.equal("bar");
foo.should.have.length(3);
beverages.should.have.property("tea").with.length(3);
FUNCTIONAL TESTING
UX/BEHAVIOR VERIFICATION
Unit tests just prove that your code doesn't work
AUTOMATION & CONTROL
↓
METRICS & PROFILING
Execution time
Loading, rendering, painting
CPU & Memory
Google Chrome Metrics
HELPS QA TESTERS
Why not let QA guys concentrate on quality rather than
routine?
TOOLS & EXAMPLES
generators
frameworks
[assertion] libraries
plugins
stat tools
complex solutions + Grunt
TECHNIQUES
DUMMIES
STUBS
MOCKS
SPIES
FIXTURES
JASMINE
What is it?..
JASMINE
Suites & specs
describe("A suite is just a function", function() {
var a;
it("and so is a spec", function() {
a = true;
expect(a).toBe(true);
});
});
JASMINE
Matchers
expect(x).toEqual(y);
expect(x).toBe(y);
expect(x).toMatch(pattern);
expect(x).toBeDefined();
expect(x).toBeUndefined();
expect(x).toBeNull();
expect(x).toBeTruthy();
expect(x).toBeFalsy();
expect(x).toContain(y);
expect(x).toBeLessThan(y);
expect(x).toBeGreaterThan(y);
expect(function(){fn();}).toThrow(e);
JASMINE
Spies
SPIES
Tracks
Functions calls
Arguments
Number of calls
SPIES
Access
All calls of function
Every argument of every call
SPIES
Can
Track and delegate
Substitute returning values
Call faked functions
Create mock objects
JASMINE
Any
describe("jasmine.any", function() {
it("matches any value", function() {
expect({}).toEqual(jasmine.any(Object));
expect(12).toEqual(jasmine.any(Number));
});
});
JASMINE
Clock
beforeEach(function() {
timerCallback = jasmine.createSpy("timerCallback"); //create spy
jasmine.Clock.useMock(); //use wrepper of system timer
});
it("causes a timeout to be called synchronously", function() {
setTimeout(function() {
timerCallback();
}, 100);
expect(timerCallback).not.toHaveBeenCalled();
jasmine.Clock.tick(101); //make time to go
expect(timerCallback).toHaveBeenCalled();
});
JASMINE
Async
It exists, but...
JASMINE
Reporter
describe("Jasmine", function() {
it("makes testing JavaScript awesome!", function() {
expect (yourCode).toBeLotsBetter();
});
});
MOCHA
['mɔkə]
MOCHA
Supports TDD assertions and BDD should/expect
Reporting & CI integration
JavaScript API
Browser Test Runner
MOCHA
describe('Array', function(){
describe('#indexOf()', function(){
it('should return -1 when the value is not present', function(){
[1,2,3].indexOf(5).should.equal(-1);
[1,2,3].indexOf(0).should.equal(-1);
})
})
})
MOCHA
describe('User', function(){
describe('#save()', function(){
it('should save without error', function(done){
var user = new User('Luna');
user.save(function(err){
if (err) throw err;
done();
});
})
})
})
MOCHA
Hooks: before(), after(), beforeEach(), afterEach()
beforeEach(function(done){
db.clear(function(err){
if (err) return done(err);
db.save([tobi, loki, jane], done);
});
})
MOCHA
MOCHA
Console reporter
MOCHA
HTML reporter
MOCHA
Nyan reporter
CHAI
CHAI
Assert, expect/should
chai.should();
foo.should.be.a('string');
foo.should.equal('bar');
foo.should.have.length(3);
tea.should.have.property('flavors')
.with.length(3);
QUESTION TIME!
How would you test an RNG?
CHAI
Plugins
here
CASPERJS
PhantomJS
API
SlimerJS
SpookyJS
CASPERJS
var casper = require('casper').create();
casper.start('http://domain.tld/page.html', function() {
if (this.exists('h1.page-title')) {
this.echo('the heading exists');
}
});
casper.run();
QUESTION TIME!
What happens if not everyone on the team adopts TDD/BDD?
CODE COVERAGE
INSTRUMENTATION
INSTRUMENTATION
ISTANBUL
Statement, branch, and function coverage
Test running tools
HTML & LCOV reporting
esprima-based
TESTING + CI = ❤
fail builds
statistics & reporting
Github is integration paradise
GITHUB IS INTEGRATION PARADISE
IRL, 100% COVERAGE IS A LIE
legacy & untestable code
permissive tests
not applicable to functional testing
MUTATION TESTING
Who tests tests?
Coverage is paramount! (it isn't)
Mutations: remove lines, alter operators, rename identifiers
The Future of Unit Testing
QUESTION TIME!
What do you do when you're offerred a project without TDD?
YOU RUN LIKE HELL
THE END

Testing JavaScript Applications