2010-12-08 - Changelog - Source code
Standalone test spies, stubs and mocks for JavaScript.
No dependencies, works with any unit testing framework.
Seamless Sinon.JS integration for QUnit.
2010-12-08 - Changelog - Source code
2016/09/19 - Changelog - Source code
This page contains light introduction to the topics Sinon.JS implements as well as some examples, all in light of the sinon-qunit plugin. It is currently very basic, but will hopefully improve in the not-so-distant future. See the full API reference for a complete reference of Sinon.JS features.
To set up a test case using QUnit and Sinon.JS, copy the below HTML template:
<!DOCTYPE html>
<html lang="en">
<head>
<title>QUnit/Sinon.JS</title>
<link rel="stylesheet" href="qunit.css" type="text/css" media="screen">
</head>
<body>
<h1 id="qunit-header">QUnit/Sinon.JS Test</h1>
<h2 id="qunit-banner"></h2>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="qunit.js"></script>
<script type="text/javascript" src="sinon-1.17.6.js"></script>
<script type="text/javascript" src="sinon-ie-1.17.6.js"></script>
<script type="text/javascript" src="sinon-qunit-1.0.0.js"></script>
<!-- Source and test files go here -->
</body>
</html>
Note! You don't have to use sinon-qunit to use Sinon.JS with QUnit, but this is the recommended approach.
A test spy is a function that records arguments, return value, the value
of this and exception thrown (if any) for all its calls. A
test spy can be an anonymous function or it can wrap an existing
function.
Test spies are useful to test both callbacks and how certain functions/methods are used throughout the system under test.
this.spy()
Calling this.spy with no arguments creates an anonymous
spy. The following test is taken from Morgan Roderick's
PubSubJS
and shows how anonymous spies are useful for testing callbacks.
test("should call all subscribers for a message exactly once", function () {
var message = getUniqueString();
var spy1 = this.spy();
PubSub.subscribe(message, spy1);
var spy2 = this.spy();
PubSub.subscribe(message, spy2);
PubSub.publishSync(message, "my payload");
ok(spy1.calledOnce, "first subscriber called once");
ok(spy2.calledOnce, "second subscriber called once");
});
this.spy can also spy on existing functions. When doing
so, the original function will behave just as normal (including when
used as a constructor) but you will have access to data about all
calls. The following is a slightly contrived example:
test("should inspect jQuery.getJSON's
usage of jQuery.ajax", function () {
this.spy(jQuery, "ajax");
jQuery.getJSON("/some/resource");
ok(jQuery.ajax.calledOnce);
equals(jQuery.ajax.getCall(0).args[0].url, "/some/resource");
equals(jQuery.ajax.getCall(0).args[0].dataType, "json");
});
With sinon-qunit, the jQuery.ajax method will be
automatically restored/"unspied" after the test.
Spies provide a rich interface to inspect their usage. The above
examples showed the calledOnce boolean property as well as
the getCall method and the returned
object's args property. There are three ways of inspecting
call data.
The preferred approach is to use the spy's calledWith
method (and friends) because it keeps your test from being too specific
about which call did what and so on. It will return true if
the spy was ever called with the provided arguments.
test("should call subscribers with message as first argument", function () {
var message = getUniqueString();
var spy = this.spy();
PubSub.subscribe(message, spy);
PubSub.publishSync(message, "some payload");
ok(spy.calledWith(message));
});
If you want to be specific, you can directly check the first argument of the first call. There are two ways of achieving this:
test("should call subscribers with message as first argument", function () {
var message = getUniqueString();
var spy = this.spy();
PubSub.subscribe(message, spy);
PubSub.publishSync(message, "some payload");
equals(spy.args[0][0], message);
});
test("should call subscribers with message as first argument", function () {
var message = getUniqueString();
var spy = this.spy();
PubSub.subscribe(message, spy);
PubSub.publishSync(message, "some payload");
equals(spy.getCall(0).args[0], message);
});
The first example uses the two-dimensional args array
directly on the spy, while the second example fetches the first call
object and then accesses it's args array. Which one to use
is a matter of preference, but the recommended approach is going with
spy.calledWith(arg1, arg2, ...) unless there's a need to
make the tests highly specific.
Test stubs are functions (spies) with pre-programmed behavior. They support the full test spy interface in addition to methods which can be used to alter the stub's behavior.
As spies, stubs can be either anonymous, or wrap existing functions. When wrapping an existing function with a stub, the original function is not called.
The following example is yet another test from Morgan Roderick's PubSubJS which shows how to create an anonymous stub that throws an exception when called.
test("should call all subscribers, even if there are exceptions", function () {
var message = getUniqueString();
var error = getUniqueString();
var stub = this.stub().throws();
var spy1 = this.spy();
var spy2 = this.spy();
PubSub.subscribe(message, stub);
PubSub.subscribe(message, spy1);
PubSub.subscribe(message, spy2);
PubSub.publishSync(message, undefined);
ok(spy1.called);
ok(spy2.called);
ok(stub.calledBefore(spy1));
});
Note how the stub also implements the spy interface. The test verifies that all callbacks where called, and also that the exception throwing stub was called before one of the other callbacks.
Mocks are slightly different from spies and stubs in that you mock an object, and then set an expectation on one or more of its methods. Expectations implement both the spies and stubs interfaces and additionally set pass criteria upfront.
To see how mocks look like in Sinon.JS, here's one of the PubSubJS tests again, this time using a method as callback and using mocks to verify its behavior:
test("should call all subscribers when exceptions", function () {
var myAPI = { method: function () {} };
var spy = this.spy();
var mock = this.mock(myAPI);
mock.expects("method").once().throws();
PubSub.subscribe("message", myAPI.method);
PubSub.subscribe("message", spy);
PubSub.publishSync("message", undefined);
mock.verify();
ok(spy.calledOnce);
});
Fake timers is a synchronous implementation of setTimeout
and friends that Sinon.JS can overwrite the global functions with to
allow you to more easily test code using them. Fake timers provide
a clock object to pass time, which can also be used to
control Date objects created through either new
Date(); or Date.now(); (if supported by the
browser).
When faking timers with IE you also need sinon-ie-1.17.6, which should be loaded after sinon-1.17.6.js.
The fake timers can be used completely stand-alone by downloading sinon-timers-1.17.6. When using the fake timers in IE you also need sinon-timers-ie-1.17.6. Load it after the first file.
test("should animate element over 500ms", function () {
var el = jQuery("<div></div>");
el.appendTo(document.body);
el.animate({ height: "200px", width: "200px" });
this.clock.tick(510);
equals("200px", el.css("height"));
equals("200px", el.css("width"));
});
XMLHttpRequest API reference
Provides a fake implementation of XMLHttpRequest and
provides several interfaces for manipulating objects created by it. Also
fakes the native XMLHttpRequest
and ActiveXObject (if available, and only
for XMLHTTP progids). Helps with testing requests made
with XHR.
When faking XHR with IE you also need sinon-ie-1.17.6, which should be loaded after sinon-1.17.6.js.
The fake server and XHR can be used completely stand-alone by downloading sinon-server-1.17.6. When using the fake server in IE you also need sinon-ie-1.17.6. Load it after the first file.
test("should fetch comments from server", function () {
var xhr = this.sandbox.useFakeXMLHttpRequest();
var requests = this.requests = [];
xhr.onCreate = function (request) {
requests.push(request);
};
var callback = this.spy();
myLib.getCommentsFor("/some/article", callback);
equals(1, this.requests.length);
requests[0].respond(200, { "Content-Type": "application/json" },
'[{ id: 12, comment: "Hey there" }]');
ok(callback.calledWith([{ id: 12, comment: "Hey there" }]));
});
High-level API to manipulate FakeXMLHttpRequest instances.
test("should fetch comments from server", function () {
var server = this.sandbox.useFakeServer();
server.respondWith("GET", "/some/article/comments.json",
[200, { "Content-Type": "application/json" },
'[{ id: 12, comment: "Hey there" }]']);
var callback = this.spy();
myLib.getCommentsFor("/some/article", callback);
server.respond();
ok(callback.calledWith([{ id: 12, comment: "Hey there" }]));
});
Sinon.JS ships with a set of assertions that mirror most behavior verification methods and properties on spies and stubs. The advantage of using the assertions is that failed expectations on stubs and spies can be expressed directly as assertion failures with detailed and helpful error messages.
The assertions can be used with either spies or stubs.
test("should call subscribers with message as first argument", function () {
var message = getUniqueString();
var spy = this.spy();
PubSub.subscribe(message, spy);
PubSub.publishSync(message, "some payload");
sinon.assert.calledOnce(spy);
sinon.assert.calledWith(spy, message);
});
Sinon uses Semantic versioning.
Copyright 2010 - 2016, Christian Johansen. Released under the BSD license.