One of the things I like about writing tests first is that it makes me write code which is easy to work with. Of course this depends on your definition of easy, so I’ll give an example.
A colleague of mine wrote a small framework to help work with Azure service bus. It takes care of the details of interacting with the service bus with the aim being that developers need only write a handler to handle particular message types.
The client framework sets a message type property on the message when it is sent and then the receiver is able to use this to locate the appropriate message handler.
I set about writing my first handler and soon found myself wondering how I could inject dependencies into it. The source code for the framework wasn’t available so I opened the dlls with dotpeek to see how the handlers were resolved and instantiated. I discovered a factory which created handlers by newing them up. Fair enough, I thought, I’ll just write my own factory that uses Castle Windsor to create instances and everything will be good.
Except I couldn’t because the service bus framework invoked the handler factory statically. In other words the code which needed the factory was tightly coupled to a particular implementation. I couldn’t tell it to use a different implementation without changing the source code which unfortunately wasn’t an option.
Doing TDD has taught me that static invocations are often the source of tight coupling. I can’t mock out the static dependency. I can only mock out an instance. When I write my test first I know this and so I would choose to specify the dependency as a settable property. This means that the framework can start up with the default factory implementation which I can then override with my version that uses Castle to provide the handler instance. As a side note, in this particular scenario I have to use property injection rather than constructor injection. The service bus framework doesn’t use dependency injection and I’m not able to change that. It doesn’t know about my factory implementation. It is the framework and not me that instantiates the component that needs the message handler factory so I have no control over the constructor arguments anyway.
Because I’m in the habit of writing test first using mocks I know that my dependencies are inverted and can be injected. Often I’ll only use either a default implementation or a mock but inverting my dependencies gives me scope to use different implementations in the future and it doesn’t come at a cost.
We eventually tried a couple of approaches for the handlers in question. I wrote some fairly stinky initialisation code within the handler that obtained an instance of the class from a Castle and then delegated the handler’s single method call to the injected instance. We didn’t really like this as it introduced complexity just so that we could use Castle. So we decided to drop Castle. The handler was simple so we could just new up dependencies and wire them by hand. Or so we thought. Over time things became more complex and we had a bunch of setup code in the handler which became the thing which set up a ‘controller’ dependency which did the actual work. Had we used a Handler factory that used Castle we wouldn’t have had to write any set up code and the handler could have kept its role as the thing which handled the message.
What prompted me to write this is the idea I encounter fairly frequently that you shouldn’t write tests for implementation details. Typically a BDD style test is there to verify behaviour. Its considered a bad thing to have this test verify mock invocations as this ties the test to a particular implementation. You should be able to vary the implementation without all the tests failing. Perfectly reasonable and I do agree. However I also think that you should write mock based implementation detail tests ahead of the implementation. If you can do that I think you will end up with cleaner code. If you need to refactor the implementation details do that test first too. If you want to try out a completely different implementation then do that test first and if you like it feel free to throw away the old implementation along with its tests. Keep BDD style tests for verifying behaviours. These should not break if you vary the implementation. But also write TDD tests using mocks to help guide your implementation. These are a great (but by no means the only) way to ensure you write clean code – assuming you can write clean tests. Knowing the distinction between these two types of test is really important as is including both types of test in your test suite.