This documentation below is an adaptation of the official Sinon.js documentation.
Sinon.js is included in Unit.JS, you can use Sinon.js with Unit.js.
Load Unit.js :
var test = require('unit.js');
Utils for the examples of this documentation :
// some functions
function once(fn) {
var returnValue;
var called = false;
return function() {
if (!called) {
called = true;
returnValue = fn.apply(this, arguments);
}
return returnValue;
};
};
var jQuery = {
ajax: function() {}
};
function getTodos(listId, callback) {
jQuery.ajax({
url: '/todo/' + listId + '/items',
success: function(data) {
// Node-style CPS: callback(err, data)
callback(null, data);
}
});
}
Spy
var spy = test.spy();
spy(42);
test.assert(spy.withArgs(42).calledOnce);
Calls the original function :
var callback = test.spy();
var proxy = once(callback);
proxy();
test.assert(callback.called);
Calls the original function only once :
var callback = test.spy();
var proxy = once(callback);
proxy();
proxy();
test.assert(callback.calledOnce);
// ...or:
test.assert.strictEqual(callback.callCount, 1);
Calls original function with right this and args :
var callback = test.spy();
var proxy = once(callback);
var obj = {};
proxy.call(obj, 1, 2, 3);
test.assert(callback.calledOn(obj));
test.assert(callback.calledWith(1, 2, 3));
Stubs
Returns the return value from the original function :
var callback = test.stub().returns(42);
var proxy = once(callback);
test.assert.strictEqual(proxy(), 42);
Mocks
Returns the return value from the original function :
var myAPI = {
method: function() {}
};
var mock = test.mock(myAPI);
mock.expects('method').once().returns(42);
var proxy = once(myAPI.method);
test.assert.equal(proxy(), 42);
mock.verify();
Test should call a method with exceptions :
var myAPI = {
method: function() {}
};
var mock = test.mock(myAPI);
mock.expects('method').once().throws();
test.exception(function() {
myAPI.method();
})
.isInstanceOf(Error);
mock.verify();
Matchers
Test should assert fuzzy :
var book = {
pages: 42,
author: 'cjno'
};
var spy = test.spy();
spy(book);
test.sinon.assert.calledWith(spy, test.sinon.match({
author: 'cjno'
}));
test.sinon.assert.calledWith(spy, test.sinon.match.has('pages', 42));
Test should stub method differently based on argument types :
var callback = test.stub();
callback.withArgs(test.sinon.match.string).returns(true);
callback.withArgs(test.sinon.match.number).throws('TypeError');
test.bool(callback('abc')).isTrue(); // Returns true
test.exception(function() {
callback(123); // Throws TypeError
})
.isValid(function(err) {
if (err.name === 'TypeError') {
return true;
}
});
Combining matchers :
var stringOrNumber = test.sinon.match.string
.or(test.sinon.match.number);
var bookWithPages = test.sinon.match.object
.and(test.sinon.match.has('pages'));
var book = {
pages: 42,
author: 'cjno'
};
var spy = test.spy();
var otherSpy = test.spy();
spy(book);
otherSpy(10);
test.sinon.assert.calledWith(spy, bookWithPages);
test.sinon.assert.calledWith(otherSpy, stringOrNumber);
Custom matchers :
var equal10 = test.sinon.match(function(value) {
return value === 10;
}, 'value is not equal to 10');
var spy = test.spy();
var otherSpy = test.spy();
spy(10);
otherSpy(42);
// ok because the argument value 10 is identical to 10 expected
test.sinon.assert.calledWith(spy, equal10);
test.exception(function() {
// throws an exception because the argument value 42
// is not identical to 10 expected
test.sinon.assert.calledWith(otherSpy, equal10);
})
.hasMessage(/value is not equal to 10/);
Sandbox
it('test using test.sinon.test sandbox', test.sinon.test(function() {
var myAPI = {
method: function() {}
};
var mockMyApi = this.mock(myAPI).expects('method').once().returns(42);
var proxy = once(myAPI.method);
test.number(proxy()).isIdenticalTo(42);
mockMyApi.verify();
}));
Testing Ajax
after(function() {
// When the test either fails or passes, restore the original
// jQuery ajax function (Sinon.JS also provides tools to help
// test frameworks automate clean-up like this)
jQuery.ajax.restore();
});
it('makes a GET request for todo items', function() {
test.stub(jQuery, 'ajax');
getTodos(42, test.spy());
test.assert(jQuery.ajax.calledWithMatch({
url: '/todo/42/items'
}));
});
Faking time
// original function
function throttle(callback) {
var timer;
return function() {
clearTimeout(timer);
var args = [].slice.call(arguments);
timer = setTimeout(function() {
callback.apply(this, args);
}, 100);
};
}
var clock;
// before load the fake timers
before(function() {
clock = test.useFakeTimers();
});
// after the test, restore the timers
after(function() {
clock.restore();
});
it('calls callback after 100ms', function() {
var callback = test.spy();
var throttled = throttle(callback);
throttled();
// faking 99ms
clock.tick(99);
test.assert(callback.notCalled);
// faking 1ms
clock.tick(1);
test.assert(callback.calledOnce);
// Also:
test.assert.strictEqual(new Date().getTime(), 100);
});