Posted by Sten Hiedel on Jun 17, 2015 TDD, avoid testing implementation details

“Extract till you drop” came up in my twitter feed, I had seen it before but I just quickly re-watched it, and recognised something really interesting.

To give a little background; I was that guy who believed that TDD can greatly improve our ability to refactor code, but I found that some places it just didn’t work for me. Some refactors made the tests break left and right. When the behaviours have not changed, but tests start breaking I know that my tests are coupled to the implementation details. As we know changing code and tests at the same time isn’t refactoring but refucktoring then I have to undo the change to get back to green so I can refactor the tests to allow moving to the new implementation. Sometimes it’s painful, sometimes impossible, so I have no choice but to refucktor and change both the tests and the code at the same time. Not ideal.

The “Extract till you drop” demostraits very well the effectiveness of TDD. At the point where Mathias had a full coverage of the behaviours through the application service and smelly code; I consider that a successfully executed red step when you have smelly code and green tests. Kent Beck has always emphasised that you cannot do two things at once, first make it work and then make it right. If your code doesn’t look and smell as bad as his code at this point then you are probably spending too much time trying to make the code right while in the red stage.

Now the green step, refactoring begins; Mathias makes all these refactors including moving some code from one class to another without touching the test code. This happens all the time when requirements change or when adding features, the code that a moment ago fitted nicely needs to be refactored to allow us to add the new feature.

At the very end of the talk Mathias mentions that some of the tests should be moved because the production code was moved out of the Service class, but he doesn’t have time. I think that by moving the tests he would have moved them into the realm of implementation details and therefore coupled the tests to the implementation details.

What do I consider an implementation detail? I found that it makes a huge difference where you draw the line. In Ian Cooper’s talk “TDD, where did it all go wrong” – if you haven’t seen it you should – he highlights that public application services are actually a very good connection point for your unit tests. Ports and Adapters (Hexagonal Architecture) work perfectly in making the boundary clear. So in short I agree with him and consider everything implementation details with the exception of the public API.

If you think that testing everything though public APIs or application services are too wide of a scope because of all the setup etc. then you’re not alone. I thought that too and had to learn it the hard way after a lot of failures. If setting up your services is painful then you really should notice and not just hide it in a dependency injection container or something similar.

At the end of the day — as J. B. Rainsberger put it — “TDD is an idea and ideas don’t do things … experienced practitioners practising TDD mindfully tend to generate designs more suited to their needs on average, all other things being equal”. I also recommend listening to Greg Young’s Keynote 8 Lines of Code, and then the Rails Conf 2012 Keynote: Simplicity Matters by Rich Hickey whose ideas have made a huge contribution to how I code and test my work.