Unit-testing writing styles in QUnit (JavaScript) vs xUnit/MSTest

Every now and then there are people doing experiments with different ways to name tests. The first time I was inspired to adopt a new way to name my tests was after Corey Haines performance kata (no I wasn’t there I saw the video, thanks Gregg). I really enjoyed the executable specification style.

Last year my naming conventions changed again after a few days with Andreas Brink, discussing BDD and the value of naming the tests as specifications, but according to the BDD format: Given, when, then.

Lately this also came up on Twitter for naming MSTest tests, also as a specification style.

QUnit output

A while back I did a little experimenting with Chrome Extensions. I created an Extension that interacts with uTorrent web-gui through a context menu item. While not perhaps the best example, I noticed something interesting as I was writing the tests for it. I use the QUnit framework since it’s the testing framework I first got testing to work for me in JavaScript.

It felt more natural to write Specification style tests in QUnit than the BDD style I’ve been using for a while. Which was quite a surprise.

To the right is an example of the output from the specification style QUnit tests for ChromeTorrent. You can check the code at Github.

Back to the tests. I try to keep my tests in a AAA style. However as I was writing the tests I noticed that QUnit doesn’t really lend it self well to this. Part of it I think is the way it fits to well with the specification style tests.

So let’s look at a QUnit test:

test("When a ChromeTorrent is created ", function() {
	$.ajax = function(settings) {
	settings.success("<div id='token' Style='display:none;'>fakeToken</div>", 200); };
	var torrent = new ChromeTorrent("host", "user", "password");
	ok(torrent != null, "it is initialized without error");
	equals(torrent.getUser(), "user", "it has a user");
	equals(torrent.getHost(), "host", "it has a host");
	equals(torrent.getPassword(), "password","it has a password");
	equals(torrent.getToken(), "fakeToken","it fetches a token from the host");

This test is a specification of what you can expect from a new ChromeTorrent object. The null check is a bit excessive but it’s my way to wind up my mind up for writing tests. However as you can see the way a QUnit test is structured, it lends it self quite well to the specification style of testing. This test fits well with AAA but lets look at the other test:

test("A ChromeTorrent can", function() {
	$.ajax = function(settings) {
		settings.success("<div id='token' style='display:none;'>fakeToken</div>", 200); };
		var torrent = new ChromeTorrent("host", "user", "password"); 
		equals(torrent.createDownloadUrlForTorrent("torrent"), "host?token=fakeToken&action=add-url&s=torrent", "construct the url for starting a download for a torrent");
		var getUrl = "";
		var password = "";
		var username = "";
		$.ajax = function(settings) {
			getUrl = settings.url;
		    password = settings.password;
		    username = settings.username;
	    equals(getUrl, "host?token=fakeToken&action=add-url&s=myTorrent", "add a torrent to the downloads");
    	equals(password, "password", "on a password protected host");
	    equals(username, "user", "with the given user");

We can see here that AAA is broken. To be honest I think of this more as a AAA cycle where I arrange, act and assert in cycles. In essence creating a “test” between each assert.

So why am I breaking AAA here?

Well its because of the output of QUnit, so each test isn’t isolated if you want to have the pretty output when using QUnit. I could improve the situation somewhat by wrapping ajax calls through a client class that I pass to ChromeTorrent, but the broken cycle will still remain.

I could use Modules to make it a little bit better but I am testing the module ChromeTorrent so it wont fit perfectly either.

There is hope! Recently I stumbled upon Jasmine, a JavaScript testing framework that has a similar output. It has better support for specification style testing, and it can be run using JSTestDriver, that was recently recommended to me. I hope to dig more into it as soon as I find some time to exercise my JavaScript Testing.