A simple DOH test fixture

To make unit test writing a useful and painless exercise, you need efficiency and repeatability. In this cookie I’ll introduce a simple test fixture class for use with Dojo’s unit test harness (DOH) that will mean less typing for you, and get you thinking about how to structure and write useful unit tests for your javascript.

You can define a test for DOH in two ways:

  1. A simple function to run, the function name is used as the test name
  2. A fixture (typically a dictionary object) that has an optional setUp, tearDown methods, and a runTest method that is the meat of the test. You can also name the test with a name property.

So, writing a unit test for DOH looks a bit like this:

doh.register("tests.somethingBasic", [
  function testSomething(t) {
       // assert that the something variable is truthy
    t.t(something); // or t.assertTrue
  }, 
  {
    name: "testSomethingElse", 
    setUp: function() {
      // do some setup before the test runs
    }, 
    runTest: function(t) {
       // assert that somethingElse is truthy
       t.t(somethingElse); 
    },
    tearDown: function() {
      // do some tearDown after the test runs
    }
  }
]);

The ability to do setup is of marginal value, if you have to write it out all over again for every test. And indeed if you look at the dojo test suite, you’ll mostly find the simple test function style being used, with any “setup” just being the first statements in that function. But, make that dictionary object a class instance, and you can define your setUp and tearDown once, and just spin off new instances for each test:

(function() {
	var _count = 0;
	dojo.declare("myns.tests.TestFixture", null, {
		name: "",
		timeout: 2000, // 2 seconds.
		setUp: function(props){ },
		constructor: function(name, props){
			if(!props) {
				props = name;
			}
			if(typeof props == "function") {
				var fn = props;
				props = {
					runTest: fn
				};
			}
			if(!props.name) {
				props.name = name || "test_" + _count++;
			}
			dojo.mixin(this, props);
			return this;
		},
		tearDown: function(){}
	});
})();

This construct gives us the ability to define tests like so:

doh.register("tests.MyThinger", [
  new test.TestFixture( "testSomething", function (t) {
       // assert that the something variable is truthy
    t.t(something); // or t.assertTrue
  }),  
  new test.TestFixture( "testSomethingElse", function (t) {
       // assert that somethingElse is truthy
       t.t(somethingElse); 
  })
]);

Here’s some closer-to-real-life usage. First extend the fixture class so it has the setUp we want to execute before each test:

dojo.declare("tests.MyThingerTestFixture", [tests.TestFixture], {
	setUp: function() {
		this.instance = new MyThinger();
	}
});
// make a shortend alias to save keyboard wear and tear
var TF = tests.MyThingerTestFixture;
 
doh.register("tests.MyThinger", [
  new TF( "testInstantiation", function (t) {
       // assert that the instance was created ok, and is what we think it is
      // note: 'this' here is the fixture itself
    t.t(this.instance);
    t.is("MyThinger", this.instance.declaredClass);
  }),  
  new TF( "testDoFoo", function (t) {
       // assert that doFoo does foo
       t.is("foo", this.instance.doFoo()); 
  })
]);

So far we’ve just been passing in a function to the fixture constructor, but we can also pass in a dictionary object (or parameter object/property bag if you prefer) that can customize the instance in the normal way:

doh.register("tests.MyThinger", [
  new TF( "testRender", {
    setUp: function() {
      // do the setUp as normal
      this.inherited("setUp", arguments);
      // ..and create an element we can render into  
      this.targetElm = dojo.create("div", null, dojo.body(), "last");
    },
    runTest: function(t) {
      // imagine the render method puts stuff into the element it is given
      this.instance.render(this.targetElm); 
      // assert that the target element got its payload
      t.is("foo", this.targetElm.innerHTML);
    },
    tearDown: function() {
      dojo.destroy(this.targetElm);
    }
  })
]);

This way you need only code what is unique about each test. If a series of tests (or maybe just more than one?) are “unique” in the same way, you can easily declare a new fixture subclass to house the code that would otherwise be duplicated.

These have been simple examples. The value of the fixture class increases at least in proportion to the complexity of your test. It doesn’t do the work for you, but it does provide a path to DRY-ness. I’ll leave you with one final wistful thought:

doh.register("tests.MyThinger", [
  new TF( "testClarityOfIntent", {
    description: "If only all tests had a description that indicated " 
      + "what they were actually supposed to be testing",
    runTest: function() {
        t.t( this.hasOwnProperty("description") && this.description.length > 10 );
    }
  })
]);