JavaScript unit testing
with QUnit and Sinon
Lars Thorup
ZeaLake Software Consulting
June, 2013
Who is Lars Thorup?
● Software developer/architect
● JavaScript, C# and C++
● Test Driven Development
● Coaching teams in agile
engineering practices
● Assessing software projects
and companies
● Founder and CEO of
BestBrains and ZeaLake
Resources
● Tools
● http://qunitjs.com/
● http://sinonjs.org/
● Sample code
● https://github.com/larsthorup/jsdevenv-qunit
● Slides
● http://www.slideshare.net/larsthorup
● @larsthorup, lars@zealake.com
● What is unit testing about?
● Express expected behaviour of the code we write
● Why is unit testing a good thing?
● Faster development
● Find and fix bugs faster
● Prevent bugs from reappearing
● Improve the design of our software
● Reliable documentation
● How do we do unit testing?
● Write test code
● Run the tests automatically
Why are we here today?
QUnit - tests and fixtures
module('module name', {
setup: function () {
this.account = new Account(4711);
}
});
test('test name, function () {
equal(this.account.id, 4711);
});
test('...', function () {
...
});
QUnit - assertions
ok(account.isValid, 'isValid'); // !
equal(account.id, 4711, 'id'); // ==
notEqual(account.state, CLOSED, 'state'); // !=
strictEqual(account.user, user, 'user'); // ===
deepEqual(account.privs, [1, 2], 'privs');
throws(function () {
account.close()
}, /failed/, 'close');
Sinon - spies, stubs and mocks
Math.randomBelow = function (n) {
return Math.floor(Math.random() * n);
};
module('util.random', {
setup: function () {
sinon.stub(Math, 'random').returns(0.85);
},
teardown: function () {
Math.random.restore();
}
});
test('randomBelow', function () {
equal(Math.randomBelow(6), 5, '6');
});
DOM
module('Control.Button', {
setup: function () {
$('<button id="next"></button>')
.appendTo('#qunit-fixture');
this.button = new Control.Button('#next', {
text: 'Next'
});
}
});
test('constructor', function () {
equal($('#next').attr('text'), 'Next', '.text');
});
● #qunit-fixture is emptied before each test
Asynchronous code
test('delayHide', function () {
expect(2);
stop();
this.button.delayHide(function () {
equal($('#next').is(':visible'), false, '!visible');
start();
});
equal($('#next').is(':visible'), true, 'visible');
});
Button.prototype.delayHide = function (callback) {
var self = this;
setTimeout(function () {
self.element.hide();
callback();
}, 100);
};
Workflow of Test Driven Development
Failing
test
Succeeding
test
Good
design Refactor
Code
Write a
test
Idea
Think, talk
Continuous Integration with Jenkins
Unit testing philosophy
● Behaviour first
● makes more sense than "Test first"
● Structure of test programs
● Given <precondition>
● When <invocation>
● Then <expectation>
● High level as well as low level
● Test user stories and requirements
● Test class design and algorithms
● Communicate intent
● Fast feedback
Advanced mocking
● Constructors
● Time: new Date()
● Timers: setTimeout(), setInterval()
● Ajax
● Events
● DOM
● CSS transitions
● Sinon sessions

Javascript unit testing with QUnit and Sinon

  • 1.
    JavaScript unit testing withQUnit and Sinon Lars Thorup ZeaLake Software Consulting June, 2013
  • 2.
    Who is LarsThorup? ● Software developer/architect ● JavaScript, C# and C++ ● Test Driven Development ● Coaching teams in agile engineering practices ● Assessing software projects and companies ● Founder and CEO of BestBrains and ZeaLake
  • 3.
    Resources ● Tools ● http://qunitjs.com/ ●http://sinonjs.org/ ● Sample code ● https://github.com/larsthorup/jsdevenv-qunit ● Slides ● http://www.slideshare.net/larsthorup ● @larsthorup, lars@zealake.com
  • 4.
    ● What isunit testing about? ● Express expected behaviour of the code we write ● Why is unit testing a good thing? ● Faster development ● Find and fix bugs faster ● Prevent bugs from reappearing ● Improve the design of our software ● Reliable documentation ● How do we do unit testing? ● Write test code ● Run the tests automatically Why are we here today?
  • 5.
    QUnit - testsand fixtures module('module name', { setup: function () { this.account = new Account(4711); } }); test('test name, function () { equal(this.account.id, 4711); }); test('...', function () { ... });
  • 6.
    QUnit - assertions ok(account.isValid,'isValid'); // ! equal(account.id, 4711, 'id'); // == notEqual(account.state, CLOSED, 'state'); // != strictEqual(account.user, user, 'user'); // === deepEqual(account.privs, [1, 2], 'privs'); throws(function () { account.close() }, /failed/, 'close');
  • 7.
    Sinon - spies,stubs and mocks Math.randomBelow = function (n) { return Math.floor(Math.random() * n); }; module('util.random', { setup: function () { sinon.stub(Math, 'random').returns(0.85); }, teardown: function () { Math.random.restore(); } }); test('randomBelow', function () { equal(Math.randomBelow(6), 5, '6'); });
  • 8.
    DOM module('Control.Button', { setup: function() { $('<button id="next"></button>') .appendTo('#qunit-fixture'); this.button = new Control.Button('#next', { text: 'Next' }); } }); test('constructor', function () { equal($('#next').attr('text'), 'Next', '.text'); }); ● #qunit-fixture is emptied before each test
  • 9.
    Asynchronous code test('delayHide', function() { expect(2); stop(); this.button.delayHide(function () { equal($('#next').is(':visible'), false, '!visible'); start(); }); equal($('#next').is(':visible'), true, 'visible'); }); Button.prototype.delayHide = function (callback) { var self = this; setTimeout(function () { self.element.hide(); callback(); }, 100); };
  • 10.
    Workflow of TestDriven Development Failing test Succeeding test Good design Refactor Code Write a test Idea Think, talk
  • 11.
  • 12.
    Unit testing philosophy ●Behaviour first ● makes more sense than "Test first" ● Structure of test programs ● Given <precondition> ● When <invocation> ● Then <expectation> ● High level as well as low level ● Test user stories and requirements ● Test class design and algorithms ● Communicate intent ● Fast feedback
  • 13.
    Advanced mocking ● Constructors ●Time: new Date() ● Timers: setTimeout(), setInterval() ● Ajax ● Events ● DOM ● CSS transitions ● Sinon sessions