Unit Testing with QUnit in jQuery: Putting it All Together in a Test Suite

Once again, the jQuery test suite is going to offer some lessons on how to organize QUnit tests. There’s really no better place to look for practical QUnit implementation hints because it’s the largest, most mature set of QUnit tests available.

As you saw earlier, the jQuery test suite is broken out into 17 modules representing more granular sections of the library, like data, css, and deferred. Within those modules are multiple tests, each containing multiple assertions. To get a sense of how those modules are organized in a real project, take a look at this heavily edited version of the Core test module presented in the following code sample. Many tests have been dropped and almost all of the assertions have been commented out, but the lessons on structure, granularity, and the way tests evolve can still be seen throughout.

Specific features are tested individually with multiple assertions per test. Also, note the presence of bug report numbers as in jQuery.merge() and jQuery(‘html’, context). These issues were reported, test cases were created, and the code was fixed. The test cases remain, providing confidence that the bug remains fixed even months or years later. Additionally, a simple reminder that QUnit is JavaScript and that tests can be dynamically created can be seen in the jQuery.camelCase() test, which uses an Array of test subjects and then jQuery.each to loop through them, testing each of the seven expected assertions.

module(“core”, { teardown: moduleTeardown });

test(“Basic requirements”, function() {


ok( Array.prototype.push, “Array.push()” );

ok( Function.prototype.apply, “Function.apply()” );

ok( document.getElementByld, “getElementByld” );

ok( document.getElementsByTagName, “getElementsByTagName” );

ok( RegExp, “RegExp” );

ok( jQuery, “jQuery” );

ok( $, “$” ) ;


test(“jQuery()”, function() {


//29 assertions


test(“selector state”, function() {


//31 assertions


test(“jQuery(‘html’)”, function() {


//18 assertions


test(“jQuery(‘html’, context)”, function() {


var $div = jQuery(“<div/>”)[0];

var $span = jQuery(“<span/>”, $div);

equal($span.length, 1, “Verify a span created with a div context works, #1763”);


test(“first()/last()”, function() {


var $links = jQuery(“#ap a”), $none = jQuery(“asdf”);

deepEqual( $links.first().get(), q(“google”), “first()” );

deepEqual( $links.last().get(), q(“mark”), “last()” );

deepEqual( $none.first().get(), [], “first() none” );

deepEqual( $none.last().get(), [], “last() none” );


test(“map()”, function() {


//8 assertions


test(“jQuery.merge()”, function() {


var parse = jQuery.merge;

deepEqual( parse([],[]), [], “Empty arrays” );

deepEqual( parse([1],[2]), [1,2], “Basic” );

deepEqual( parse([1,2],[3,4]), [1,2,3,4], “Basic” );

deepEqual( parse([1,2],[]), [1,2], “Second empty” );

deepEqual( parse([],[1,2]), [1,2], “First empty” );

// Fixed at [5998], #3641

deepEqual( parse([-2,-1], [0,1,2]),[-2,-1,0,1,2],

“Second array including a zero (falsy)”);

// After fixing #5527

deepEqual( parse([], [null, undefined]), [null, undefined],

“Second array including null and undefined values”);

deepEqual( parse({length:0}, [1,2]),

{length:2, 0:1, 1:2}, “First array like”);


test(“jQuery.extend(Object, Object)”, function() {


//28 assertions


test(“jQuery.each(Object,Function)”, function() {


//14 assertions


test(“jQuery.sub() – Static Methods”, function(){


//18 assertions


test(“jQuery.sub() – .fn Methods”, function(){


//378 assertions


test(“jQuery.camelCase()”, function() {

var tests = {

“foo-bar”: “fooBar”,

“foo-bar-baz”: “fooBarBaz”,

“girl-u-want”: “girlUWant”,

“the-4th-dimension”: “the4thDimension”,

“-o-tannenbaum”: “OTannenbaum”,

“-moz-illa”: “MozIlla”,

“-ms-take”: “msTake”



jQuery.each( tests, function( key, val ) {

equal( jQuery.camelCase( key ), val, “Converts: ” + key + ” => ” + val );



Code snippet is from jquery-suite.html

Source: Otero Cesar, Rob Larsen (2012), Professional jQuery, John Wiley & Sons, Inc

Leave a Reply

Your email address will not be published. Required fields are marked *