Unit Testing JavaScript with QUnit and Mockjax

I’ve been experimenting a bit with JavaScript. My lack of real knowledge of the language, apart from some simple DOM-manipulations, is starting to become embarrassing!

So a couple of months ago I decided I should pick up the JS axe, and do some chopping. And the first step to guiding yourself into any new programming language is the selection of (or writing of…) the unit testing framework!

My first choice was qunit. I’d decided that I’d stick close to the jquery way of doing things, to begin with, so this seemed a logical choice. I’m very much used to automated build systems, so my first steps, after getting the most basic unit test running in a browser, was of automating the testing. This was not as easy as I had hoped! Having to start a full-blown webbrowser during a build is frowned upon. It requires, apart from plenty of time, that the build server has a graphical UI running and available, and is rather prone to errors. Setting-up something like Rhino is easy enough, but will fail as soon as we need to do things like DOM manipulation. Luckily, there turned out to be a reasonable compromise: using PhantomJS.

PhantomJS is a full WebKit browser, but one that is completely headless! This means you can have it load web-pages, which are fully functional, without needing to have a UI visible. It’s completely scriptable from JavaScript, and runs very quickly. Great! The PhantomJS pages even included some examples on how to use it with qunit.

Of course, as any JavaScript I write is usually part of a Java project, and I had actually written a little (atmosphere based) server for my test project, I wanted to run this from a maven build. I found the phantomjs-qunit-runner maven plugin, and thought I was all set to go!

But it wasn’t that easy… The maven plugin worked fine, but had trouble understanding javascript libraries loaded during testing. Since my tests involved mocking out the service I was using (they were supposed to be unit tests, after all!) I could not manage to run them using the phantomjs-qunit-plugin.

It took me a few attempts to understand how the maven plugin dealt with making separate JavaScript files available to PhantomJs, but I finally managed to make it work.

If you are going to try anything from this post, make sure that you have a version of the phantomjs-qunit-runner that has my changes! As of writing, that means checking out trunk, and building it yourself.

From this point on, everything is easy!

We  start with a maven pom.xml that sets up the phantomjs runner:

    <build>
        <finalName>GoalServer</finalName>
        <plugins>
            <plugin>
                <groupId>net.kennychua</groupId>
                <artifactId>phantomjs-qunit-runner</artifactId>
                <version>1.0.12-SNAPSHOT</version>
                <configuration>
                    <jsSourceDirectory>src/main/javascript/</jsSourceDirectory>
                    <jsTestDirectory>src/test/javascript/</jsTestDirectory>
                    <ignoreFailures>false</ignoreFailures>
                    <phantomJsExec>/home/wouter/opt/phantomjs/bin/phantomjs</phantomJsExec>
                    <libraries>
                        <directory>src/test/javascript/lib/</directory>
                        <includes>
                            <include>**/*.js</include>
                        </includes>
                    </libraries>
                </configuration>
                <executions>
                    <execution>
                        <phase>test</phase>
                        <goals><goal>test</goal></goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

You can see that I’ve stuck to the standard maven directory structure, keeping my javascript in the src/main/javascript and its tests in src/test/javascript. You do need to specify where the phantomjs executable is installed. This is slightly unfortunate, and should in a real project be delegated to a configuration setting (in the maven settings.xml, probably). For an example, having it hard-coded is clearer.
The part of this that I added is the libraries tag, where you use the default maven fileset syntax to define all the libraries you want to have available when executing the tests. In my codebase, I put all the javascript libraries in src/test/javascript/lib, but an argument could be made to put these somewhere outside of your src dirs. The plugin doesn’t care, as the fileset is translated to fully qualified paths before handing things over to PhantomJS.

I must admit that my goals weren’t set very high for my first test. After all, this was to be my first javascript test! So it turned out like this:

test("Test Test", function() {
    console.log("Testing test");
    equal(1, 0, "equal!");
});

Very exciting! And indeed, it failed:

[ERROR] Failed to execute goal net.kennychua:phantomjs-qunit-runner:1.0.12-SNAPSHOT:test (default) on project GoalServer: One or more QUnit tests failed -> [Help 1]

Now if you look carefully, you might be able to fix that test yourself. I’ll leave it at that, because I was quick to move on to my next step, which involved calling a function in javascript code which was in another file, and not located in the test code directory.

test("Test true", function() {
   equal(1, GameScheduleClient.testing.isTrue(), "It s true");
});

This is calling the following mindbogglingly complex function in src/main/javascript/GameScheduleClient.js:

var GameScheduleClient = GameScheduleClient || {};

GameScheduleClient.testing = function() {
    return {
        isTrue : function() {
            return 1;
        }
    };
} ();

If this doesn’t work, take a look at the advice above, and ensure you have a version of the qunit-runner that includes the patches I did. Otherwise you’ll have to do what I did, and run around in circles for a day or so.

Next step is to be able to call a service, which we’ll mock with MockJax. I’m not going to explain all the details of how mockjax works, for that I suggest you read something from someone who actually understands this stuff. But as long as you’ve put the library in the right place, and use the right version of the maven plugin, the following code should work:

module("Mock Ajax", {
    setup: function () {
        $.mockjax({
            url:"/mockedservice",
            contentType:"text/json",
            responseText:[ { bla:"Test" }]
        });
    },
    teardown: function () {
        $.mockjaxClear();
    }
});
asyncTest("Test I get a mocked response from a service", function () {
    $.getJSON("/mockedservice", function (response) {
        ok(response, "There's no response!");
        equal(response.responseText.bla, "NotTest", "response was not Test");
        start();
    });
});

Note that there is no supporting javascript method that we’re actually testing here. The $.mockjax call sets up mockjax to respond to a (jquery) ajax call to the /mockedservice url with a response containing the string Test. The $.getJSON call is a regular jquery ajax call, and this test simply verifies that the response.

The test module has a separate setup and teardown, which are called for each test, as you’d expect in an xUnit type framework. The test must be an explicit asyncTest, that is started explicitly within that method.

And that, as they say, is all there is to it! All in all, qunit provides a simple interface for basic unit-testing. I’m now looking into Jasmine for a more elaborate set-up, and a little better integration with the maven build environment.

4 thoughts on “Unit Testing JavaScript with QUnit and Mockjax

  1. net.kennychua
    phantomjs-qunit-runner

    src/main/js/
    src/main/test/js/
    false
    src/main/phantomjs

    src/main/lib/

    **/*.js

    test
    test

    I have this setting, but whenever i tried mvn test:
    [ERROR] Failed to execute goal net.kennychua:phantomjs-qunit-runner:1.0.15:test (default) on project phantomjs-qunit-runner: One or more QUnit tests failed -> [Help 1]

    any idea..

Leave a Reply to François Dussert (@usul_) Cancel reply