If it is hard to test, you did something wrong
I've often been asked questions like how would you test this or been told that there cannot be unit test for some code because it is impossible to test. Well, my opinion is that if something is hard to test it is all your fault. You designed it, you implemented it and hence it is your fault it is hard to test.
The situation in which people have the hardest time accepting that it is their own fault is when dealing with external dependencies. They say things like "I'm using this other module that does not provide a nice interface". And then they typically revert to some mocking framework to cleanup their mess. Most mocking frameworks today are powerful enough through reflection or even binary instrumentation that they magically can make untestable code very untestable.
Rather than using powerful tools as my savior, I use abstractions. Whenever I depend on something that is not easy to test and that I do not have the power to change I hide it behind an abstraction; an interface or just a class with a few virtual methods I can override in my tests. Is this abstraction just another level of indirection? I do not believe so. Isolating dependencies makes it possible for me to implement something the way I want it to be and then deal with the behavior of the dependency separately. Here is an example from real life:
I needed to implement a feature that depended on two components that really didn't lend them selves for easy faking. So I created two new interfaces representing the functionality I needed for my feature. As I started to integrate the first component it became clear that the interface I needed was not provided by the dependency in an easy way. I needed to put some logic in there to make it work the way my feature expected it. So I created a new interface representing the dependency functionality and made that a dependency of a class that added the logic needed to transform dependency results into what my feature needed. I did not expect this but it was easy given my design and the fact I did not know exactly how the dependency worked in the beginning did not stop me from being productive. My second dependency turned out to behave the way I expected and the interface I invented could easy be used to wrap the second dependency.
The key is that the wrapper for each external dependency should be thin. Preferably one-liners so there is no need to test them. That does not mean the method signature needs to be the same as your dependency. I often make my wrapper signature simpler than what the dependency actually needs.
And remember; if it is hard to test - You did something wrong!
Comments
Anonymous
June 17, 2013
Well done, couldn't have said it better myself!Anonymous
August 02, 2013
Thanks for blogging, I was looking for a good explanation on the benefits of wrappers for testing :) Now I don't have to write one.