Friday, September 21, 2007

Refactoring your tests

Martin Fowler's seminal book on refactoring speaks about the various code smells that you need to watch out for to keep your code healthy. Along with refactoring your code however, it's essential that you refactor your unit tests as well. Here's a nice paper that I found that describes various unit-test smells.

Refactoring and unit tests

It's an established fact that refactoring needs a strong scaffolding in the form of unit tests. If you have a good unit test suite, it gives you that extra boost of confidence to refactor your code, knowing fully well that you are not breaking anything. All this is well documented by several people way smarter than me, but here's an instance where unit tests saved the day for me.

What I had was a class similar to the one below:



public class Foo {
public Foo() {
initThis();
initThat();
initTheOther();
}

public void someOtherMethod() {
}
...
...
}




I figured that the code above might not be the most optimum since all the initXXX() methods were not exactly cheap and calling them eagerly on class construction was not exactly necessary. Therefore, I fired up eclipse and moved the three calls into a single public method init() that could be invoked by a callee before using an instance of foo(). I was happy that I had achieved a neat little refactoring by postponing heavy initialization to when it was needed.



public class Foo {
public Foo() {
//nothing here now
}

/* Call this before you use this instance */
public void init() {
initThis();
initThat();
initTheOther();
}

public void someOtherMethod() {
}
}




But, on running my unit tests for someOtherMethod(), I started getting inexplicable NPEs all over the place. This made me realize that something that happened in one of the initXXX() methods was required for executing someOtherMethod() successfully. That led me to add a call to init() in the constructor - something that wouldn't have come to my notice until somebody had actually run the application and reported a bug. Unit tests saved me a lot of time that I would have had to spend much later.

Agreed, I probably should've redesigned my class to call init() from someOtherMethod(), but that probably wouldn't have been the best choice if all the other methods in my class depended upon init() being called beforehand.