Spoiler worth more than car
Picture by greefus groinks
AttributionShare AlikeSome rights reserved.

In the previous part of this series on retrofitting unit tests I talked about how you can break the unit-test as integration-test pattern by introducing mock objects. Probably the best way to do this is by using one of the many mock object toolkits out there1 . For Java development my preference is for EasyMock although there are plenty of others.

Introducing mock objects can cause their own set of headaches though and when retrofitting unit tests to existing code you’re likely to find a number of reasons why it can prove difficult or even impossible to introduce them.

The first and often hardest problem to get around is that the code to be tested is tightly-coupled with the dependency you’re trying to mock out. If class A uses an instance of class B it’ll be impossible to mock that instance if it’s constructed somewhere inside class A. You can get around this by doing a small amount of refactoring and introducing dependency injection but there could be the suspicion that you’re changing the design of your code to make it testable.

I really like dependency injection and inversion of control as general patterns but I realise that it’s something of a controversial subject. The arguments for and against it are summed up well in Martin Fowler’s excellent piece on the subject and there’s been an ongoing discussion on mockobjects.com about it’s use in unit testing as well. Some of the people I’ve spoken to who dislike DI feel that it breaks basic rules of encapsulation. Personally I think you need to strike a balance between encapsulation and tight-coupling between objects.

Assuming you’re coding to interfaces I see no reason why you can’t inject the implementation to be used without breaking encapsulation. If anything you’ll just be exposing your object’s contract with the rest of the system rather than how it works. To add a bit of DI while avoiding having to change the way the object is used throughout the code you could add a package-scope constructor that sets the mock instance required. By making it package-scope you’re limiting the scope for it to be used outside of the context of a unit test. The drawback is that you’re now adding code purely for testing and your test will be bypassing the constructor used at runtime.

One alternative solution to dependency injection when you need to break tightly-coupled dependencies in unit tests is to use Aspects. You could create an Aspect to intercept invocations on particular methods, creating a kind of pseudo-mock. This article on xprogramming.com has a good description of an implementation of mock objects using aspects. This isn’t something I’ve had a chance to try out but I suspect it has some drawbacks and any implementation may prove complex. If you try it out let me know how you get on.

Apart from tight-coupling between objects there are other problems you’ll come across when introducing mock objects while retrofitting unit tests in Java code. Static and final methods are the enemy of mock objects as are public properties, particularly with something like EasyMock. Statics and finals can’t be overridden and the mock object will have no control over access to public properties. If there are a lot of these in the code to be tested then it can cause problems for your mock objects.

Sometimes though these problems are code-smells and deserve to be refactored as you go. Public properties are the antithesis of encapsulation and direct access should be replaced with the use of getter and setter methods2. Statics often indicate a laziness or poor understanding of object-oriented design while a lot of finals can often be a sign of premature optimisation.

You can get around some of these problems with a slight refactoring of the code under test. Refactoring as you go is part of the basic approach to writing a unit test anyway but when you’re working on existing code you need to do it carefully, especially if you have a limited understanding of what the code is doing in the first place. Your best bet is to write as much of the test as you can, refactor the code a little and write more of the test. Run the test each time you change something to make sure that you’re at least keeping the result of your test consistent with the previous result.

Finally, mock objects are great but not if you have to mock a complex data model. Remember that the purpose of a mock object is to model the behaviour of some external dependency without having it actually do anything. If you find that you’re creating mock object after mock object to model a set of beans you’re heading in the wrong direction. If a complex data model is required you’re better off having a test data model handy that can be dropped into the code under test, perhaps as the return value from a call to an object that is mocking the model’s container. Again there might be a code smell here if an object is essentially a container for some data but has a method that performs some transformation or operation on that data. There may be a case of divided responsibilities and a need to split the class in two.

In the next part of the series I’ll talk about how to react when, having broken your dependencies using mock objects, your new unit test looks hideously complex.

– Fintan


1 – Why re-invent the wheel?
2 – God help you if you’re opposed to DI on encapsulation grounds but are quite happy to expose class properties publicly. You’ll deserve everything you get…



No Responses Yet to “Retrofitting Unit Tests Part 3 – Mock Object Hell”  

  1. No Comments Yet

Leave a Reply