Getting Started with QUnit

Software testing is the process of evaluating a piece of software to detect differences between expected and actual outputs for a given set of inputs. Testing, and unit-testing in particular, should be an essential part of every developer’s life. Unfortunately, many developers seem to be scared of this activity.

In JavaScript there are a lot of frameworks we can choose from to test our code base. Some examples are Mocha, Selenium, and QUnit. In this article, I’ll introduce you to QUnit. QUnit is the unit-testing framework developed and maintained by the jQuery team, which is the same team behind projects like jQuery and jQuery UI.

Setting up QUnit

One of the main reasons why a lot of developers use QUnit is its ease of use. Starting off with this framework is very simple, and the main concepts can be grasped in a few hours.

The obvious first step to perform in order to employ QUnit is to download it. There are several ways to do that: downloading it manually from the website, using a CDN, using Bower, or using npm. My suggestion is that unless you’re developing a simple live demo, you should not rely on the CDN to test your code. So, stick with the other options.

For this article I don’t want to set any prerequisite (read Bower and npm), so we’ll employ the first method. Therefore, go to the QUnit website and download the latest version of both the JavaScript file (named qunit-1.14.0.js) and the CSS file (named qunit-1.14.0.css).

Place them into a folder where you’ll also create an index.html. In this file we’ll place the HTML code shown in the homepage of the website that I’m repeating below for your commodity.

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <title>QUnit Example</title>
      <link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.14.0.css">
   </head>
   <body>
      <div id="qunit"></div>
      <div id="qunit-fixture"></div>
      <script src="//code.jquery.com/qunit/qunit-1.14.0.js"></script>
      <script src="tests.js"></script>
   </body>
</html>

As you can see, this code uses the CDN to include the CSS and JavaScript files. So, you have to update the links to include the files you have previously downloaded.

In the markup you can see that there are a couple of <div>s in place. The first, having qunit as its ID, is used by the framework to show its user interface where the results of the tests are displayed. The second <div>, whose ID is qunit-fixture, should be used by you, the developer. This element allows a developer to test code that adds, edits, or removes elements from the DOM without having to worry about cleaning up the DOM after each test. If you put the elements created by the code inside this <div>, QUnit will take care of the reset for us.

Finally, we have the inclusion of a tests.js file that represents the file containing the tests. My advice is to use a file to store your tests when working on a real project. In the live demos I’ve created for this tutorial I’ve used JSBin, which of course doesn’t allow file uploads. Therefore, in the demos you’ll see that I’ve inlined the code of the tests.

Now that you know the meaning of each part of the markup, open up the index.html page in your browser and see what happens.

If everything went well, you should see the interface as shown by the live demo below, which is also available as a JSBin:

QUnit Example

At this stage, the only relevant part of this interface for us is the section that shows the time QUnit has spent in processing the tests, the number of assertions defined, and the number of tests that passed and failed. The demo above shows that we haven’t defined any test. Let’s fix that.

How to Create a Test with QUnit

QUnit offers two methods to create a new test: QUnit.test() and QUnit.asyncTest(). The first is used to test code that run synchronously, while the latter is used to test asynchronous code. In this section, I’ll describe how to create tests for synchronous code.

The signature of the QUnit.test() method is:

QUnit.test(name, testFunction)

The first parameter, name, is a string that helps us identify the test created. The second parameter, testFunction, is the function containing the assertions that the framework will execute. The framework passes to this function an argument that exposes all of QUnit’s assertion methods.

Putting this description into code, we can update the file tests.js with the following code:

QUnit.test('My first test', function(assert) {
   // Assertions here...
});

This code creates a new test identified by the string “My first test” and a function with an empty body. Adding a test without any assertion isn’t of any utility. To fix this issue, we have to learn the assertion methods available in QUnit.

The Assertion Methods of QUnit

Assertions are the core of software testing. They are the piece that enables us to verify that our code is working as expected. In QUnit we have a bunch of methods to verify these expectations. They can be accessed within a test through the parameter passed to the function of the QUnit.test() method (assert in our previous example).

The list below summarizes the methods available, along with their signature and purpose:

  • deepEqual(value, expected[, message]): A recursive, strict comparison that works on all the JavaScript types. The assertion passes if value and expected are identical in terms of properties, values, and they have the same prototype;
  • equal(value, expected[, message]): Verify the value provided is equal the expected parameter using a non-strict comparison (==).
  • notDeepEqual(value, expected[, message]): Same as deepEqual() but tests for inequality;
  • notEqual(value, expected[, message]): Same as equal() but tests for inequality;
  • propEqual(value, expected[, message]): A strict comparison of the properties and values of an object. The assertion passes if all the properties and the values are identical;
  • strictEqual(value, expected[, message]): Verify the value provided is equal to the expected parameter using a strict comparison (===);
  • notPropEqual(value, expected[, message]): Same as propEqual() but tests for inequality;
  • notStrictEqual(value, expected[, message]): Same as strictEqual() but tests for inequality;
  • ok(value[, message]: An assertion that passes if the first argument is truthy;
  • throws(function [, expected ] [, message ]): Test if a callback throws an exception, and optionally compare the thrown error;

The meaning of the parameters accepted by these methods is described below:

  • value: The value returned by a function, a method, or stored in a variable that has to be verified;
  • expected: The value to test against. In case of the throws() method, this can be an Error Object (instance), Error Function (constructor), a RegExp that matches (or partially matches) the String representation, or a callback Function that must return true to pass the assertion check;
  • message: An optional string describing the assertion;
  • function: The function to execute that should return an Error;

Now that you know the methods available and the parameters, it’s time to see some code. Instead of writing several tests for a single function, I’ll try to reproduce a more realistic example. By any means the tests I’ll show you should be considered a complete test suite, but they should give you a concrete idea of where to start.

In order to write the mentioned tests, we need to define some code to test. In this case I’m going to define an object literal as follows:

var App = {
   max: function() {
      var max = -Infinity;
      for (var i = 0; i < arguments.length; i++) {
         if (arguments[i] > max) {
            max = arguments[i];
         }
      }

      return max;
   },
   isOdd: function(number) {
      return number % 2 !== 0;
   },
   sortObj: function(array) {
      array.sort(function(a, b) {
         var date1 = new Date(a.timestamp).getTime();
         var date2 = new Date(b.timestamp).getTime();

         if (date1 < date2) {
            return -1;
         } else if (date1 === date2) {
            return 0;
         } else {
            return 1;
         }
      });
   }
};

As you can see, we’ve defined an object literal containing three functions: max(), isOdd(), and sortObj(). The first takes an arbitrary number of arguments and returns the maximum. isOdd() accepts a number as its argument and tests if it’s odd. sortObj() accepts an array of objects, that ideally should have a property called timestamp, and sorts them based on the value of this property.

A possible set of tests for these functions is shown below:

QUnit.test('max', function (assert) {
    assert.strictEqual(App.max(), -Infinity, 'No parameters');
    assert.strictEqual(App.max(3, 1, 2), 3, 'All positive numbers');
    assert.strictEqual(App.max(-10, 5, 3, 99), 99, 'Positive and negative numbers');
    assert.strictEqual(App.max(-14, -22, -5), -5, 'All positive numbers');
});

QUnit.test('isOdd', function (assert) {
    assert.ok(App.isOdd(5), '5 is odd');
    assert.ok(!App.isOdd(2), '5 is not odd');
    assert.ok(!App.isOdd(0), '0 is not odd');
    assert.throws(function () {
        App.isOdd(null);
    },
        /The given argument is not a number/,
        'Passing null raises an Error');
    assert.throws(function () {
        App.isOdd([]);
    },
    new Error('The given argument is not a number'),
        'Passing an array raises an Error');
});


QUnit.test('sortObj', function (assert) {
    var timestamp = Date.now();

    var array = [{
        id: 1,
        timestamp: timestamp
    }, {
        id: 3,
        timestamp: timestamp + 1000
    }, {
        id: 11,
        timestamp: timestamp - 1000
    }];

    App.sortObj(array);

    assert.propEqual(array, [{
        id: 11,
        timestamp: timestamp - 1000
    }, {
        id: 1,
        timestamp: timestamp
    }, {
        id: 3,
        timestamp: timestamp + 1000
    }]);
    assert.notPropEqual(App.sortObj(array), array, 'sortObj() does not return an array');
    assert.strictEqual(App.sortObj(array), undefined, 'sortObj() returns
});

The first test created is identified by the string “max”. Within this test you can see four assertions that use the strictEqual() method. We’re using this method instead of equal() because we want to avoid the case where the following assertion would pass:

assert.equal(App.max(0, true), 1);

Inside the test we’re checking for several different types of input. What I’m trying to suggest with this test is to try to cover the largest number of situations possible: no parameters, all positive numbers, all negative numbers, mixed cases. I haven’t covered every possibility, but this is a good start.

The second test, identified with the string “isOdd”, shows you the use of ok() and throws(). The former is useful when you need to verify functions that return a Boolean value like the isOdd() function of our example. You can also see the throws() method in action. Probably the most interesting part of the assertions using throws() isn’t the first parameter, which is the function that raises the error (in these cases because we passed an incorrect parameter), but the variations of the second parameter. In fact, I’ve employed both a regular expression and an Error instance.

The third and last test, identified by the string “sortObj”, puts into action other assertion methods. The first assertion uses propEqual() to verify the array passed to the sortObj() function returns an array containing the same objects (same properties and values) in the order we expected. In this test the deepEqual() method is a good fit too because the expected parameter is identical to the input array (same properties, values, and prototype). I could have not employed strictEqual() because they are not the same object, that is two objects pointing to the same memory address.
The second assertion is a bit naive and serves only to show the use of notPropEqual(). It’s naive because we’re already verifying the expected value in a more accurate way using the strictEqual() method in the third assertion.

Did you like the example? Have you learned something new apart from the signature of the methods? I hope so. Before concluding this tutorial, there is one more thing to discuss.

Setting Expectations

When creating a test, it’s a best practice to set the number of assertions we expect to execute. By doing so, the test will fail in case one or more assertions aren’t executed. The QUnit framework offers the expect() method for this purpose. This method is particularly useful when dealing with asynchronous code but it’s better to use it also when testing synchronous functions. The signature of the expect() method is:

expect(assertionsNumber)

Where the assertionsNumber parameter specifies the number of assertions expected.

With the knowledge of this new concept, let’s update our tests to set the number of assertions we expect to run:

QUnit.test('max', function(assert) {
   expect(4);
   
   // Assertions here...
});

QUnit.test('isOdd', function(assert) {
   expect(5);
   
   // Assertions here...
});

QUnit.test('sortObj', function(assert) {
   expect(3);
   
   // Assertions here...
});

A live demo of the code, including the call to expect(), is shown below and available as a JSBin.

Getting started with QUnit

Conclusion

In this tutorial, I’ve introduced you to the magic world of testing, and especially how to unit-test your JavaScript code with QUnit. We’ve seen how easy is to set up the QUnit framework and what methods it provides to test synchronous functions. In addition, you learned the set of assertion functions the framework offers to test our code. Finally, I’ve mentioned the importance of setting the number of assertions we expect to run and how we can set them using the expect() method. I hope you enjoyed the article and you’ll consider integrating QUnit in your projects.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.globalwebforce.com/ Hitesh Parekh

    It seems that QUnit offers a basic testing script, but I
    might be wrong. Will test drive QUnit on my current project.