QUnit Advanced Concepts: Modules and Configuration

Share this article

In the past weeks I’ve covered several features of QUnit in the tutorials Getting Started with QUnit and How to test asynchronous code in QUnit. I described how to set up the QUnit testing framework to start testing your JavaScript project, what an assert is, what methods QUnit provides, and how to create tests for synchronous or asynchronous code. In this article I’ll discuss how you can organize your test code in modules, and how you can tweak QUnit to better fit your needs using the configuration properties exposed by the framework.

Organizing QUnit in Modules

The ability to organize a project in smaller, more manageable parts isn’t a new concept in software development. Developers have always strived to keep their code simple and organized by splitting the codebase in multiple files or modules. Testing is no different. Keeping our tests organized and in multiple modules, especially if we’re writing tests for a large project, is very useful and usually enhances its maintainability. QUnit provides a method named QUnit.module() that allows us to group our tests into modules. The signature of this method is shown below:
QUnit.module(name[, lifecycle])
The name parameter is a string used to identify the module, while lifecycle is an object containing two optional functions to run before (setup) and after (teardown) each test. To specify which tests belong to a given module, you don’t need to do any sort of wrapping of the tests like this:
// No, it's not like that!
QUnit.module('My first module, {
setup: function() {},
teardown: function() {},
tests: function() {
// Tests here
}
});
A test belongs to a given module simply if it’s defined after a call to QUnit.module() but before another call to QUnit.module() is found. In the next example, we have tests named “Test 1” and “Test 2” that belong to module “Module 1”, and another test, “Test 3”, that belongs to “Module 2”.
// It's like that and that's the way it is
QUnit.module('Module 1');

QUnit.test('Test 1', function(assert) {
// assertions here
});

QUnit.test('Test 2', function(assert) {
// assertions here
})

QUnit.module('Module 2');

QUnit.test('Test 3', function(assert) {
// assertions here
});
Ideally, module names express an isolated part of your project. For example, the jQuery library has the following modules: ajax, core
, css, event, selector, etc. Now that you have a clear idea of how tests are grouped in modules, let’s learn more about the setup and teardown functions. Let’s say that you want to run severals tests on the following object:
var myObj = {
name: 'Aurelio De Rosa',
items: []
};
You want to be sure that before a test is performed, the items property is filled with the numeric values 1, 2 and 3. In addition you want that every time a test is concluded, any additional property that isn’t name or items is deleted from the object. Such a goal can be achieved with the following code:
QUnit.module('My module', {
setup: function() {
myObj.items = [1, 2, 3];
},
teardown: function() {
for (var prop in myObj) {
if (prop !== 'name' && prop !== 'items') {
delete myObj[prop];
}
}
}
});

QUnit.test('Test 1', function(assert) {
expect(2);

// Set a new property of the myObj object
myObj.color = 'red';

assert.strictEqual(myObj.items.length, 3, 'The setup function has pushed 3 elements');
assert.strictEqual(myObj.items, [1, 2, 3], 'The setup function has pushed the expected elements');
});

QUnit.test('Test 2', function(assert) {
expect(1);

assert.ok(!myObj.color, 'The teardown function removed the color property');
});
A live demo of this example is shown below and also available as a JSfiddle. Now, let’s see how we can create a custom configuration in QUnit.

How to Configure QUnit

The QUnit framework exposes a bunch of configuration properties that we can modify to better fit our project’s needs. The framework offers a default configuration good for most cases, but we can tweak it by updating the QUnit.config property. This property is an object containing the following properties (reported in alphabetic order):
  • altertitle: A Boolean to enable (true) or disable (false) QUnit from updating the title of the test page by adding a checkmark or an “x” to specify if a testsuite passed or failed. The default value is true.
  • autostart: A Boolean which, if set to false, specifies that you want to run the tests by yourself by calling QUnit.start() and not when the load event is triggered. The default value is true.
  • hidepassed: A Boolean to specify if the passed tests should be hidden (true) or not (false). The default value is false.
  • module: A string that specifies a single module to run. The default value is undefined, so QUnit runs all the modules defined.
  • reorder: A Boolean to indicate if QUnit should run tests that failed on a previous execution first (true) or not (false). The default value is true.
  • requireExpects: A Boolean to specify if you want to force a call to expect() in each test defined (true) or not (false). The default value is true.
  • testNumber: An array to run specific test blocks by their order number. The order is set as the tests blocks are loaded. The default value is undefined.
  • testTimeout: A number that indicates a maximum time execution after which all tests will fail. The default value is undefined.
  • scrolltop: A Boolean to specify if you want to avoid that QUnits goes to the top of the page when all the tests are executed (true) or not (false). The default value is true.
  • urlConfig: An array that manages the form controls to place into the QUnit toolbar. By extending this array, you can add your own checkboxes and select lists.
Now that you know what features you can modify, let’s see how we can use them. But first, an important point to keep in mind is that the custom configuration must be written after the inclusion of the QUnit’s JavaScript file, but before you define the tests. Let’s say that you want to force the testers in your team to always define the number of assertions to execute. You also want QUnit to hide the passed tests because the testsuite is very large. Finally, you want to stop QUnit from scrolling to the top of the page automatically. In this case you can write:
<script src="qunit-1.15.0.js"></script>
<script>
QUnit.config.hidepassed = true;
QUnit.config.requireExpects = true;
QUnit.config.scrolltop = true;
</script>
<script>
QUnit.test('My test', function(assert) {
// assertions go here...
});
</script>
In this example we’ve seen a basic custom configuration. You can expand on it and create a very complicated one that is right for your project.

Conclusion

In this article I introduced you to modules in QUnit and showed you how to create a custom configuration. In the first section we discussed how to create a module in QUnit using the QUnit.module() method, and learned how the framework groups tests together. Then, I described how to create setup and a teardown functions that run before and after every test in a module. In the second section, I listed all the properties exposed by QUnit to change its default configuration to better fit your project’s need. I hope you liked this tutorial. Thanks to this, and my previous articles, you’re now able to start testing your JavaScript-based projects with QUnit.

Frequently Asked Questions (FAQs) about QUnit Advanced Concepts and Configuration

What are the key differences between QUnit and other testing frameworks?

QUnit is a powerful, easy-to-use JavaScript unit testing framework. It’s used by the jQuery, jQuery UI, and jQuery Mobile projects and is capable of testing any generic JavaScript code. Unlike other testing frameworks, QUnit’s syntax is relatively simple and it has a straightforward setup. It also provides a clean and organized interface for running tests and viewing results. Furthermore, QUnit supports asynchronous testing, which is a significant advantage when testing code that includes AJAX requests, timers, or other asynchronous behavior.

How can I use QUnit for asynchronous testing?

QUnit provides a function called assert.async() for asynchronous testing. This function returns a callback, which you can call when your asynchronous operation is completed. This tells QUnit that your asynchronous test is done and it can move on to the next test. Here’s a simple example:

QUnit.test("asynchronous test", function(assert) {
var done = assert.async();
setTimeout(function() {
assert.ok(true);
done();
}, 100);
});

How can I group related tests in QUnit?

QUnit provides a function called module() for grouping related tests. This function takes a string as its argument, which is the name of the module. All tests that follow a call to module() will be grouped under that module. Here’s an example:

QUnit.module("group1");
QUnit.test("test1", function(assert) {
assert.ok(true);
});
QUnit.test("test2", function(assert) {
assert.ok(true);
});

What are the hooks in QUnit and how can I use them?

Hooks are functions that can be used to handle repetitive setup and teardown tasks. QUnit provides four hooks: before(), beforeEach(), afterEach(), and after(). These hooks are called before and after each test, or before and after all tests in a module, respectively. Here’s an example:

QUnit.module("group1", {
beforeEach: function() {
// This function is called before each test in this module.
},
afterEach: function() {
// This function is called after each test in this module.
}
});

How can I measure code coverage with QUnit?

Measuring code coverage with QUnit requires integrating a code coverage tool. Istanbul is a popular tool for this purpose. It instruments your JavaScript code with line counters, so you can track how well your tests exercise your codebase. After running your tests, Istanbul generates a detailed report of your code coverage.

How can I handle exceptions in QUnit tests?

QUnit provides a function called throws() to test if a function throws an exception. This function takes two arguments: a function that is expected to throw an exception, and an error object or constructor that the thrown exception is expected to match. Here’s an example:

QUnit.test("throws test", function(assert) {
function throwsError() {
throw new Error("error");
}
assert.throws(throwsError, Error, "throws an Error");
});

How can I run a specific test or module in QUnit?

QUnit provides a feature to run a specific test or module. You can simply click on the name of the test or module in the HTML report to run it. Alternatively, you can use the QUnit.only() function to run a specific test, or the QUnit.module.only() function to run a specific module.

How can I skip a test in QUnit?

QUnit provides a function called QUnit.skip() to skip a test. This function takes two arguments: the name of the test and a function that contains the test. The test will be skipped when QUnit runs the tests, and the reason will be displayed in the HTML report.

How can I use QUnit for testing DOM manipulations?

QUnit is capable of testing DOM manipulations. You can use jQuery or native DOM APIs to manipulate the DOM in your tests, and then use QUnit’s assertion functions to verify the results. Here’s an example:

QUnit.test("DOM test", function(assert) {
var $div = $("<div>").appendTo("body");
$div.text("hello");
assert.equal($div.text(), "hello", "text is set correctly");
$div.remove();
});

How can I configure QUnit to suit my needs?

QUnit provides a global configuration object called QUnit.config. You can modify this object to change the behavior of QUnit. For example, you can set QUnit.config.autostart to false to prevent QUnit from automatically starting the tests. Then you can manually start the tests by calling QUnit.start().

Aurelio De RosaAurelio De Rosa
View Author

I'm a (full-stack) web and app developer with more than 5 years' experience programming for the web using HTML, CSS, Sass, JavaScript, and PHP. I'm an expert of JavaScript and HTML5 APIs but my interests include web security, accessibility, performance, and SEO. I'm also a regular writer for several networks, speaker, and author of the books jQuery in Action, third edition and Instant jQuery Selectors.

ColinIQUnitTesting
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week