Friday, February 11, 2005

Unit testing

Everyone knows that unit tests are important. Besides providing a fine grained testing approach, they also help in asserting that the critical functionality is not broken after refactoring. Of late, I'm facing a few problems while writing Unit tests.
While adding some new functionality to the application, I had to modify an already bloated method (approx 80 lines of code) and add a few more lines of code to it. My pair and I didnt want to do this for 2 reasons :- Firstly, the method does too many things by itself and hence becomes unreadable. Secondly, its a nightmare to test such a big method. So we refactored it into a couple of smaller methods and implemented the new functionality. All goes well !!
Not so soon !! We then decided to test the new method ( yeah... we didnt do TDD !!). The method takes a data object as a parameter which has more than a dozen of fields in it. So we created a stub of the data object which implemented only the required getter and setter methods. When we passed this stub as the argument, the test failed !! Apparently the method used some Utlility class to convert the data object to a Domain object (which obviously required the data object stub to implement most of its methods). So now what ??
We moved the call to the Utility class to another method. It looked something like this --

Class foo{
public void method(DataObject data){
//do something;
public DomainObject convert(DataObject data){

Now to test this method we subclassed "foo" and overrode the "convert" method such that it returns the Data Object with only the required members set. The test passed.
I am not impressed with this style of testing though. What was the whole point of the test ?? At the end we were just testing a proxy implementation instead of the actual implementation. Is this the only way to test such methods? If you are aware of a better way to test such methods, kindly post a comment about it :-)


Nikhil said...

Boo Hoo Hoo :(
Is this what working in the software sector look like?Lots of code?Or is there more to it??

David Kemp said...

An alternative is to modify foo so that it delegates the conversion to another object that is set either in foo's constructor or via a setter.

Your test code can now initialize foo with a mock converter. Your production code could either initialize foo with a real converter, or foo could have a default constructor that initializes the converter to the real converter.

Just another case of favouring composition over inheritance.