Very easy.
test("some tests", function() { expect(3); ok(true, "passes because true is true"); equal("1", 1, "passes because '1' == 1"); strictEqual("1", 1, "fails because '1' !== 1"); });
Thousands of tests.
Just qunit.js, qunit.css, and a little bit of HTML:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>MyApp Test Suite</title> <link rel="stylesheet" href="qunit.css" type="text/css"> <script src="qunit.js"></script> <script src="myapp.js"></script> <script src="myapp-test.js"></script> </head> <body> <h1 id="qunit-header">MyApp Test Suite</h1> <h2 id="qunit-banner"></h2> <div id="qunit-testrunner-toolbar"></div> <h2 id="qunit-userAgent"></h2> <ol id="qunit-tests"></ol> <div id="qunit-fixture"></div> </body> </html>
It's really this simple.
test("The name of the test", function() { // Assertions. });
You say “this is how it should work,”
and QUnit tells you when it doesn't.
Get in the habit of doing this!
// You can either set an expectation (number) like this. test("test name", function() { expect(3); // QUnit expects 3 assertions in this test. }); // Or like this. test("test name", 3, function() { // QUnit expects 3 assertions in this test. });
ok
,
equal
,
notEqual
,
strictEqual
,
notStrictEqual
,
deepEqual
,
notDeepEqual
,
raises
ok
A boolean assertion that passes if the first argument is truthy.
test("ok", 3, function() { ok(true, "passes because true is true"); ok(1, "passes because 1 is truthy"); ok("", "fails because empty string is not truthy"); });
equal
A comparison assertion that passes if actual == expected.
test("equal", 3, function() { var actual = 5 - 4; equal(actual, 1, "passes because 1 == 1"); equal(actual, true, "passes because 1 == true"); equal(actual, false, "fails because 1 != false"); });
notEqual
A comparison assertion that passes if actual != expected.
test("notEqual", 3, function() { var actual = 5 - 4; notEqual(actual, 0, "passes because 1 != 0"); notEqual(actual, false, "passes because 1 != false"); notEqual(actual, true, "fails because 1 == true"); });
strictEqual
A comparison assertion that passes if actual === expected.
test("strictEqual", 3, function() { var actual = 5 - 4; strictEqual(actual, 1, "passes because 1 === 1"); strictEqual(actual, true, "fails because 1 !== true"); strictEqual(actual, false, "fails because 1 !== false"); });
notStrictEqual
A comparison assertion that passes if actual !== expected.
test("notStrictEqual", 3, function() { var actual = 5 - 4; notStrictEqual(actual, 1, "fails because 1 === 1"); notStrictEqual(actual, true, "passes because 1 !== true"); notStrictEqual(actual, false, "passes because 1 !== false"); });
deepEqual
Recursive comparison assertion, working on primitives, arrays and objects, using ===.
test("deepEqual", 7, function() { var actual = {a: 1}; equal( actual, {a: 1}, "fails because objects are different"); deepEqual(actual, {a: 1}, "passes because objects are equivalent"); deepEqual(actual, {a: "1"}, "fails because '1' !== 1"); var a = $("body > *"); var b = $("body").children(); equal( a, b, "fails because jQuery objects are different"); deepEqual(a, b, "fails because jQuery objects are not equivalent"); equal( a.get(), b.get(), "fails because element arrays are different"); deepEqual(a.get(), b.get(), "passes because element arrays are equivalent"); });
notDeepEqual
Recursive comparison assertion. The result of deepEqual, inverted.
test("notDeepEqual", 3, function() { var actual = {a: 1}; notEqual( actual, {a: 1}, "passes because objects are different"); notDeepEqual(actual, {a: 1}, "fails because objects are equivalent"); notDeepEqual(actual, {a: "1"}, "passes because '1' !== 1"); });
raises
Assertion to test if a callback throws an exception when run.
test("raises", 3, function() { raises(function() { throw new Error("Look ma, I'm an error!"); }, "passes because an error is thrown inside the callback"); raises(function() { x // ReferenceError: x is not defined }, "passes because an error is thrown inside the callback"); raises(function() { var a = 1; }, "fails because no error is thrown inside the callback"); });
Execution order cannot be guaranteed!
// Don't do this. var counter = 0; test("first test", 1, function() { counter++; equal(counter, 1, "counter should be 1"); }); test("second test", 1, function() { counter++; equal(counter, 2, "counter should be 2"); }); test("third test", 2, function() { counter++; equal(counter, 2, "counter should be 2"); ok(false, "oops, an error"); });
Any markup in here will be reset after every test (uses jQuery if available).
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>MyApp Test Suite</title> <link rel="stylesheet" href="qunit.css" type="text/css"> <script src="qunit.js"></script> <script src="myapp.js"></script> <script src="myapp-test.js"></script> </head> <body> <h1 id="qunit-header">MyApp Test Suite</h1> <h2 id="qunit-banner"></h2> <div id="qunit-testrunner-toolbar"></div> <h2 id="qunit-userAgent"></h2> <ol id="qunit-tests"></ol> <div id="qunit-fixture"> <ul> <li>foo</li> <li>bar</li> <li>baz</li> </ul> </div> </body> </html>
A little forethought can save a lot of frustration.
module("jQuery#enumerate"); test("chainable", 1, function() { var items = $("#qunit-fixture li"); strictEqual(items.enumerate(), items, "should be chaninable"); }); test("no args passed", 3, function() { var items = $("#qunit-fixture li").enumerate(); equal(items.eq(0).text(), "1. foo", "first item should have index 1"); equal(items.eq(1).text(), "2. bar", "second item should have index 2"); equal(items.eq(2).text(), "3. baz", "third item should have index 3"); }); test("0 passed", 3, function() { var items = $("#qunit-fixture li").enumerate(0); equal(items.eq(0).text(), "0. foo", "first item should have index 0"); equal(items.eq(1).text(), "1. bar", "second item should have index 1"); equal(items.eq(2).text(), "2. baz", "third item should have index 2"); }); test("1 passed", 3, function() { var items = $("#qunit-fixture li").enumerate(1); equal(items.eq(0).text(), "1. foo", "first item should have index 1"); equal(items.eq(1).text(), "2. bar", "second item should have index 2"); equal(items.eq(2).text(), "3. baz", "third item should have index 3"); });
(Time for an example)
If it makes sense, test your plugin
in older versions of jQuery too.
Because your unit tests should be organized too.
module("core"); test("a test in the core module", function() { ok(true, "this test had better pass"); }); test("another test in the core module", function() { ok(true, "this test had also better pass"); }); module("options"); test("a test in the options module", function() { ok(true, "this test really, really better pass"); }); test("another test in the options module", function() { ok(false, "sadly, this test is going to fail"); });
Configure setup and teardown callbacks to streamline your tests.
// Defining a "setup" callback. module("module1", { setup: function() { ok(true, "once extra assert per test"); } }); test("test with setup", function() { expect(1); }); // Defining both "setup" and "teardown" callbacks. module("module2", { setup: function() { ok(true, "once extra assert per test"); this.prop = "foo"; }, teardown: function() { ok(true, "and one extra assert after each test"); } }); test("test with setup and teardown", function() { expect(3); same(this.prop, "foo", "this.prop === 'foo' in all tests"); });
But as you can see, sometimes no errors is a bad thing.
test("no errors", function() { var actual = false; setTimeout(function() { ok(actual, "this test would fail.. if it ever ran"); }, 1000); });
Now you get an error.. but it's not the error you want.
test("expectations", function() { expect(1); var actual = false; setTimeout(function() { ok(actual, "this test would fail.. if it ever ran"); }, 1000); });
stop
& start
You must tell QUnit to wait for an asynchronous action to complete.
test("stop & start", function() { expect(1); var actual = false; stop(); setTimeout(function() { ok(actual, "this test actually runs, and fails"); start(); }, 1000); });
asyncTest
Another way to tell QUnit to wait for an asynchronous action to complete.
asyncTest("asyncTest & start", function() { expect(1); var actual = false; setTimeout(function() { ok(actual, "this test actually runs, and fails"); start(); }, 1000); });
stops
& starts
Jörn added this in... because I asked nicely.
test("stops & starts", function() { expect(4); var url = "http://jsfiddle.net/echo/jsonp/?callback=?"; stop(); $.getJSON(url, {a: 1}, function(data) { ok(data, "data is returned from the server"); equal(data.a, "1", "the value of data.a should be 1"); start(); }); stop(); $.getJSON(url, {b: 2}, function(data) { ok(data, "data is returned from the server"); equal(data.b, "2", "the value of data.b should be 2"); start(); }); });
What are you testing anyways, your
client code or your server code?
If you mock your AJAX Requests, you test your JavaScript, not your server.
// Simulate your API. $.mockAjax("json", { "/user": {status: -1}, "/user/(\\d+)": function(matches) { return {status: 1, user: "sample user " + matches[1]}; } }); // Unit tests. test("user tests", function() { expect(5); stop(); $.getJSON("/user", function(data) { ok(data, "data is returned from the server"); equal(data.status, "-1", "no user specified, status should be -1"); start(); }); stop(); $.getJSON("/user/123", function(data) { ok(data, "data is returned from the server"); equal(data.status, "1", "user found, status should be 1"); equal(data.user, "sample user 123", "user found, id should be 123"); start(); }); });
Add ?filter=foo
to the URL to run only tests whose names contain "foo".
“Modules” Test Suite | |
---|---|
All tests | modules.html |
“core” module | modules.html?filter=core |
“options” module | modules.html?filter=options |
“Assertions” Test Suite | |
All tests | assertions.html |
“ok” test | assertions.html?filter=ok |
“equal” tests | assertions.html?filter=equal |
Just add ?noglobals
to the URL, and QUnit will fail any “leaky” tests.
test("not leaky", 1, function() { var x = true; ok(x, "passes because x is true"); }); test("leaky", 1, function() { x = true; ok(x, "also passes because x is true"); });
Automatically capture QUnit test results, à la TestSwarm.
// Runs once at the very beginning. QUnit.begin = function() { console.log("Running Test Suite"); }; // Runs once at the very end. QUnit.done = function(failures, total) { console.info("Suite: %d failures / %d tests", failures, total); }; // Runs once after each assertion. QUnit.log = function(result, message) { console[ result ? "log" : "error" ](message); }; // Runs before each test. QUnit.testStart = function(name) { console.group("Test: " + name); }; // Runs after each test. QUnit.testDone = function(name, failures, total) { console.info("Test: %d failures / %d tests", failures, total); console.groupEnd(); }; // Runs before each module. QUnit.moduleStart = function(name) { console.group("Module: " + name); }; // Runs after each module. QUnit.moduleDone = function(name, failures, total) { console.info("Module: %d failures / %d tests", failures, total); console.groupEnd(); }; // Runs after each test group. Redefining this function will // override the built-in #qunit-fixture reset logic. QUnit.reset = function() { console.log("Test done!"); };
Distributed Continuous Integration for JavaScript.