A theory about testable web UI IV: YUI Test (plus a bit of mocking)

Feb 16, 2009 08:26

So, last time, I was trying to write a test around requesting some JSON data from a service, and return it. Unfortunately, JsUnit is not up to the task of handling asynchronous logic at all, so I decided to ditch it in search of greener pastures.

Having spent some time working with YUI in the past, I settled on trying YUI Test out.

It took some finagling, but I successfully managed to convert my first test over to the new testing framework. It's got quite a lot of boilerplate, so I won't inline it, but you can see it here.

Now that we've regained lost ground, time to try what we were doing before. Here's my test:

testCanRequestJson : function() {
var self = this;
asyncRequestJson('getStats.json', function(result) {
self.resume(function() {
Assert.areEqual('andy', result.name);
Assert.areEqual(9999, result.hit_points);
Assert.areEqual(1, result.kickboxing);
Assert.areEqual(99, result.linear_algebra);
Assert.areEqual(99, result.cross_stitching);
});
});

self.wait();
},
The way it works is that the "self.wait();" call tells the testing framework to wait until a corresponding "self.resume(fn)." If the resume doesn't come within a few seconds, the test fails. It's a bit ugly, but it does the job admirably, and I don't know how you'd do it better without fibres or coroutines or something, so I'm not about to complain too loudly.

This is great, as it demonstrates I can interact with the network in a sensible way, but it's kind of annoying that I have to have an extra data file just to make the test run. I think it's fine for a functional test like this, but I don't want to have to write functional tests for everything.

The next step is to plug these values into my widget. Here's my test:

testWidgetIsPopulated : function() {
var grueStats = {
name:"Grue",
hit_points:1,
kickboxing:1,
linear_algebra:0,
cross_stitching:99999
};

var net = new FakeNetwork();
var sw = new StatusWidget('avatar-1',
function (url, onComplete) { return net.asyncRequestJson(url, onComplete); }
);

net.completeRequest('getStats.json', grueStats);

Assert.areEqual("Grue", sw.getName());
Assert.areEqual(1, sw.getHitPoints());
Assert.areEqual(1, sw.getKickboxingScore());
}FakeNetwork? Whassat? It's this:

function FakeNetwork() {
this.requests = {};
}

FakeNetwork.prototype = {
asyncRequestJson : function(url, onComplete) {
this.requests[url] = onComplete;
},

completeRequest : function(url, result) {
this.requests[url](result);
delete this.requests[url];
}
};Simple, yes? This way, I don't have to actually have a JSON service for every little thing, and I don't have to worry about asynchronous results or network funkiness. Win!

Tomorrow, I will do some badly-needed refactoring, and lay out what the future direction of the design should be.

Source code

code

Previous post Next post
Up