Saturday, 7 April 2012

Dojo: mocking dependencies with DOH

This post demonstrates a way to use the Dojo 1.7 AMD loader to provide mock/stub/fake/dummy artefacts in place of the defined dependencies.

Mocking code

The birds/nest script has a single dependency, birds/warbler:

/* birds/nest.js */
define(["birds/warbler"], function (warbler) {
    "use strict";
    return warbler + " in the nest";
});

The birds/warbler script provides a single string, "warbler":

/* birds/warbler.js */
define([], function() {
    "use strict";
    return "warbler"; 
});

Imagine that instead of just returning a string birds/warbler performed more complex operations, perhaps involving network I/O on resources that wouldn't be available at test time. We aren't trying to test birds/warbler here; we want to test birds/nest. It would be useful to substitute a simpler mock object during the unit test without compromising the design of the birds/nest type.

Using Dojo config paths

The birds/tests/cuckoo script will act as a substitute for birds/warbler:

/* birds/tests/cuckoo.js */
define([], function() {
    "use strict";
    return "cuckoo";
});

A HTML file will be used to load the unit test:

<!DOCTYPE html>
<!-- birds/tests/cuckooTests.html -->
<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script src="../../dojo-release-1.7.2-src/dojo/dojo.js"
data-dojo-config="paths:{'birds':'../../birds','birds/warbler':'../../birds/tests/cuckoo'}"></script>
        <script src="cuckooTests.js"></script>
    </head>
    <body>
    </body>
</html>

The dojo/_base/config paths feature has been used to substitute the "cuckoo" in place of the "warbler".

The DOH test can be in-lined into the HTML but has been separated for the sake of cleanliness:

/* birds/tests/cuckooTests.js */
require(["doh", "birds/nest"], function(doh, nest) {
    "use strict";
    doh.register("birds.tests.nofake", [{
        name: "cuckoo",
        runTest: function() {
            doh.assertEqual("cuckoo in the nest", nest);
        }
    }]);
    doh.runOnLoad();
});

DOH tests in their own HTML files

By loading the DOH test in its own HTML file, it will not affect any other tests that expect birds/warbler in its original form:

<!DOCTYPE html>
<!-- birds/tests/warblerTests.html -->
<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script src="../../dojo-release-1.7.2-src/dojo/dojo.js"
data-dojo-config="paths:{'birds':'../../birds'}"></script>
        <script src="warblerTests.js"></script>
    </head>
    <body>
    </body>
</html>
/* birds/tests/warblerTests.js */
require(["doh", "birds/nest"], function(doh, nest) {
    "use strict";
    doh.register("birds.tests.nofake", [{
        name: "warbler",
        runTest: function() {
            doh.assertEqual("warbler in the nest", nest);
        }
    }]);
    doh.runOnLoad();
});

These HTML tests can be loaded using the require feature:

/* birds/tests/module.js */
define(["doh", "require"], function(doh, require) {
    "use strict"
    doh.register("birds.tests.cuckooTests", require.toUrl("./cuckooTests.html"), 30000);
    doh.register("birds.tests.warblerTests", require.toUrl("./warblerTests.html"), 30000);
});

Note that there can be a performance overhead with this approach as multiple copies of files will be loaded.

Running the tests

Directory structure:

birds\nest.js
birds\warbler.js
birds\tests\cuckoo.js
birds\tests\cuckooTests.html
birds\tests\cuckooTests.js
birds\tests\module.js
birds\tests\warblerTests.html
birds\tests\warblerTests.js
dojo-release-1.7.2-src\< Dojo code >
        

The tests can be run by firing up a HTTP server and using the following relative URL:

./dojo-release-1.7.2-src/util/doh/runner.html?testModule=birds.tests.module&paths=birds,../../birds

End notes

I wrote a little bit about DOH in an earlier post.

1 comment:

  1. You can load your runner.html tests in async mode by adding &async=true to the url

    ReplyDelete

All comments are moderated