Breaking Dependencies to Test Code in Java: Irritating Parameter

Whenever I need to add a new feature to some legacy code I attempt to add some tests around the code before I start making changes. This ensures that I don’t break the original purpose of the code. This is not always easy though as some classes and methods can be difficult to get into a test harness. One example I will talk about now is that of the Irritating Parameter, which was pointed out to me in the book http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052. If you haven’t read this book I thoroughly recommend it as it explains ways of breaking dependencies to get classes under test, which I have found can be difficult when working with legacy code and large systems.

When a concrete class is passed as a variable to a method, or a constructor, it can make it very difficult to get the method/class under test. Take the following example:

If we want to add a method to this class then we need to make sure we add tests so we don’t break any current functionality. To do this though we will have to instantiate the CreditValidator in a test environment. The problem is the constructor takes in a JDBCConnection object that is used to connect to a database that is not available to our test environment. As you may have guessed that object is the Irritating Parameter.

Mocking frameworks

You may be thinking a mocking framework, such as EasyMock, would come in handy here as it would allow you to mock the JDBCConnection object and therefore bypass the setupConnection() method with ease. Well you are correct, that would work, but it is not the best thing to do. If you are not in a massive hurry, due to the inevitable looming deadlines we all have, then it would be better to improve the design with some basic refactoring.

The problem with this class is that it is tightly coupled to the JDBCConnection object, which is a concrete class. This means the CreditValidator class must always connect to a database using a JDBCConnection. If, in the future, your boss wants to change the validation of credit so that it uses a different type of connection then the CreditValidator is going to have to change (along with all the tests that should now be in place).

Extract interface

A better solution would be to replace the JDBCConnection with an interface and then pass this into the constructor instead. This would mean the CreditValidator is coupled to an interface and not a specific implementation, which would make it loosely coupled. Your development IDE should have a way to do this for you easily, e.g. in Eclipse, you click Refactor in the menu bar and then Extract Interface and follow the wizard. So once we have done this the code would look like this:

In your tests you will easily be able to instantiate a CreditValidator now and know that setupConnection will not attempt to connect to a database. You have also cleaned up the design so that it is easier to add new features in the future.

There is no silver bullet

Sometimes you cannot extract an interface because the class of the parameter passed in is coming from a library. Take the DynamoHttpServletResponse object in ATG, for example – ATG is an ecommerce framework. This is passed into all ATG servlets and is a concrete class. We cannot create an interface to pass in so we have to come up with another solution to get our method into a test harness. Sometimes we cannot improve the design but we still need to test the code. In this instance we can use a mocking framework or we could just try passing null.

In the code above we can pass null as the request and the response as they aren’t being used in the method. However, they are being passed into the pfh.handleLogin() method and that means we should create a FakeProfileFormHandler and override handleLogin so that it returns true or false based on your test.

Extend and Override

Another technique that can be used to get the CreditValidator under test is called extend and override. With this technique you create a new class that extends JDBCConnection and then you override the setupConnection method so that it just returns. In your test you can now pass in your new class to the CreditValidator constructor and no database connection will be created.

Conclusion

The above examples are only trivial but you should take away from this that there are many ways to get classes/methods into a test harness when there is an irritating parameter. Ideally you should clean up the design to make it more reusable and easier to maintain in the future but this is not always possible so using a mocking framework, passing null or using the extend and override technique will at least help you to break the dependency so that you can test your new feature.