Unit Testing Custom Code with the Dojo Objective Harness

April 15th, 2008 - by dmachi

Many Dojo developers are aware of the Dojo Objective Harness (DOH) that the Dojo Toolkit uses for unit testing. Many people, however, want to use DOH for testing their own code or even non-Dojo code that they have written. While DOH has always supported this, there currently aren’t many examples of doing so. Let’s see if we can help that out.

Out of the box, DOH has supported custom code since the beginning. Tests can include any custom namespaces or code by easily using standard JS techniques. Coming to an understanding of how custom namespaces work with Dojo and how and when DOH loads tests will help us illustrate how you can take advantage of DOH in your own code.

How do custom namespace lookups work in Dojo?

Dojo’s system for using custom namespaces is quite simple to understand and use. Dojo maps the top level namepaces (ie, ‘dojo’ or ‘company’) relative to the location of the dojo.js file. The dojo namespace itself is mapped to the directory where dojo.js is located. The other major Dojo projects that are distributed with or alongside of the toolkit are typically located at ../module. Let’s look at a Dojo checkout:

dojotoolkit/
    dojo/
        dojo.js
    dijit/
    dojox/
    util/
 

If we wanted to include custom namespaces with the least amount of effort, adding additional modules inside of the dojotoolkit directory above would suffice. Dojo would automatically resolve any mappings to this location. For example if you dojo.require("company.widget.foo"), Dojo will attempt to load the foo resource from dojotoolkit/dojo/../company/widget/foo.js. While this is easy, it often means that you would have to intermingle your own code with the Dojo source which can be difficult to upgrade and/or use svn:externals with. We would prefer a structure that doesn’t intermingle code. The common structure is something like this:

js/
    dojotoolkit/
        dojo/
        dijit/
        dojox/
        util
     company/
        moduleA/
        widget/
 

Unfortunately, when you dojo.require("company.widget.foo"), you would get an error because it is looking, by default, in the js/dojotoolkit/dojo/../company directory and not finding anything except a 404. The solution to this, in normal code, is to use dojo.registerModulePath.

dojo.registerModulePath("company", "../../company");
 

This solves the location problem, and forces the module’s root to be located at ../../company relative to the dojo.js file.

What’s the problem then?

If DOH can support custom code and Dojo provides a mechanism for registering new namespaces, then everything should be ok, right? Close, but not quite. Not only is the code loaded by using the Dojo package system, but so are the tests. Before your test code can be run (where it can use dojo.registerModulePath), the DOH is attempting to load your tests modules using the loader. If your test code is located in js/dojotoolkit/company/ this will be fine because Dojo would automatically look in ../company for that namepace. As mentioned above, however, that is not ideal, so we put our code in js/company instead, which of course, Dojo fails to automatically recognize. At the same time, there is no simple way to register those module paths so the custom namespace can be recognized. Tests are launched by naming the tests that you want to run in the URL. If there is no matching resource for a provided test package, it will fail.

How are tests launched?

Typically, there is an HTML file that is simply a redirect to DOH’s runner.html with a single query parameter, testModule, that defines what resource the runner should load. For example, for dojo.rpc, in the dojox/tests/ directory, there is a runTests.html file that serves this purpose. This file can exist anywhere, so long as it redirects to the runner.

Let’s take a look at the redirect:

<meta http-equiv="REFRESH" content="0;
     url=../../../util/doh/runner.html?testModule=dojox.rpc.tests.module">
 

Of course if we’re testing our own code, we want to include our module instead

<meta http-equiv="REFRESH" content="0;
     url=../../../util/doh/runner.html?testModule=company.tests.foo">
 

Without any additional data, runner.html will be unable to locate company.tests.foo to load this resource. To solve this problem, last week I introduced an additional query parameter, registerModulePath, that can be passed to the runner. Additional modules specified here will be registered prior to loading the test files, which allows tests in custom namespaces to work. The above example would be rewritten as:

<meta http-equiv="REFRESH" content="0;
    url=../../../util/doh/runner.html?testModule=company.tests.foo
          &registerModulePath=company,../../company">
 

While I don’t believe it is really necessary, you can register multiple namespaces by separating them with a semicolon. (Note: this additional parameter is currently only available in SVN, however for those of you who wish to use this with an older version of Dojo, copying the current DOH code over the old code should present no problems).

What about non-Dojo code?

The above examples are great if you have a Dojo project and you are following the common patterns seen when developing and deploying Dojo. If you want to write tests for your non-Dojo code you can too, though you still have to follow Dojo/DOH semantics when writing the tests themselves. Let’s look at an example project directory structure.

/project
   index.html
   foo.html
   js/
   css/
   images/
   tests/
 

As you can see this has no relation to how a Dojo project is normally structured. And for this project we don’t have any need for including Dojo in the actual running code, but we’d still like to take advantage of the tests. Here is a simple way to do so without changing directory structure. Include the Dojotoolkit in your tests directory so it looks like:

/project
    tests/
        dojotoolkit/
            dojo/
            util/
                 doh/
 

Since we don’t want to redo our directory structure to include the tests, we need to map the root of our project relative to dojo.js as its own module. For example:

<meta http-equiv="REFRESH" content="0;
     url=../../../util/doh/runner.html?testModule=project.tests.module
           &registerModulePath=project,../../../..">
 

We then put our test, modules.js, inside of the tests directory as you would expect. Our runTests.html goes in this same directory as well, so from the top level we don’t really have to follow the typical Dojo pattern. A user or developer of ‘project’ can just go into the tests directory and load runTests.html.

What’s in the test file itself?

The test file is structured just as any other DOH unit test is and needs to include dojo.provide("project.tests.module") at the top to satisfy packaging requirements. The typical pattern is to define dojo.require()s for individual groups of tests in the module.js file and then the individual tests would be included in those required resources, but this is by no means a necessity. Here is an example module.js done this way:

dojo.provide("project.tests.module");

dojo.require("project.tests.functions");
dojo.require("project.tests.features");
dojo.require("project.tests.errorHandling");
 

An individual test file can vary drastically in structure, but I would recommend reading the Dojo Book for an example of some of these tests. A simple test might look like this:

dojo.provide("project.tests.functions");
 

if you are using Dojo already, something like this can include your code

dojo.require("project.someModule");
 

If not, you might want to include code like this, or you might also want to look at URL based tests below. This is relative to the runner.html file in util/doh.

dojo.io.script.get({
       url:"../../../../someModule.js"
});
 

The tests themselves:

doh.register("project.tests.TestGroupA",
       [
               {
                       name: "My Function Test [_myfunc()]",
                       timeout: 4000,
                       runTest: function(){
                              var result = _myFunc("a", "b");
                              doh.assertEqual("Foo", result);
                            }
                 }
   ]
);
 

That’s not too bad. You can also create a ‘normal’ HTML page with your code and your tests included. This is especially useful in the non-Dojo case. Inclusion simply uses registerUrl instead of register:

doh.registerUrl("project.tests.TestGroupB",
     dojo.moduleUrl("project.tests","testGroupB.html")
);
 

This will load the file in the project/tests/testGroupB.html file. Tests can be included in this page for execution with results being reported back to the DOH. You might include something like this on the test page:

<script type="text/javascript" src="runner.js"></script>
<script type="text/javascript">
   doh.register("project.tests.TestGroupB", [
               function(){
                       doh.assertEqual("FOO", someObjectOnMyPage);
                 }
   ]);
</script>
 

All in all, tests are pretty easy to write this way once you figure out the pattern. I hope this helps get you started.

Bookmark and Share

12 Responses to “Unit Testing Custom Code with the Dojo Objective Harness”

  1. [...] SitePen Blog » Unit Testing Custom Code with the Dojo Objective Harness Dustin talks about using DOH to test code, even code that doesn’t use Dojo. (tags: javascript test) [...]

  2. [...] Machi has posted on DOH, the Dojo Objective Harness which is a testing framework for [...]

  3. Javier says:

    The function dojo.registerModulePath was only recently checked in, so it is not available in the currently available 1.1 release. Do you know when this will be incorporated into a Dojo release?

  4. dmachi says:

    dojo.registerModulePath has always been available as part of Dojo. It is only the registerModulePath query option to runner.html (part of DOH) that didn’t have this option. This portion will be available in the 1.2 release of Dojo. However, little else has changed and so if you need this capability with an older version, simply download the newer DOH and copy it over your current one. This will have no affect on the stability or usability of the Dojo libraries themselves.

  5. [...] my previous post on the Dojo Object Harness (DOH), I discussed how to write unit tests for custom code in custom [...]

  6. Maulin Shah says:

    actually, even the current trunk of runner.html does not fully implement registerModulePath. it reads it from the querystring, which is a start, but does nothing with it. i had to add:
    [code]
    if (window.registerModulePath) {
    for (var i in window.registerModulePath) {
    dojo.registerModulePath(window.registerModulePath[i][0], window.registerModulePath[i][1]);
    }
    }
    [/code]

  7. dmachi says:

    Maulin,

    Thanks for the reply. You dont’ ened to do this in runner.html, since runner.html includes runner.js which contains that code.
    This happens around line 46 of runner.html.

    Thanks,
    Dustin

  8. Maulin Shah says:

    Dustin,

    Aha! I figured you couldn’t have overlooked that! But when I copied from the svn source, I only copied runner.html. I guess I needed to copy over both runner.html and runner.js to my 1.1.1 source folder. I should have known that!

    Thanks again,

    Maulin

  9. [...] The Dojo Toolkit also provides a robust unit testing environment. Its testing tool is called the Dojo Objective Harness; it facilitates the use of custom namespaces but requires special steps to utilize the test runner included within the Dojo Toolkit. Dustin Machi’s SitePen blog post provides instructions for making the Dojo Objective Harness work. [...]

  10. Madeeha says:

    I am using dijit robotx and using doh.robot.initRobot(myAppUrl) within my test page to run tests against my actual app. I load this page up in DOH runner. It is working except after a really short amount of time, it gives me a timeout error. I ensured my timeouts for the tests were long, but it still happens. Is there some special timeout rules when using DOH runner with dijit robotx tests?

    According to this page: http://www.dojotoolkit.org/2008/10/31/doh-robot-part-2-automating-acceptance-tests-and-user-stories it makes it seem like you can’t run dijit robotx tests within runner – but I was able to get this far.

  11. Barton says:

    I just started learning Dojo and want to run some tests from the command line with DOH. I found an article at IBM DeveloperWorks and this one but neither address running with the command line. I’ve tried providing the dojoUrl and testModule and I get the error Could Not Load and, as this article says, the wrong directory path. I looked at runner.js and the registerModulePath is only applicable with the browser. How are others running command line tests that are similar to the directory structure presented here?

    Thanks,

    Barton

  12. kaczmar2 says:

    Since each modulePath name/value pairs are separated on the querystring with a semicolon (;), you can run across a quirk in IE. IE will truncate the URL contained in a META REFRESH tag if it contains a semi colon. As a result, this will not work:

    Dojo Unit Test Runner

    Redirecting to D.O.H runner.

    Note how the URL will get truncated after dojo/myModule1.

    You must do something like this:

    Dojo Unit Test Runner

    function loadRunner() {
    var url = “../../util/doh/runner.html?testModule=myModule.tests.module,myModule2.tests.module&registerModulePath=myModule1,../../dojo/myModule1;myModule2,../../dojo/myModule2″;
    window.location = url;
    }

    Redirecting to D.O.H runner.

    Good article. Keep up the good work.

Leave a Reply