Skip to content

cjohansen.no

Using Sinon.JS with QUnit

Since my talk at Front-Trends in Poland last week I've gotten a few requests for examples of using Sinon.JS with QUnit. Sinon.JS is completely test framework agnostic and should be very easy to use with any testing framework. This post introduces a mini-plugin, sinon-qunit and shows a few examples of using Sinon.JS with QUnit.

Spies, stubs and mocks

Using one-off spies, stubs and mocks is straight-forward, and does not require anything extra. Set up a QUnit test case HTML file like so:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<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-0.8.0.js"></script>
    <script type="text/javascript" src="sinon-ie-0.8.0.js"></script>
    <!-- Source and test files go here -->
  </body>
</html>

This is just a vanilla QUnit setup, with two additional files - Sinon.JS and the IE fix. With this in place you can use stubs and spies like so:

test("Using spies", function () {
    var spy = sinon.spy();
    spy();

    ok(spy.called);
    ok(spy.calledOnce);
});

As long as you are not messing with the global environment, this works as expected.

Faking global methods

If you want to spy on, stub or mock global methods, like jQuery.ajax, the above approach will give you trouble because the fakes are not restored after the test has run. The higher-level helpers in Sinon.JS can easily be used as previously explained with QUnit through the help of sinon-qunit. Add the following line to the HTML file:

<script type="text/javascript" src="sinon-qunit-0.8.0.js"></script>

Then you can use spy, stub and mock as properties of this inside the test:

test("Stubbing global environments": function () {
    this.stub(jQuery, "ajax");
    jQuery.ajax("/url", {});

    ok(jQuery.ajax.called);
});

When the test is done running, the jQuery.ajax method is restored to the original one.

Timers

The clock property works as previously explained:

test("Some timing stuff", function () {
    var spy = this.spy();
    setTimeout(spy, 150);

    this.clock.tick(149);
    ok(!spy.called);

    this.clock.tick(1);
    ok(spy.called);
});

XMLHttpRequest and the fake server

The final piece of the puzzle is the fake server. The default configuration for sinon-qunit is slightly more conservative than the default one. It does not automatically stub XMLHttpRequest. However, you can easily use it. All you have to do is to call this.sandbox.useFakeServer:

test("should make request", function () {
    var server = this.sandbox.useFakeServer();
    var dataSource = new XHRDataSource();
    dataSource.get();

    equal(1, server.requests.length);
});

More examples

If you're keen on seeing more examples, I've update the repository for the code from my Front-Trends 2010 talk. It now includes a directory with all the tests updated for QUnit, so you can compare them to the original JsTestDriver tests.

The test case can be viewed live here.

The live search functional test shows how you can use Sinon.JS to stub network requests:

test("should display suggestions as user types", function () {
    var server = this.sandbox.useFakeServer();

    server.respondWith(
        "GET", "/search?q=Robo",
        [200, { "Content-Type": "application/json" },
         '["Robocop", "Robocop 2", "Robocop 3"]']
    );

    server.respondWith(
        "GET", "/search?q=Roboc",
        [200, { "Content-Type": "application/json" },
         '["Robocop", "Robocop 2", "Robocop 3"]']
    );

    var form = jQuery("#qunit-fixture");
    form.liveSearch();
    var input = form.find("input[type=text]");

    input.val("R");
    input.trigger("keyup");
    this.clock.tick(89);

    input.val("Ro");
    input.trigger("keyup");
    this.clock.tick(98);

    input.val("Rob");
    input.trigger("keyup");
    this.clock.tick(69);

    input.val("Robo");
    input.trigger("keyup");
    this.clock.tick(109);

    var results = form.find("ol.live-search-results li");
    equal(0, results.length);

    input.val("Roboc");
    input.trigger("keyup");
    this.clock.tick(150);

    server.respond();

    results = form.find("ol.live-search-results li");
    equal(3, results.length);
    equal("Robocop", results.get(0).innerHTML);
    equal("Robocop 2", results.get(1).innerHTML);
    equal("Robocop 3", results.get(2).innerHTML);
});

Known issues

Everything in Sinon.JS works as expected with the QUnit plugin, even the assertions. However, when using the Sinon.JS assertions, QUnit does not get the assertion counter right, as you can see on the above live search example.

Let me know what you think, either here, or find me on Twitter (@cjno).

Possibly related

2006 - 2014 Christian Johansen Creative Commons License