Medryx Observations

June 9, 2008

Building (and Testing) Dojo Widgets — MVC?

Filed under: dojo, javascript, oop, unit testing — Tags: , , , , , — maulin @ 8:04 am

There are plenty of tutorials out there about Dojo widgets, so I won’t write another one. But I would like to document here my philosophy for writing widgets, allowing for separation of concerns and perhaps even an MVC architecture.

In my mind, a widget is a hybrid of view component and model component. It builds a UI, then receives events on that UI that it passes on the model. But this can get messy, especially for testing, debugging, and changes later on down the road. So I propose that we separate the widget into its elemental components, and then only combine them in the “final widget”. Here’s what I mean:

Lets create widget called TeamList, that creates a list of teams. When you click on a team you get a dialog with team details.

/widgets
---/ui
------/tests
---------/all.js
---------/runTests.html
---------/TeamListTest.js
------/_TeamList
---------/TeamList.html
---------/TeamListView.js
---------/TeamListController.js
---------/tundra.css
------TeamList.js
---/themes
------/tundra.css

As you can see, I actually seperate my widgets into several files:

  1. TeamList.html: the standard widget template
  2. TeamListView.js: a class that DOES NOT derive from the usual _Widget or _Templated, but that “pretends to”.

    This class is used to manipulate everything related to the UI, and to create the events that the widget will generate (or translate the low level user events to more useful ones for the application).

    The key is that none of the “high-level” events will trigger ANYTHING to happen in the model. That is the job of the controller.

    This makes this class quite isolated, and fairly easy to test and change, as long as it adheres to its contract of high-level events that it will trigger.

    Here is the code for my TeamListView:

     
    dojo.declare("medryx.widgets.ui._TeamList.TeamListView", medryx.AbstractBase, {
        //*** attributes
        //filter the teams
        filter:null,
     
        //sort the teams. this can be an array of sort objects (see AbstractDao.sort)
        //or can be a comma-seperated list of fields (simpler, but less flexible)
        sort:null,
        headerContent:"",
    	footerContent:"",
     
        //*** internal
        templatePath: dojo.moduleUrl("medryx","widgets/ui/teamList/teamList.html"),
        controller:null,
        teams:null,
     
        //** nodes defined in the template -- only defined here so we remember them
        list:null,
     
        /**
         *  abstract method
         *  MUST return a valid initialized controller
         */
        $buildController:null,
     
        postCreate:function() {
            this.controller = $buildController();
            this.loadTeams({
                filter: this.filter,
                sort: this.sort
            });
        },
       /**
         * delegate to the controller.
         */
        loadTeams:function() {
            var deferred = this.controller.loadTeams(arguments);
            deferred.addCallback(dojo.hitch(this, this.displayTeams, this.list);
        },
       displayTeams:function(list, teams) {
    	dojo.forEach(teams, function(team) {
    		var li = document.createElement("li");
    		li.innerHTML = team.getName();
    		list.appendChild(li);
    		dojo.connect(list, "onclick", dojo.hitch(this, "teamClicked", team));
    	});
       },
       teamClicked:function(team, event) {
       }

    So lets point out some interesting things about this “View”. Notice that it does not do anything about actually loading teams, or handling what happens once a team gets clicked. It just translates the low level click event to a high level teamClicked event that passes the team of interest to the event.

    Next, notice that while we implement postCreate(), we are not inheriting (yet) from _Widget or _Templated. Here’s why. Lets say I want to test this class. If it had already inherited from these classes, it would automatically call the lifecycle events of the class. But I don’t necessarily want that to happen… yet. I want to call my postCreate method manually and run my tests to be sure everything in this class is working right, before I start wiring things up.

    One other small point — look at displayTeams. Notice how I actually pass the list to which I want to attach the items to this method. Obviously I could use “this.list” throughout displayTeams, and then I wouldn’t have to pass the argument. But stylistically, I find it easier to write tests for methods that are “self contained” and have as little reference to “this.*” as possible. If I hadn’t passed the list as a parameter, I would have had to remember in my tests to set this.list to a new list before running my test. And you can easily forget to do that. In other words, I try to make my widgets as “stateless” as possible in the View. I like to let the final widget inject all the state that is necessary.

    Here is the (very simple) controller class:

    dojo.declare("medryx.widgets.ui._TeamList.TeamListController", null, {
        view:null,
        constructor:function(view) {
            this.view = view;
            dojo.connect(view, "teamClicked", this, "pageTeam");
        },
     
     /**
      * return a deferred that will callback for all teams
      * that are to be display. the arguments are filter and sort
      * either as parameters or as kw arguments
      */
        loadTeams:function() {
            return medryx.dao.TeamsDao.getAll(arguments);
        },
     
        pageTeam:function(team) {
            var pageForm = new medryx.widgets.ui.PageForm();
            pageForm.setTeam(team);
            pageForm.show();
        }
    });

    This is where I connect this widget to other layers of the application. So all interactions outside of the widget should occur through this controller. Notice how I connect up the “teamClicked” event to a “pageTeam” function. Another advantage of separating the view from the controller is that allI have to do is a use a different controller, and I can get a totally different widget. Again, this is a fairly easy class to test .

    So where does this all come together? In medryx.widgets.ui.TeamList, of course!

    dojo.declare("medryx.widgets.ui.TeamList", [dijit._Widget, dijit._Templated, medryx.widgets.ui._TeamList.TeamListView], {
    	$buildController:function() {
                 return new medryx.widgets.ui._TeamList.TeamListController	(this);
              }
    });

    All this does is create the actual widget class, mixin the _Widget, _Templated to allow the magic of dojo to take over, and to define the controller that will be used.

Simplified Dojo D.O.H. Testing

Filed under: dojo, javascript, oop, unit testing — Tags: , , , , , , — maulin @ 7:19 am

In my last post, I talked about how to use Dojo’s DOH for unit testing. But the more I wrote about it and used it, the more I realized that there were a couple of things that made it difficult for me to wrap my head around.

First, setUp and tearDown were not similar to their counterparts in other unit-testing frameworks, such as JUnit.

Secondly, the whole model for programming an asynchronous test seemed a bit confusing.

So I have come up with what seems to me to be a ridiculously simple solution — a custom “test fixture”.

As you recall, when registering a set of tests, you pass the name of the set followed by an array of test methods or test fixtures. The methods are simple function calls. The test fixture had the following properties:

  1. name
  2. runTest
  3. timeout
  4. setUp
  5. tearDown

In all the examples I found, this was declared as an associative array, setting each of these properties.

So a typical test looked like:

doh.register("some.test.package", [
function testOne() {
},
{
	name:"test2",
	setUp:function() {
		doSomeSetup();
	},
	tearDown:function() {
		doSomeTearDown();
	},
	runTest:function() {
		doh.assertTrue(doSomethingTrue());
	}
},
{
	name:"test3",
	setUp:function() {
		doSomeSetup();
	},
	tearDown:function() {
		doSomeTearDown();
	},
	runTest:function() {
		doh.assertTrue(doSomethingElseTrue());
	}
}
]);

But what if instead we write a class that implements all the above properties? Then we could subclass that class for a particular set of tests that shared a setUp/tearDown series. So in my first cut, I wrote a simple TestFixture class that looks like this:

dojo.declare("medryx.util.test.TestFixture", null, {
	constructor:function(testName, test) {
		this.name = testName;
		this.runTest = test;
	}
});

Okay, so its not rocket science. But already I feel like it cleaned up my tests substantially. Because now I have:

dojo.declare("some.test.fixture", medryx.util.test.TestFixture, {
	setUp:function() {
		doSomeSetup();
	},
	tearDown:function() {
		doSomeTearDown();
	}
});
 
doh.register("some.test.package", [
function testOne() {
},
new some.test.fixture("test2", function() {
	doh.assertTrue(doSomethingTrue());
}),
new some.test.fixture("test3", function() {
	doh.assertTrue(doSomethingElseTrue());
})
]);

Well that’s a start. But that got me thinking. Could I use this to simplify asynchronous tests?

As I discussed in my previous post, there is essentially a template for doing asynchronous testing:

  1. Create a doh.Deferred
  2. Do your deferred thing
  3. Attach the doh.Deferred.getTestCallback(), instantiated with your own assertions, to you deferred thing’s callback/errback
  4. Return the doh.Deferred

So I wrote a new and improved fixture:

/**
 * Use this test fixture for asynchronous tests.
 * 
 * @param {Object} testName
 * @param {Object} asyncTest: a method that ALWAYS returns a Deferred (dojo or doh)
 * @param {Object} assertionsCallback: a method that you will use to test your assetions once your defered is done
 */
dojo.declare("medryx.util.test.AsyncTestFixture", null, {
    constructor:function(testName, asyncTest, assertionsCallback) {
        this.name = testName;
        this.asyncTest = asyncTest;
		this.assertionsCallback = assertionsCallback;
    },
	runTest:function() {
		var d = new doh.Deferred();
		var actualDeferred = this.asyncTest();
		actualDeferred.addBoth(d.getTestCallback(dojo.hitch(this, "assertionsCallback")));
		return d;
	}
});

So now my asynchronous tests derive from this AsyncTextFixture and are created as:

	new subclass.of.AsyncTestFixture("testName",  function() {
		var def = doSomethingDeferred();
		return def;
	}, function(result) {
		doh.assertEqual("expected", result);
	});

I just think its easier to “remember” how to do aync testing with this fixture. Thoughts?

June 8, 2008

Dojo D.O.H. Unit Testing

Filed under: dojo, javascript, oop, unit testing — Tags: , , , , , — maulin @ 7:23 am

I have been spending the last few days experimenting with Dojo’s DOH for unit testing my javascript applications. While it has a relatively steep learning curve, and relatively little documentation, once you figure it out, it is actually quite simple and powerful. But it took me almost a whole day just to get up and running!

There are two great and simple articles about DOH on site pen that I found indispensible in learning DOH. The first talks about the logistics of setting up the tests and running them, and the second talks about how to do tests of asynchronous functions. There is a section of the Book of Dojo dedicated to this as well, but I found it hard to follow.

Setting up your test environment

The first trick to getting DOH running is to understand the “DOH Runner” application. This application lives in your /dojotoolkit/util/doh directory. If you launch it directly, it will run all the dojo unit tests. (This, by the way may be the most confusing part of runner.html. I’ve seen lots of posts about how people can’t figure out how to get runner working. I may work on cleaning this up to make it more intuitive for people to use without any difficult initial setup.) But you want to run your tests? To do this you provide the runner with a querystring argument called testModule that is your package with tests. For example, your url may look like:

../dojotoolkit/util/doh/runner.html?testModule=medryx.dao.tests.all

But that assumes that the module you pass in is a “sister” (i.e. at the same folder level) to dojo, dijit, and dojox. If it is not, then you will need a Dojo 1.2 implementation of runner.html and runner.js. Since that is not available yet, you will have to get a snapshot of the latest nightly build or access these files from subversion. These build in support for registerModulePath.

So lets say you have the following structure:

	/
	---/lib
	------/dojotoolkit
	---------/dojo
	---------/dijit
	---------/dojox
	---------/util
	------/mytoolkit
	---------/medryx

That means that when you call dojo.require(”medryx.something”) you want it to look at a path that dojo does not natively understand. So when loading dojo you have to configure djConfig with modulePaths or by calling dojo.registerModulePath() afer loading dojo. Well if you have this structure, and you are running dojo, then I am sure you know how to do this with dojo. The problem is that runner.html loads and tries to run your tests before you even get a chance to run any of your code. So the runner needs to know about the module paths when it is being called. The solution to this was adding registerModulePath as an option on the querystring for runner, so that now your url would look like:

../dojotoolkit/util/doh/runner.html?testModule=medryx.dao.tests.all&registerModulePath=medryx,../../medryx

So what does

medryx.dao.tests.all

look like? My general practice mirrors that of Dojo — I put a tests folder in every package.
The tests folder has the following content:

  1. tests: each group in its own file (roughly a 1:1 number of test files to classes in the package)
  2. all.js: a simple file that dojo.requires all the tests above
  3. runTests.html file which is a simple meta redirect to runner.html with the appropriate querstring arguments already set. Such as:
    <html>
        <head>
        <title>Dojox Unit Test Runner</title>
        <meta http-equiv="REFRESH" content="0;url=../../../dojotoolkit/util/doh/runner.html?testModule=medryx.dao.tests.all&registerModulePath=medryx,../../medryx"></HEAD>
        <BODY>
            Redirecting to D.O.H runner.
        </BODY>
    </HTML>
    

So now execute runTests.html, and you run all the tests in that package. Hurray! But what does a test look like?

Writing Tests with DOH

To register a set of test methods with DOH to be run, you call doh.register with the name of the test, and an array of the actual tests. The tests in the array can be in one of two forms: a simple function(), or an associative array with the following properties: name, runTest (the test method), setUp, and tearDown — similar to junit. Notice that there is no setUp or tearDown that can be written once and then called from before and after every test (as there is in junit). The setUp and tearDown methods here only apply to the one call to runTest that is contained in that fixture. You can sort of get around this by creating a setUp and tearDown method of your own, as a sparsely populated fixture, and then just fill in name and runTest. For example:

dojo.provide("medryx.dao.tests.TeamsDaoTest");
dojo.require("medryx.dao.TeamsDao");
dojo.require("doh.runner");

dojo.declare("medryx.dao.tests.TeamsDaoTest.fixture", null, {
	constructor(name, runTest) {
		this.name = name;
		this.runTest = runTest;
	}
	setUp:function() {
		//do something before every test you want to run
	},
	tearDown:function() {
		//do something after every test you want to run
	}
});

doh.register("medryx.dao.tests.AbstractDaoTest",
[
	new medryx.dao.tests.TeamsDaoTest.fixture("someTest", function(){
		//do your test
	}),
	new medryx.dao.tests.TeamsDaoTest.fixture("someTest2", function(){
		//do your test2
	})
]);

I personally think that is a more natural use of setUp and tearDown. I mean, if I can only use the setUp and tearDown once, then why whouldn’t I just put it in the runTest method?

So now you have a simple set of synchronous tests. You have a set of assertions supported in doh (though I would like to see more in the future). But everything you need is there: assertTrue, assertFalse, and assertEqual. Note that these DO NOT support a message that is displayed during failure. I think that is a problem — it makes finding the reason for your failed test more work than it has to. I would love to see them extend each of these methods to contain an optional 3rd parameter that is a message that gets displayed when there is a failure.

There is a nifty assertError method that I would like to spend a couple seconds on as well. This assertion takes and expected error (i.e. an Error object — which is another reason to always subclass Error for your own exceptions!) as its first parameter. You then pass a scope, and the name of a function with an array of args, and it will execute the method inside of a try-catch, and if it does not get your expected error, it will fail. This is a little complicated, but here is an example that makes it perhaps a little more simple to understand.

{
	name:"testOneInstance",
	instantiate:function() {
		new medryx.dao.TeamsDao();
	},
	runTest:function() {
	   doh.assertError(medryx.exceptions.SingletonException, this, "instantiate");
	}
},

This will fail if creating a new medryx.dao.TeamsDao() does not return a SingletonException.

Asynchronous Tests

DOH really comes into its own with asynchronous testing. This is certainly not intuitive at first, but after you understand the pattern, it becomes fairly easy to implement. I have actually posted some suggestions to the dojo forum about ways that asynchronous testing could be made simpler with a minimal set of modifications, so we’ll see how that goes. Until then, this is how I suggest you implement asynchronous tests. Before you begin, make sure you really understand Deferred’s!

First, you need to understand the contract you are making with DOH when you run an asynchrounous test. You “mark” a test as asynchronous by having your runTest method return an instance of doh.Deferred. Notice that is NOT dojo.Deferred. DOH has its own Deferred implementation that you must return.

When you return a doh.Deferred from your runTest method, you are agreeing to, at some point run the .callback() or the .errback() methods on the deferred you return. The

d.errback(e)

method should be called with any exceptions that occur in your test, or if your assertions fail. Otherwise, call

d.callback(true)

So to summarize:

To Make Your Test Do This Call This
Pass deferred.callback(true);
Fail deferred.errback(some new assertion error)
Fail in Error deferred.errback(error);

Notice how this is VERY different than the usual methodology for running tests in which you do something, and then make some assertions about the outcome, and the assertions themselves handle telling the test-framework whether the test passed or failed.

So an example of implementing an asynchronous test using this method would be: (crediting Dustin Machi)

doh.register("tests.DeferredExample",[
     function myDeferredTest(t){
           var dohDeferred = new doh.Deferred();
           var expectedResult = "foo";         

           var realDeferred = someAsyncFunction();

           realDeferred.addBoth(function(result){
                    if (result==expectedResult) {
                       //our test succeeded, fire the callback
                       dohDeferred.callback(true)
                    }else{
                       dohDeferred.errback(new Error("Unexpected Return: ", result));
                    }
           });

           return dohDeferred;
    },
    .... more test functions
]);

So can you even you assertions in this? Well, not quite. A failed assertion throw an error. If an error occurs in your test you must call errback. So if you surround your assertions in a try-catch block you can use them. So your new test looks like:

doh.register("tests.DeferredExample",[
     function myDeferredTest(t){
           var dohDeferred = new doh.Deferred();
           var expectedResult = "foo";         

           var realDeferred = someAsyncFunction();

           realDeferred.addBoth(function(result){
                    try{
		doh.assertEqual(result, expectedResult);

                       //our test succeeded, fire the callback
                       dohDeferred.callback(true)
                    } catch(e){
                       dohDeferred.errback(new Error("Unexpected Return: ", result));
                    }
           });

           return dohDeferred;
    },
    .... more test functions
]);

Well that is starting to feel a little more natural, but I’m still not quite satisfied. Its still a lot of glue code, and it still feels to hard to write quickly and easily and reliably. Enter the template method pattern.

doh.Deferred contains a method called getTestCallback that is a template for all of the above glue code. All you pass to getTestCallback is a function in which you perform your testing as usual, and the glue code is handled for you. So the above can be simplified to:

doh.register("tests.DeferredExample",[
     function myDeferredTest(t){
           var dohDeferred = new doh.Deferred();
           var expectedResult = "foo";         

           var realDeferred = someAsyncFunction();

           realDeferred.addBoth(dohDeferred.getTestCallback(function(result) {
		   doh.assertEqual(result, expectedResult);
	});

           return dohDeferred;
    },
    .... more test functions
]);

Now that is not too bad! Create the doh.Deferred, run your asynchronous test, and attach the doh.Deferred’s getTestCallback method (containing all of your assertions) to your asynchronous method’s callback and errback handlers. Excellent!

I hope this helps get you started on the wonderful road of javascript unit testing!

Powered by WordPress