This article will be appearing in my forthcoming book on Backbone.js and continues the section on unit testing. We previously looked at Jasmine and will now look at QUnit and SinonJS.

QUnit is a powerful JavaScript test suite written by jQuery team member Jörn Zaefferer and used by many large open-source projects (such as jQuery and Backbone.js) to test their code. It’s both capable of testing standard JavaScript code in the browser as well as code on the server-side (where environments supported include Rhino, V8 and SpiderMonkey). This makes it a robust solution for a large number of use-cases.

Quite a few Backbone.js contributors feel that QUnit is a better introductory framework for testing if you don’t wish to start off with Jasmine and BDD right away. As we’ll see later on in this article, QUnit can also be combined with third-party solutions such as SinonJS to produce an even more powerful testing solution supporting spies and mocks, which some say is preferable over Jasmine.

My personal recommendation is that it’s worth comparing both frameworks and opting for the solution that you feel the most comfortable with.

QUnit

Getting Setup

Luckily, getting QUnit setup is a fairly straight-forward process that will take less than 5 minutes.

Let’s go through the elements above with qunit mentioned in their ID. When QUnit is running:

qunit-header shows the name of the test suite

qunit-banner shows up as red if a test fails and green if all tests pass

qunit-testrunner-toolbar contains additional options for configuring the display of tests

qunit-userAgent displays the navigator.userAgent property

qunit-tests is a container for our test results

When running correctly, the above test runner looks as follows:

The numbers of the form (a, b, c) after each test name correspond to a) failed asserts, b) passed asserts and c) total asserts. Clicking on a test name expands it to display all of the assertions for that test case. Assertions in green have successfully passed.

If however any tests fail, the test gets highlighted (and the qunit-banner at the top switches to red):

Assertions

QUnit supports a number of basic assertions, which are used in testing to verify that the result being returned by our code is what we expect. If an assertion fails, we know that a bug exists. Similar to Jasmine, QUnit can be used to easily test for regressions. Specifically, when a bug is found one can write an assertion to test the existence of the bug, write a patch and then commit both. If subsequent changes to the code break the test you’ll know what was responsible and be able to address it more easily.

Some of the supported QUnit assertions we’re going to look at first are:

expect( amount ) – the number of assertions expected to run within each test

strictEqual( actual, expected, message) – offers a much stricter comparison than equal() and is considered the preferred method of checking equality as it avoids stumbling on subtle coercion bugs

deepEqual( actual, expected, message ) – similar to strictEqual, comparing the contents (with ===) of the given objects, arrays and primitives.

Creating new test cases with QUnit is relatively straight-forward and can be done using test(), which constructs a test where the first argument is the name of the test to be displayed in our results and the second is a callback function containing all of our assertions. This is called as soon as QUnit is running.

What we’re doing in the above is defining a variable with a specific value and then testing to ensure the value was what we expected it to be. This was done using the comparison assertion, equal(), which expects its first argument to be a value being tested and the second argument to be the expected value. We also used ok(), which allows us to easily test against functions or variables that evaluate to booleans.

Note: Optionally in our test case, we could have passed an ‘expected’ value to test() defining the number of assertions we expect to run. This takes the form: test( name, [expected], test ); or by manually settings the expectation at the top of the test function, like so: expect( 1 ). I recommend you to make it a habit and always define how many assertions you expect. More on this later.

As testing a simple static variable is fairly trivial, we can take this further to test actual functions. In the following example we test the output of a function that reverses a string to ensure that the output is correct using equal() and notEqual():

Comparing the actual output of a function against the expected output:

Running these tests in the QUnit test runner (which you would see when your HTML test page was loaded) we would find that four of the assertions pass whilst the last one does not. The reason the test against "double" fails is because it was purposefully written incorrectly. In your own projects if a test fails to pass and your assertions are correct, you’ve probably just found a bug!

Adding structure to assertions

Housing all of our assertions in one test case can quickly become difficult to maintain, but luckily QUnit supports structuring blocks of assertions more cleanly. This can be done using module() – a method that allows us to easily group tests together. A typical approach to grouping might be keeping multiple tests testing a specific method as part of the same group (module).

Using setup() and teardown() :

These callbacks can be used to define (or clear) any components we wish to instantiate for use in one or more of our tests. As we’ll see shortly, this is ideal for defining new instances of views, collections, models or routers from a project that we can then reference across multiple tests.

raises – an assertion which tests if a callback throws any exceptions

Fixtures

From time to time we may need to write tests that modify the DOM. Managing the clean-up of such operations between tests can be a genuine pain, but thankfully QUnit has a solution to this problem in the form of the #qunit-fixture element, seen below.

We can either opt to place static markup in the fixture or just insert/append any DOM elements we may need to it. QUnit will automatically reset the innerHTML of the fixture after each test to its original value. In case you’re using jQuery, it’s useful to know that QUnit checks for its availability and will opt to use $(el).html() instead, which will cleanup any jQuery event handlers too.

Fixtures example:

Let us now go through a more complete example of using fixtures. One thing that most of us are used to doing in jQuery is working with lists – they’re often used to define the markup for menus, grids and a number of other components. You may have used jQuery plugins before that manipulated a given list in a particular way and it can be useful to test that the final (manipulated) output of the plugin is what was expected.

For the purposes of our next example, we’re going to use Ben Alman’s $.enumerate() plugin, which can prepend each item in a list by its index, optionally allowing us to set what the first number in the list is. The code snippet for the plugin can be found below, followed by an example of the output is generates:

Next, we need to think about what should be tested. $.enumerate() supports a few different use cases, including:

No arguments passed – i.e $(el).enumerate()

0 passed as an argument – i.e $(el).enumerate(0)

1 passed as an argument – i.e $(el).enumerate(1)

As the text value for each list item is of the form “n. item-text” and we only require this to test against the expected output, we can simply access the content using $(el).eq(index).text() (for more information on .eq() see here).

Asynchronous code

As with Jasmine, the effort required to run synchronous tests with QUnit is fairly straight-forward. That said, what about tests that require asynchronous callbacks (such as expensive processes, Ajax requests and so on)? When we’re dealing with asynchronous code, rather than letting QUnit control when the next test runs, we can inform that we need it to stop running and wait until it’s okay to continue once again.

Remember: running asynchronous code without any special considerations can cause incorrect assertions to appear in other tests, so we want to make sure we get it right.

Writing QUnit tests for asynchronous code is made possible using the start() and `stop() methods, which programmatically set the start and stop points during such tests. Here’s a simple example:

A jQuery $.ajax() request is used to connect to a test resource and assert that the data returned is correct. deepEqual() is used here as it allows us to compare different data types (e.g objects, arrays) and ensures that what is returned is exactly what we’re expecting. We know that our Ajax request is asynchronous and so we first call stop(), run the code making the request and finally at the very end of our callback, inform QUnit that it is okay to continue running other tests.

Note: rather than including stop(), we can simply exclude it and substitute test() with asyncTest() if we prefer. This improves readability when dealing with a mixture of asynchronous and synchronous tests in your suite. Whilst this setup should work fine for many use-cases, there is no guarantee that the callback in our $.ajax() request will actually get called. To factor this into our tests, we can use expect() once again to define how many assertions we expect to see within our test. This is a healthy safety blanket as it ensures that if a test completes with an insufficient number of assertions, we know something went wrong and fix it.

SinonJS

Similar to the section on testing Backbone.js apps using the Jasmine BDD framework, we’re nearly ready to take what we’ve learned and write a number of QUnit tests for our Todo application.

Before we start though, you may have noticed that QUnit doesn’t support test spies. Test spies are functions which record arguments, exceptions and return values for any of their calls. They’re typically used to test callbacks and how functions may be used in the application being tested. In testing frameworks, spies can usually be either anonymous functions or wrap functions which already exist.

What is SinonJS?

In order for us to substitute support for spies in QUnit, we will be taking advantage of a mocking framework called SinonJS by Christian Johansen. We will also be using the SinonJS-QUnit adapter which provides seamless integration with QUnit (meaning setup is minimal). Sinon.JS is completely test-framework agnostic and should be easy to use with any testing framework, so it’s ideal for our needs.

The framework supports three features we’ll be taking advantage of for unit testing our application:

Anonymous spies

Spying on existing methods

A rich inspection interface

Using this.spy() without any arguments creates an anonymous spy. This is comparable to jasmine.createSpy() and we can observe basic usage of a SinonJS spy in the following example:

We can also use this.spy() to spy on existing functions (like jQuery’s $.ajax) in the example below. When spying on a function which already exists, the function behaves normally but we get access to data about its calls which can be very useful for testing purposes.

SinonJS comes with a rich spy interface which callows us to test whether a spy was called with a specific argument, if it was called a specific number of times and test against the values of arguments. A complete list of features supported in the interface can be found here (http://sinonjs.org/docs/), but let’s take a look at some examples demonstrating some of the most commonly used ones:

Matching arguments: test a spy was called with a specific set of arguments:

Stubs and mocks

SinonJS also supports two other powerful features which are useful to be aware of: stubs and mocks. Both stubs and mocks implement all of the features of the spy API, but have some added functionality.

Stubs

A stub allows us to replace any existing behaviour for a specific method with something else. They can be very useful for simulating exceptions and are most often used to write test cases when certain dependencies of your code-base may not yet be written.

Let us briefly re-explore our Backbone Todo application, which contained a Todo model and a TodoList collection. For the purpose of this walkthrough, we want to isolate our TodoList collection and fake the Todo model to test how adding new models might behave.

We can pretend that the models have yet to be written just to demonstrate how stubbing might be carried out. A shell collection just containing a reference to the model to be used might look like this:

Assuming our collection is instantiating new models itself, it’s necessary for us to stub the models constructor function for the the test. This can be done by creating a simple stub as follows:

this.todoStub = sinon.stub( window, "Todo" );

The above creates a stub of the Todo method on the window object. When stubbing a persistent object, it’s necessary to restore it to its original state. This can be done in a teardown() as follows:

this.todoStub.restore();

After this, we need to alter what the constructor returns, which can be efficiently done using a plain Backbone.Model constructor. Whilst this isn’t a Todo model, it does still provide us an actual Backbone model.

The expectation here might be that this snippet would ensure our TodoList collection always instantiates a stubbed Todo model, but because a reference to the model in the collection is already present, we need to reset the model property of our collection as follows:

this.todoList.model = Todo;

The result of this is that when our TodoList collection instantiates new Todo models, it will return our plain Backbone model instance as desired. This allows us to write a spec for testing the addition of new model literals as follows:

Mocks

Mocks are effectively the same as stubs, however they mock a complete API out and have some built-in expectations for how they should be used. The difference between a mock and a spy is that as the expectations for their use are pre-defined, it will fail if any of these are not met.

Here’s a snippet with sample usage of a mock based on PubSubJS. Here, we have a clearTodo() method as a callback and use mocks to verify its behavior.

Practical

We can now begin writing test specs for our Todo application, which are listed and separated by component (e.g Models, Collections, Views, Routers). It’s useful to pay attention to the name of the test, the logic being tested and most importantly the assertions being made as this will give you some insight into how what we’ve learned can be applied to a complete application.

In case you haven't had a chance to try out one of the Koans kits as yet, they are a set of unit tests using a specific testing framework that both demonstrate how a set of specs for an application may be written, but also leave some tests unfilled so that you can complete them as an exercise.

App

It can also be useful to write specs for any application bootstrap you may have in place. For the following module, our setup initiates and appends a TodoApp view and we can test anything from local instances of views being correctly defined to application interactions correctly resulting in changes to instances of local collections.

Further Reading & Resources

That’s it for this section on testing applications with QUnit and SinonJS. I encourage you to try out the QUnit Backbone.js Koans and see if you can extend some of the examples. For further reading consider looking at some of the additional resources below:

I hope this post was useful. I would like to thank the very clever Andrée Hansson and Sindre Sorhus for their technical reviews. Any corrections or feedback on the post is always welcome as I would like to ensure it's as accurate as possible. Thanks!

how do you setup a test framework when so much of a page’s JS is fairly light on logic, and heavy on interaction with the DOM?

forgive me, i’ve definitely written applications where a majority of what was needed was computational output, in such case it’s easy for me to see the case for qunit (which i used extensively ).

i try to decouple as much as possible the handling of data and any reliance on the DOM, but in the end, my typical JS usage is for gathering user input, sending that data to the server, receiving data from the sever, and then updating DOM elements to reflect some of those changes, often with animation and several layers of callback chains.

to be clear, i’m asking for an answer to describe testing when your JS’s main responsibility is managing user input, and managing UI’s..

My answer to this is probably not going to be as advanced or as complex as you were hoping for I’ve never really run into issues with DOM interaction testing as long as you’re using fragments correctly. This holds true with both QUnit and Jasmine. Even if a large portion of your app is dynamically generating content to be injected in the DOM, you can still use fragments to append that content, ensure it’s what you were expecting etc. From the sounds of what you’re doing, your typical setup would probably be an asynchronous test that 1) simulates the user input/behaviour, 2) sends the data to and from the server, 3) updated your fragments based on the data returned (and there are a number of things that can be tested across those three points). Even if you’re animating content beyond that, you can easily continue using async tests (and even go further with timers and deferreds) to ensure that all of your points of expected behaviour are being executed at the right time and in the right order. I don’t believe there’s much more to it than that.

Thanks for letting me know. This type of thing used to bother me, but at the end of the day as long as it means more people are going to get to see the content and learn from it I’ll probably just let it slide. I appreciate the heads up either way!