Skip to main content

Testdriven development with JavaScript and QUnit

I've always had problems with JavaScript, because the lack of tooling, no good source for documentation and the most advanced way of debugging being alert. It has become easier over the years with jQuery and Firebug, but being used to C# I'm still a bit lost when it comes to JavaScript. I was recently required to write some validation logic in JavaScript, and to make sure that I was on track the whole time, I adopted TDD in JavaScript with QUnit.

QUnit

Writing tests for JavaScript is actually much easier than unit testing C# code. You have so much more freedom in a dynamically typed language that makes it easier to write tests that will not pass, such as code in a statically typed language would not compile.

Getting started

Download jQuery, qunit.js and qunit.css and include them in an HTML document that will be host of your test suite.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Required field validation</title>
    <script type="text/javascript" src="jquery-1.3.2.min.js"></script>
 <script type="text/javascript" src="qunit.js"></script>
    <link rel="stylesheet" href="qunit.css" type="text/css" media="screen" />

<!-- SYSTEM UNDER TEST --> <script type="text/javascript" src="Validation.js"></script> <script type="text/javascript"> $(document).ready(function () { /* TESTS GO HERE */ }); </script> </head> <body> <!-- QUNIT REQUIRED HTML --> <h1 id="qunit-header">Required field validation</h1> <h2 id="qunit-banner"></h2> <h2 id="qunit-userAgent"></h2> <ol id="qunit-tests"></ol>

<!-- TEST STRUCTURE --> <div id="test-container" style="display: none"> </div> </body> </html>

Keywords

You write your tests in the HTML document that you've created, and you use different HTML documents to divide your test suite in sections as it grows. My first test looks like this.

module("Validate on blur()");
test("Should validate required field as false when input value is empty", function() {
 expect(1);

/* Initialize the validation */ heartbeat.initialize();

/* Select the required 'Name' field and execute blur() event */ $('#Name').blur();

equals($('#name-field').data('isValid'), false, 'Expected the field to be marked as invalid on blur()'); });

  • module([string] heading) - A way to group tests together under a common heading
  • test([string] name, function()) - The name of the test and the test code
  • expect(1) - tells the test runner that I will use 1 assert
  • equals([obj] actual, [obj] expecting, [string] message) - compares actual object with expecting object and fails if they are not equal. The message describes the failing condition.
  • ok([boolean] condition) - though not present here, you can use the ok method instead of equals if you want to check that a condition is true.

It runs on the following HTML part.

<div id="name-field" class="field name required">
 <label for="Name">Your name</label>
 <input id="Name" name="Name" type="text" value="" />
</div>

The beautiful thing about this is that I can write this test and get a red result without having implemented the actual validation logic yet. So, I implement the logic to make the test green and in my browser it looks like this.

test success

One test executed successfully!

Setup and Teardown

Using the same HTML structure for all tests in the test suite is however not a good idea, because it will accuire state that will effectivly affect all other tests in the test suite. This means that we will have to recreate the HTML structure for every test run.

/* SETUP / TEARDOWN */
QUnit.testStart = function (name) {
 $('#test-container').append('<div id="name-field" class="field name required"><label for="Name">Your name</label><input id="Name" name="Name" type="text" value="" /></div>');
};

QUnit.testDone = function (name, failures, total) { $('#test-container').empty(); } /* ************* */

There is no longer any reason not to unit test your JavaScript. You can download the whole example from here.

comments powered by Disqus