How to Test your JavaScript Code with QUnit

How to Test your JavaScript Code with QUnit

Tutorial Details
  • Topic: JavaScript
  • Difficulty: Intermediate
  • Estimated Completion Time: 30 minutes
  • Required File: QUnit JS

QUnit, developed by the jQuery team, is a great framework for unit testing your JavaScript. In this tutorial, I’ll introduce what QUnit specifically is, and why you should care about rigorously testing your code.

What is QUnit

QUnit is a powerful JavaScript unit testing framework that helps you to debug code. It’s written by members of the jQuery team, and is the official test suite for jQuery. But QUnit is general enough to test any regular JavaScript code, and it’s even able to test server-side JavaScript via some JavaScript engine like Rhino or V8.

If you’re unfamiliar with the idea of “unit testing”, don’t worry. It’s not too difficult to understand:

In computer programming, unit testing is a software verification and validation method in which a programmer tests if individual units of source code are fit for use. A unit is the smallest testable part of an application. In procedural programming a unit may be an individual function or procedure.

This is quoted from Wikipedia. Simply put, you write tests for each functionality of your code, and if all of these tests are passed, you can be sure that the code will be bug-free (mostly, depends on how thorough your tests are).

Why You Should Test Your Code

If you haven’t written any unit tests before, you probably just apply your code to a website directly, click for a while to see if any problem occurs, and try to fix it as you spot one. There are many problems with this method.

First, it’s very tedious. Clicking actually is not an easy job, because you have to make sure everything is clicked and it’s very likely you’ll miss one thing or two. Second, everything you did for testing is not reusable, which means it’s not easy to find regressions. What is a regression? Imagine that you wrote some code and tested it, fixed all the bugs you found, and published it. Then, a user sends some feedback about new bugs, and requests some new features. You go back to the code, fix these new bugs and add these new features. What might happen next is that some of the old bugs come up again, which are called “regressions.” See, now you have to do the clicking again, and chances are you won’t find these old bugs again; even if you do, it’ll take a while before you figure out that the problem is caused by regressions. With unit testing, you write tests to find bugs, and once the code is modified, you filter it through the tests again. If a regression appears, some tests will definitely be failed, and you can easily spot them, knowing which part of the code contains the bug. Since you know what you have just modified, it can easily be fixed.

Another advantage of unit testing is especially for web development: it eases the testing of cross-browser compatibility. Just run your tests on different browsers, and if a problem occurs on one browser, you fix it and run these tests again, making sure it doesn’t introduce regression on other browsers. You can be sure that all of the target browsers are supported, once they all pass the tests.

I’d like to mention one of John Resig’s projects: TestSwarm. It takes JavaScript unit testing to a new level, by making it distributed. It’s a website that contains many tests, anyone can go there, run some of the tests, then return the result back to server. In this way, code can be tested on different browsers and even different platforms really quickly.

How to Write Unit Tests with QUnit

So how do you write unit tests with QUnit exactly? First, you need to set up a testing environment:

<!DOCTYPE html>
<html>
<head>
	<title>QUnit Test Suite</title>
	<link rel="stylesheet" href="http://github.com/jquery/qunit/raw/master/qunit/qunit.css" type="text/css" media="screen">
	<script type="text/javascript" src="http://github.com/jquery/qunit/raw/master/qunit/qunit.js"></script>
	<!-- Your project file goes here -->
	<script type="text/javascript" src="myProject.js"></script>
	<!-- Your tests file goes here -->
	<script type="text/javascript" src="myTests.js"></script>
</head>
<body>
	<h1 id="qunit-header">QUnit Test Suite</h1>
	<h2 id="qunit-banner"></h2>
	<div id="qunit-testrunner-toolbar"></div>
	<h2 id="qunit-userAgent"></h2>
	<ol id="qunit-tests"></ol>
</body>
</html>

As you can see, a hosted version of QUnit framework is used here.

The code that is going to be tested should be put into myProject.js, and your tests should be inserted into myTests.js. To run these tests, simply open this HTML file in a browser. Now it’s time to write some tests.

The building blocks of unit tests are assertions.

An assertion is a statement that predicts the returning result of your code. If the prediction is false, the assertion has failed, and you know that something has gone wrong.

To run assertions, you should put them into a test case:

// Let's test this function
function isEven(val) {
	return val % 2 === 0;
}

test('isEven()', function() {
	ok(isEven(0), 'Zero is an even number');
	ok(isEven(2), 'So is two');
	ok(isEven(-4), 'So is negative four');
	ok(!isEven(1), 'One is not an even number');
	ok(!isEven(-7), 'Neither is negative seven');
})

Here we defined a function, isEven, which detects whether a number is even, and we want to test this function to make sure it doesn’t return wrong answers.

We first call test(), which constructs a test case; the first parameter is a string that will be displayed in the result, and the second parameter is a callback function that contains our assertions. This callback function will get called once QUnit is run.

We wrote five assertions, all of which are boolean. A boolean assertion expects its first parameter to be true. The second parameter is also a message that will be displayed in the result.

Here is what you get, once you run the test:

a test for isEven()

Since all these assertions have successfully passed, we can be pretty sure that isEven() will work as expected.

Let’s see what happens if an assertion has failed.

// Let's test this function
function isEven(val) {
	return val % 2 === 0;
}

test('isEven()', function() {
	ok(isEven(0), 'Zero is an even number');
	ok(isEven(2), 'So is two');
	ok(isEven(-4), 'So is negative four');
	ok(!isEven(1), 'One is not an even number');
	ok(!isEven(-7), 'Neither does negative seven');

	// Fails
	ok(isEven(3), 'Three is an even number');
})

Here is the result:

a test contains failed assertion for isEven()

The assertion has failed because we deliberately wrote it wrong, but in your own project, if the test doesn’t pass, and all assertion are correct, you know a bug has been found.

More Assertions

ok() is not the only assertion that QUnit provides. There are other kinds of assertions that are useful when testing your project:

Comparison Assertion

The comparison assertion, equals(), expects its first parameter (which is the actual value) is equal to its second parameter (which is the expected value). It’s similar to ok(), but outputs both actual and expected values, making debugging much easier. Like ok(), it takes an optional third parameter as a message to be displayed.

So instead of:

test('assertions', function() {
	ok( 1 == 1, 'one equals one');
})
a boolean assertion

You should write:

test('assertions', function() {
	equals( 1, 1, 'one equals one');
})
a comparison assertion

Notice the last “1″, which is the comparing value.

And if the values are not equal:

test('assertions', function() {
	equals( 2, 1, 'one equals one');
})
a failed comparison assertion

It gives a lot more information, making life a lot easier.

The comparison assertion uses “==” to compare its parameters, so it doesn’t handle array or object comparison:

test('test', function() {
	equals( {}, {}, 'fails, these are different objects');
	equals( {a: 1}, {a: 1} , 'fails');
	equals( [], [], 'fails, there are different arrays');
	equals( [1], [1], 'fails');
})

In order to test this kind of equality, QUnit provides another kind assertion: identical assertion.

Identical Assertion

Identical assertion, same(), expects the same parameters as equals(), but it’s a deep recursive comparison assertion that works not only on primitive types, but also arrays and objects. Assertions, in the previous example, will all pass if you change them to identical assertions:

test('test', function() {
	same( {}, {}, 'passes, objects have the same content');
	same( {a: 1}, {a: 1} , 'passes');
	same( [], [], 'passes, arrays have the same content');
	same( [1], [1], 'passes');
})

Notice that same() uses ‘===’ to do comparison when possible, so it’ll come in handy when comparing special values:

test('test', function() {
	equals( 0, false, 'true');
	same( 0, false, 'false');
	equals( null, undefined, 'true');
	same( null, undefined, 'false');
})

Structure Your Assertions

Putting all assertions in a single test case is a really bad idea, because it’s very hard to maintain, and doesn’t return a clean result. What you should do is to structure them, put them into different test cases, each aiming for a single functionality.

You can even organize test cases into different modules by calling the module function:

module('Module A');
test('a test', function() {});
test('an another test', function() {});

module('Module B');
test('a test', function() {});
test('an another test', function() {});
structure assertions

Asynchronous Test

In previous examples, all assertions are called synchronously, which means they run one after another. In the real world, there are also many asynchronous functions, such as ajax calls or functions called by setTimeout() and setInterval(). How can we test these kinds of functions? QUnit provides a special kind of test case called “asynchronous test,” which is dedicated to asynchronous testing:

Let’s first try to write it in a regular way:

test('asynchronous test', function() {
	setTimeout(function() {
		ok(true);
	}, 100)
})
an incorrent example of asychronous test

See? It’s as if we didn’t write any assertion. This is because the assertion ran asynchronously, by the time it got called, the test case had already finished.

Here is the correct version:

test('asynchronous test', function() {
	// Pause the test first
	stop();

	setTimeout(function() {
		ok(true);

		// After the assertion has been called,
		// continue the test
		start();
	}, 100)
})
a correct example of asychronous test

Here, we use stop() to pause the test case, and after the assertion has been called, we use start() to continue.

Calling stop() immediately after calling test() is quite common; so QUnit provides a shortcut: asyncTest(). You can rewrite the previous example like this:

asyncTest('asynchronous test', function() {
	// The test is automatically paused

	setTimeout(function() {
		ok(true);

		// After the assertion has been called,
		// continue the test
		start();
	}, 100)
})

There is one thing to watch out for: setTimeout() will always call its callback function, but what if it’s a custom function (e.g, an ajax call). How can you be sure the callback function will be called? And if the callback is not called, start() won’t be called, and the whole unit testing will hang:

unit testing hangs

So here is what you do:

// A custom function
function ajax(successCallback) {
	$.ajax({
		url: 'server.php',
		success: successCallback
	});
}

test('asynchronous test', function() {
	// Pause the test, and fail it if start() isn't called after one second
	stop(1000);

	ajax(function() {
		// ...asynchronous assertions

		start();
	})
})

You pass a timeout to stop(), which tells QUnit, “if start() isn’t called after that timeout, you should fail this test.” You can be sure that the whole testing won’t hang and you’ll be notified if something goes wrong.

How about multiple asynchronous functions? Where do you put the start()? You put it in setTimeout():

// A custom function
function ajax(successCallback) {
	$.ajax({
		url: 'server.php',
		success: successCallback
	});
}

test('asynchronous test', function() {
	// Pause the test
	stop();

	ajax(function() {
		// ...asynchronous assertions
	})

	ajax(function() {
		// ...asynchronous assertions
	})

	setTimeout(function() {
		start();
	}, 2000);
})

The timeout should be reasonably long enough to allow both callbacks to be called before the test continues. But what if one of the callback isn’t called? How can you know that? This is where expect() comes in:

// A custom function
function ajax(successCallback) {
	$.ajax({
		url: 'server.php',
		success: successCallback
	});
}

test('asynchronous test', function() {
	// Pause the test
	stop();

	// Tell QUnit that you expect three assertions to run
	expect(3);

	ajax(function() {
		ok(true);
	})

	ajax(function() {
		ok(true);
		ok(true);
	})

	setTimeout(function() {
		start();
	}, 2000);
})

You pass in a number to expect() to tell QUnit that you expect X many assertions to run, if one of the assertion isn’t called, the number won’t match, and you’ll be notified that something went wrong.

There is also a shortcut for expect(): you just pass the number as the second parameter to test() or asyncTest():

// A custom function
function ajax(successCallback) {
	$.ajax({
		url: 'server.php',
		success: successCallback
	});
}

// Tell QUnit that you expect three assertion to run
test('asynchronous test', 3, function() {
	// Pause the test
	stop();

	ajax(function() {
		ok(true);
	})

	ajax(function() {
		ok(true);
		ok(true);
	})

	setTimeout(function() {
		start();
	}, 2000);
})

Conclusion

That’s all you need to know to get started witih QUnit. Unit testing is a great method to test your code before publishing it. If you haven’t written any unit tests before, it’s time to get started! Thanks for reading!

Add Comment

Discussion 63 Comments

Comment Page 1 of 21 2
  1. Elijah Manor says:

    Great post on Unit Testing jQuery. Another nice library to use in conjunction w/ QUnit is QMock which allows you to mock your jQuery or JavaScript code.

    That way you could stub out your methods and test the interaction with how many times you expect a method to be called. You can tell the mocking framework to return certain values for your methods and then unit test their interaction as well.

    http://github.com/ashb/qmock I’d look at the juice branch since it has a lot of new features…

    • Thanks for the plug Elijah :)

      Yep, just to confirm what Elijah mentioned, QMock has been re-written and I’m hoping to release it once some decent docs have been written, hopefully in next month or so.

      I’d also recommend checking out JSpec if you prefer a Behaviour-Driven testing style (with mocks and much more): http://github.com/visionmedia/jspec

  2. Way says:

    QUnit is as good for fast and exhaustive javascript testing as this tutorial about QUnit ;)

  3. Sean Foushee says:

    Great tutorial, thanks for the introduction; and a special thanks to the jQuery team!

  4. Mithun says:

    Yes I agree to:

    “Great tutorial, thanks for the introduction; and a special thanks to the jQuery team!”

  5. Crysfel says:

    we should test our code always!! thanks for this tutorial!

  6. insic says:

    this is worth to try with. nice tut!

  7. Joe says:

    Can you do some more of these. I am having a hard time wrapping my head around Unit Testing.

  8. olivier says:

    I used QUnit in the past and was very satisfied with it. However, I found JsTestDriver and found that QUnit is only part of the solution of testing your JS code. JsTestDriver allows you to implement continuous integration, code coverage, test reports …

  9. Ashit Vora says:

    Would like to learn more about Test Framework (Js, PHP)

  10. Niklas says:

    Thanks for this! Very useful indeed.

  11. I ‘m waiting a tools like this! tnx!

  12. Luke says:

    ‘Died on test #1: cannot access optimized closure’

    been searching all over the net with no successful explanation. Anyone got an idea why is that occurring?

  13. Always nice to be able to test your innovative solutions.

  14. Mike Krisher says:

    if you are using Rails, you may want to check out my plugin for adding QUnit testing:

    http://github.com/mkrisher/qunit_for_rails

  15. itw says:

    Thanks for this, it looks great!

    But most of my Javascript methods usually manipulate the DOM.
    Is it possible to test that?

  16. Really good examples. There are a bunch of unit testing suites available for JavaScript but since I’m a jQuery fan-boy, I use QUnit :D. If anyone is wanting to know more about unit testing for PHP I would check out PHPUnit.

  17. Great tutorial, thanks for the introduction

  18. Bankacı says:

    nice tut ! that is great :)

  19. Berkay UNAL says:

    Nice tut. Unit testing is always needed. For the AS developers here is the one http://asunit.org/

  20. labilbe says:

    Very good tutorial, thank you for your time!

  21. Barry McGee says:

    Nice post, must look into this..

    thanks!

  22. Vizor says:

    Thanks for tut, very interesting!

  23. W.J. says:

    Nice post! Thanks a lot!

  24. this is great! Thanks you very much!

  25. I’d much rather see asynchronous tests run serially, with no “guessing” as to when they will complete, like I do in the jQuery longUrl unit tests:

    http://github.com/cowboy/jquery-longurl/blob/master/unit/unit.js
    http://benalman.com/projects/jquery-longurl-plugin/

    Here’s some example code:

    test( ‘async tests’, function() {

    // My custom async function
    function ajax(data, successCallback) {
    $.ajax({
    url: ‘server.php’,
    data: data,
    success: successCallback
    });
    };

    // Run next test in tests array, or start().
    function next(){
    var test = tests.shift();
    test ? test() : start();
    };

    // Tests to run, in order.
    var tests = [
    function(){
    ajax({ foo: 1 }, function(){
    ok(true);
    next();
    });
    },
    function(){
    ajax({ bar: 2 }, function(){
    ok(true);
    next();
    });
    },
    function(){
    ajax({ baz: 3 }, function(){
    ok(true);
    next();
    });
    },
    ];

    stop();
    next();
    });

  26. Here’s a gist of the previous code, without the weird comment formatting issues:

    http://gist.github.com/299286

  27. Golf Royalty says:

    great tut. bookmarked for suture reference.

    Cheers,

    Lee.

  28. bractus says:

    Looks like completes jQuery’s unit testing gap.
    This is create.

    Thanks

  29. SM says:

    Simple and useful. Thanks

  30. idasil says:

    thanks very nice works

  31. Napolux says:

    This tutorial is exactly what I was searching for. Thank You!

  32. 123doing says:

    It’s very good.
    I like this.
    Thanks for share.
    And I wrote something to introduce this project for my readers.
    You can find the post about this in my website.
    If something is wrong,pls figure it out.thanks.

  33. rawego says:

    like someone already mentioned… how do you test the javascript manipulation of the dom, the dom which only exists on your application, say, aspx page, and is generated from code behind dynamically???

    this “assert(function: add(2+2),4)” stuff just ain’t gonna cut it in the real world :))

    thank you!

  34. Jay says:

    “how do you test the javascript manipulation of the dom, the dom which only exists on your application, say, aspx page, and is generated from code behind dynamically???”

    If your code can not be unit tested then there is something wrong with your code. Period. Now, generated JavaScript code would not even be excepted into one of my projects. You can do better than that.

    • K Boucher says:

      ” “how do you test the javascript manipulation of the dom, the dom which only exists on your application, say, aspx page, and is generated from code behind dynamically???”

      If your code can not be unit tested then there is something wrong with your code. Period. Now, generated JavaScript code would not even be excepted into one of my projects. You can do better than that. ”

      How is this an answer to the question? I believe you mean “.. would not even be accepted …”, and while I avoid this practice whenever possible (mostly ‘cuz I like to avoid the middle-tier) it is quite common.

      I’ll chime in with the chorus, how do you use this to test DOM manipulation?

  35. Ori says:

    Don’t know what kind of projects you develop, but huge dynamically created projects are using a lot of Javascript DOM manipulations (and not dynamic generated Javascript :) ). I’m with rawego, this won’t cut it in the real world.

  36. Alexander Trefz says:

    This is a really nice round up. Thank you.

  37. Alex York says:

    @Jay: I think you were too quick to criticise @rawego there. Sure, if you are generating dynamic JavaScript on the server-side then that is always going to be tricky (impossible?) to unit test, but the point remains that he was asking how to use QUnit to test manipulation of the DOM and you didn’t really help anyone with your answer.

    Can anyone provide an example, snippet or tutorial for using QUnit to test DOM manipulation?

    I agree somewhat with the statement that “this ain’t gonna cut it in the real world” – let’s face it, JavaScript manipulating the DOM is pretty standard. What would be nice is that if you had a function like this:

    function add(a, b) {
    var result = a + b;
    $(“input#ResultTestBox”).val(result);

    In the above test, I would love to test two things: the addition of a and b, and the result correctly being put into a DOM element. I would love to test the second thing by providing some mock HTML. Possible?

    • Jason Rice says:

      Basically how the jQuery team does their tests with qUnit is to have a ‘mock page’, which contains the elements you need to test in it.

      http://view.jquery.com/trunk/jquery/test/unit/core.js

      For dynamically generated pages, you already know the basic elements you need since you’ve written the JS. You could have part of your test for the server code output that mock page and load it into your testRunner to test the JS side.

    • Conrad Damon says:

      You can load your app so that the DOM is populated, and then run your tests with assertions about the state of the DOM. You can use teardown() to return the DOM to a known state before the next test or module.

  38. krishna says:

    Thank u very much it is very helpful for starters.

  39. Paul Evans says:

    Very good, very clear post. Thank you very much!

  40. Nick says:

    Thanks for the post.
    Really cool but not clear with DOM-elements testing.
    I think it’s really important.

  41. Deerawan says:

    Nice tutorial for QUnit. I have just considered to use it for my next project. Bravo!

  42. Ryan Sharp says:

    In software engineering, a “regression” isn’t a reference to old bugs coming back – it’s a reference to a regression in quality.

    It’s very clear that you’re not much of a developer and should probably avoid spreading misinformation and re-publishing basic summaries that are available on Wikipedia and the jQuery docs.

  43. Jo says:

    Thanks Gavin

    @Ryan – old bugs coming back is a regression in quality. How about making a positive contribution next time and not a comment regression.

  44. I went over this internet site and I conceive you have a lot of fantastic information, saved to fav (:.

  45. I am used to firebug for testing my JavaScript. Now I have another option with my other browswers.

    Thank you very much

  46. Hi,

    Tuts looks gr8 , but dont know how to test DOM elements,Can anyone provide samples.

    I am using JQuery UI……………..

  47. Chris says:

    Thanks for this tutorial.

  48. Brooky says:

    Very clear tutorial, thanks

  49. dhimes says:

    I know I’m late here, but I’ve had success with the AJAX testing by putting the calls to “start()” in the success and fail callbacks. This makes for a simpler test IMHO.

    module(“ajax test setup”);
    asyncTest(‘POST[\'test\']‘,function(){
    jQuery.ajax({
    type: “POST”,
    url: “app.php”,
    data: “test=1″,
    success: function(){
    start();
    ok(true,”AJAX call successful”);
    },
    error: function(){
    start();
    ok(false,’AJAX call failed’);
    }
    });

    });

    • dhimes says:

      Hmmm. The -pre- markup didn’t work above. I’ll try again, but if it fails this time I’m leaving it (since I don’t seem to be able to edit):

      module(“ajax test setup”);
      asyncTest(‘POST[\'test\']‘,function(){
      jQuery.ajax({
      type: “POST”,
      url: “app.php”,
      data: “test=1″,
      success: function(){
      start();
      ok(true,”AJAX call successful”);
      },
      error: function(){
      start();
      ok(false,’AJAX call failed’);
      }
      });

      });

Comment Page 1 of 21 2

Add a Comment

To add a code snippet to your comment, please wrap your code like so: <pre name="code" class="html">YOUR CODE</pre>. You can replace the class name with "js," "css," "sql," or "php." If there are any "<" or ">" within your code, please search and replace them with: &lt; and &gt; respectively.