Key Takeaways
- QUnit allows for test code to be organized into modules, enhancing maintainability especially for larger projects. This organization is achieved through the QUnit.module() method, which groups tests together based on their order of definition.
- QUnit also provides setup and teardown functions that run before and after each test in a module, which can be utilized to prepare and clean up the testing environment for each individual test.
- QUnit’s configuration can be customized to better suit project needs. This customization is achieved by modifying properties of the QUnit.config object, which includes properties related to test execution, display of test results, and more.
- Custom configurations for QUnit must be written after the inclusion of the QUnit’s JavaScript file and before the definition of the tests. This allows for specific adjustments such as forcing the definition of the number of assertions to execute, hiding passed tests, or disabling automatic scrolling to the top of the page.
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 namedQUnit.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 theQUnit.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 istrue
.autostart
: A Boolean which, if set tofalse
, specifies that you want to run the tests by yourself by callingQUnit.start()
and not when the load event is triggered. The default value istrue
.hidepassed
: A Boolean to specify if the passed tests should be hidden (true
) or not (false
). The default value isfalse
.module
: A string that specifies a single module to run. The default value isundefined
, 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 istrue
.requireExpects
: A Boolean to specify if you want to force a call toexpect()
in each test defined (true
) or not (false
). The default value istrue
.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 isundefined
.testTimeout
: A number that indicates a maximum time execution after which all tests will fail. The default value isundefined
.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 istrue
.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.
<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 theQUnit.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()
.
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.