JavaScript Test Driven Development with QUnit - Tutorial

In this post lets talk about testing your javascript code. There are couple of libraries that help you test your javascript. For Unit Testing JavaScript, QUnit is one of the most popular, easy to use Framework. It is maintained by the jQuery team. QUnit comes tested by QUnit. Lets address a very simple query first: Do we need to test JavaScript as well? To some extent, the answer is yes. The JS code is much more complex than you think, besides the different browsers take and process JS differently, hence having your JS code tested makes sure things do not break whether it is adding new regression or it is about addressing a bug.

1. How do we get started?

Getting started with QUnit is very simple. Putting the following 5 lines of code will suffice:

  <head>
    <link rel="stylesheet" href="/css/qunit.css">
    <script type="text/javascript"  src="qunit.js"></script>
    <script type="text/javascript"  src="test.js"></script>
  </head>

  <body>
    <div id="qunit"></div>
  </body>

1. Include the QUnit css and js file. 
2. Introduce a div with id qunit in the body
3. Start writing Tests

2 How do we go about writing tests?

2.1 Basic lies in Assertion(Validating the expected behavior)

Following are the assertion statements that can be used: ok, equal, notEqual, strictEqual, notStrictEqual, deepEqual, notDeepEqual, raises

Simple Examples are as follows:

  ok(true,  "passes because true is true");
  equal(1, 1,     "passes because 1 == 1");
  notEqual(1, 0,     "passes because 1 != 0");
  strictEqual(1, 1,     "passes because 1 === 1");
  notStrictEqual(1, 1,     "fails because 1 === 1");
2.2 Set Expectations

Next to assert statements comes expectations, the number of asserts in a given test. You can either set an expectation (number) in either of the two ways shown below:

test("test name", function() {
  expect(3);
  // Put 3 assertions here
});

test("test name", 3, function() {
  // Put 3 assertions here
});
2.3 Organizing Your Test Modules

When we talk about testing, modularity is something which we should take extra care of. You can define modules as follows:

module("my-module-name");
test( "hello test", function() {
  ok( 1 == "1", "Passed!" );
});

In the above snippet, we have given a module name and put some tests within that.

2.4 setup and teardown
module("my-module-name", {
  setup: function() {
    this.prop = "first val";
  },
  teardown: function() {
    ok(true, "and one extra assert after each test");
  }
});

test("test with setup and teardown", function() {
  ok(true,  "passes because true is true");
});

test("test with fixtures", function(){
  expect(1);
  same(this.prop, "first val", "this.prop === 'first val' in all tests");
});

In the above snippets, we first define a setup method which will assign a value of "first val" to the attribute prop in a scope, and that is what we are asserting in the test. The setup and teardown functions are called before and after each test.

2.5 Test Fixtures

If you want some values to be ready for the tests, you can add them in the body with an id qunit-fixture as follows:

<div id="qunit-fixture">
    <ul>
    <li>first</li>
    <li>second</li>
    <li>third</li>
  </ul>
</div>

These fixtures will be re-created on every setup and teardown method. They can be tested as follows:

  var items = $("#qunit-fixture li").enumerate();
  equal(items.eq(0).text(), "1. first", "first item should have index 1");
3 Sample Test Case

Having looked at all the components separately, lets take a look at a simple QUnit test: var isPalin = function( str ){ return (str.split('').reverse().join('') == str) ? true : false; }; module( 'Palin Test' ); test( 'isPalin()', function() { expect( 2 ); equal( isPalin('madam'), true, 'Test a true palindrome' ); notEqual( isPalin('world'), true, 'Test a false palindrome'); });

Observations in the above snippets:

  1. We have a function palindrome, which checks if a string is palindrome or not.

  2. We have a test module named Palin Test

  3. There are two assert statements as mentioned by expect (2)

  4. The first assert statement validates that the word madam is a palindrome.

  5. The second statement validates that the word world is not a palindrome.

Writing QUnit tests is as easy as that.

4 Some Tools and few Guidelines

Having looked at all above. Where do we go from here? You might want to explore the following.

Extra care while writing tests:

Above all, be consistent. Very very consistent. Be consistent with absolutely everything, in following:

  • how you organize code in each of your test modules?

  • how you introduce test cases for your application components?

  • how you introduce test methods for asserting the behavior?

  • how you structure test methods?

  • how you approach testing common components?

  • how you apply reuse?

The above will vary for project to project, and gradually if you consider the above in mind you will end up with a much more maintainable and less painful JS codebase.

Comments

blog comments powered by Disqus