Friday, March 5, 2010

Javascript unit testing

Javascript unit test framework is just plain html file loaded in browser, which executes your javascript code in predefined order.
It is quite easy to create it from scratch, but there is a plenty of ready frameworks, that provide nice reports and allow to run different combinations of tests.
I tried QUnit and it quite ok. It is just basically several files, that you have to download - javascript and css. Then you have to create new html file where you will describe your tests. Also from this html you have to link QUnit files and javascript code that you want to test. Unless you want to put your functions in test file, you must have javascript in separate file.
So, in the end you will have something like this:

<html>
  <head>
    <script src="jquery-1.3.2.js"></script>
    <script src="testrunner.js"></script>
    <script type="text/javascript" src="code.js" ></script>
    <link rel="stylesheet" href="testsuite.css" type="text/css"/>
    <script type="text/javascript">

    </script>
  </head>
  <body>
    <ol id="tests"></ol>
    <div id="main"></div>
  </body>
</html>


Where code.js is your javascript code to test.
<ol id="tests"></ol> and <div id="main"></div> is where QUnit puts it's stats.
Now you can start to write tests.
For example, our code.js has something like:

function sum(v1, v2) {
  return v1 + v2;
}


To test it, we should have to call method test, for example like

test("2+2=4", function() {
  var result = sum(2, 2);
  equals(4, result);
});


If your code accesses some html element, there are several options - you can include your real html code into tests.html body, or you can mock it with some trivial html element. You can also add and destroy necessary html elements in test scenario. In general, include is probably best option as it avoids dublication, but simple mock elements was best choise in my cases so far. Drawbacks of include is that it requires server-side code and calls.
For example, if your code is

function showSum(v1, v2) {
  $('#result').text(sum(v1, v2));
}


You can add div element with id 'result'. And now all your tests will look like:

<html>
  <head>
    <script src="jquery-1.3.2.js"></script>
    <script src="testrunner.js"></script>
    <script type="text/javascript" src="code.js" ></script>
    <link rel="stylesheet" href="testsuite.css" type="text/css"/>
    <script type="text/javascript">
      test("2+2=4", function() {
        var result = sum(2, 2);
        equals(4, result);
      });
      test("show 2+2=4", function() {
        showSum(2, 2);
        equals(4, $('#result').text());
      });
    </script>
  </head>
  <body>
    <ol id="tests"></ol>
    <div id="main"></div>
    <div id="result"></div>
  </body>
</html>


Now we have some state between tests (value in 'result' div) and it is not good, because it can confuse tests and bring some surprizes.
Traditional way how to clean state between tests is to reset it. It is normally done in methods setup or teardown.
Setup is called before every test and supposed to prepare test for running and teardown is called after every test and supposed to clean environment after test.
In QUnit it is set in function 'module', like this:

module("my tests", {
  setup: function() {
    $('#result').text('12345');
  },
  teardown: function() {
    $('#result').text('');
  }
});


Here we set result to some value, for example, to check that it really updates value, but not appends. And in teardown we reset 'result' div to it's initial empty state for next test. It usually has more sence in big projects with many state parameters.

No comments:

Post a Comment