Like many programmers who first heard of concepts of Test Driven Development (TDD), I was like “What the heck? Yet more code to write for something that won’t produce end user output?”. I’ve been puzzling for quite some time to get myself accepting this concept, because Code Complete mentioned it so I thought it must have been a good thing (very great book by the way, every serious programmer should read it).
I sometimes write unit tests and I think unit tests are very useful in some tricky areas. It’s just that I haven’t been motivated enough to try TDD (test first then write code).
Then today I realized how TDD is a natural progression for people writing unit tests. TDD promotes clean OO designs. In short, I can even almost say that “If you can test it, it has good design”.
I came across this realization while trying to write unit tests for this piece of JavaScript code afterwards:
/**
* Existing (bad design) JavaScript
*/
var Gateway = { ajaxGet: function(){}, };
// AccountManager uses Gateway, this is the “class” we want to test in this example
var AccountManager =
{
get: function ( id, onComplete )
{
Gateway.ajaxGet ( “/accounts/” + id , function ( profile ){ onComplete ( profile ); } );
}
}
That’s intuitive enough, huh? As much as I thought it was, until I tried to write unit tests for it using JsMock, which has the typical usage of:
/**
* Typical JsMock usage example
*/
var mockControl = null;
function Worker(){} Worker.prototype = { doWork: function(){} }
function setUp() { mockControl = new MockControl(); }
function testWorker()
{
var workerMock = mockControl.createMock ( Worker );
workerMock.expects().doWork();
workerMock.doWork();
workerMock.verify();
}
With the code I have written, it is impossible to do testing with
JsUnit:
/**
* Attempt to test my existing code with JsMock (in JsUnit)
*/
var mockControl;
function setUp() { mockControl = new MockControl(); }
function testAccountManagerGet()
{
var accountManagerMock = mockControl.createMock ( Gateway );
// ERROR! mockControl.createMock() only accepts a class name,
// but Gateway is a variable name
}
So to make things compatible with JsUnit, I had to rewrote the whole stuff:
/**
* My revised JavaScript compatible with JsUnit
*/function Gateway (){}
Gateway.prototype = { ajaxGet: function(){} }
function AccountManager ( gateway ) { this.gateway = gateway; }
AccountManager.prototype =
{
get: function ( id )
{
this.gateway.ajaxGet ( “/accounts/” + id );
}
}
/**
* JsUnit tests for my revised code
*/var mockControl = null;
function setUp() { mockControl = new MockControl(); }
function testAccountManagerGet() { // OK now, because Gateway is a class var gatewayMock = mockControl.createMock ( Gateway );
var accountManager = new AccountManager ( gatewayMock );
// test fixtures var id = “kizzx2”;
gatewayMock.expects().ajaxGet ( “/accounts/” + id ); accountManager.get ( id );
mockControl.verify(); } The above changes might look redundant at first and not so intuitive. But then I came to think of it, it removed the dependency between my AccountManager class and the Gateway class. I wasn’t quite aware of such anti-pattern until JsUnit/JsMock brought it to my attention.
If I had used TDD in the first place, where I would have to write the tests first, it would have enforced me to use clean OO models to design things. Now I think that’s a pretty solid justification for using TDD.