Unit Testing Custom Code with the Dojo Objective Harness

By on April 15, 2008 10:50 am
Notice: We now recommend the DOH replacement, Intern. Read our posts about Intern for more information.

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
          ®isterModulePath=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
           ®isterModulePath=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:

 
 

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

Comments

  • Pingback: Blue Sky On Mars » links for 2008-04-16()

  • Pingback: Ajaxian » DOH, let me test my code!()

  • Javier

    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?

  • 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.

  • Pingback: SitePen Blog » Creating Asynchronous Tests with the Dojo Objective Harness()

  • 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:

    if (window.registerModulePath) {
    				for (var i in window.registerModulePath) {
    					dojo.registerModulePath(window.registerModulePath[i][0], window.registerModulePath[i][1]);
    				}
    			}
    
  • 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

  • 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

  • Pingback: Unit testing options for JavaScript | Programming and Development | TechRepublic.com()

  • Madeeha

    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.

  • 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

  • kaczmar2

    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.

  • RobinG

    DOH isnt working with firefox 3…

  • RobinG: Could you elaborate, as it has never stopped working for us.

  • Sam

    Can I test an external URL with this tool.
    e.g: yahoo may have some widgets and i want to automate testing it ?

  • Sam

    I am given an external application that has a lot of dojo widgets, and I want to be able to
    – Click on a certain button (s)
    – Click on Submit or Save or Cancel button.
    – go from one frame to another. ( or from one pane to another )

    I do not know the name of the widget ?
    Can I do it with this utility ?

  • Using DOH itself we can call javascript functions, but not (reliably) click buttons and fire their event handlers. That’s the role filled by the DOH robot – to automate interactions. There’s 2-part write up of Automating Web UI Unit Tests with Real User Events:

    http://o.dojotoolkit.org/2008/08/11/doh-robot-automating-web-ui-unit-tests-real-user-events
    http://o.dojotoolkit.org/2008/10/31/doh-robot-part-2-automating-acceptance-tests-and-user-stories

    The docs for doh robot are at:
    http://www.dojotoolkit.org/reference-guide/util/dohrobot.html

    hth,
    /Sam

  • Peter K.H. Gragert

    Link above is dead: uses for unit testing. ?1

  • Updated… though you should probably look at Intern, our successor to DOH. That article is several years old.

  • Thanks for sharing such informative blog!!! LoadRunner is
    very popular tool to validate a software application by generating actual load.
    It gives precise information about the software performance in real time. Loadrunner
    training Chennai

  • vignesh

    Wonderful informative..Congtraz to posted for many information..hadoop training chennai

  • anitha

    This is an awesome post.Really very informative and creative contents.Thanks to sharing these concept is a good way to enhance the knowledge.I like this site very much.I like it and help me to development very well.Thank you for this brief explanation and very nice information.Well, got a good knowledge

  • Krishna Veni