Roy has a pretty thoughful post on the barrier to entry for most developers with Test-Driven Development. I hope I am not doing Roy a disservice by summarizing it as: we have made TDD unapproachable to many by making it more and more complex.
I would agree with many of the points that Roy makes about returning to the basics when approaching TDD. I still find Kent Beck’s book captues the essence of TDD. It also contain wisdom that when I return to it makes me realize that much of what I assumed were new ideas were all there in Kent’s original book on the topic. I like the clarity of the Four-Phase Test and its cousin Act/Arrange/Assert. I agree that we should prefer testing the state of the class we are testing over its behavior.
But I do not think that another naming taxonomy for techniques to swap out depended-on components is going to do anything more than muddy the waters. Maybe you don’t like Gerard’s Test Doubles but they do catalogue a lot of what is out there in the wild. The naming conventions behind test doubles are not the issue most people encounter. The problem they hit is that in reality there are hard-to-test areas and people look for help in overcoming them.
After I learnt TDD I wrote, by my current standards, poor unit tests. I wrote tests that touched the Database. I wrote tests that touched the file system and called across the network. I wrote tests that checked private methods. I wrote tests that shared fixture or had dependencies between them. I put mocks everywhere, replacing every depended upon component. And I suffered with fragility, slowness, an unmaintainable code for my mistakes. But I needed to make those mistakes. Because it was only by making the mistake that I could understand the need for and value of a solution.
We live in a fairly lucky time for software craftsmanship. The wisdom of the masters of our trade is published in accessible formats for all of us to read. So when we need to learn at the feet of the masters we do not have to look for opportunities to work alongside them, we can find their wisdom in the form of patterns, principles, and practices published in books or on the web. I started my career in a period where this kind of knowledge was much less accessible, and the only way to discover a better solution was to find one yourself or work with someone who knew of a different approach. We live in fortunate times. But the ease with which we can find best practice often masks the fact that understanding the value of that advice usually requires us to have experienced the pain beforehand.
When I teach TDD one point I try to flag is that the first step is to start writing tests. Not to worry about how good those tests are, but to start writing tests. Once you are writing tests and experiencing the pain that comes from poor tests, you will appreciate the value of some of the best practices out there. Because then you understand them as solutions to problems not just more complexity. But first, just start writing tests. The difficulty with the software community is that experienced practitioners can overwhelm newbies by flooding them with information on ‘how to do it right’. The problem is that the most important step is not doing it right, but doing it at all. As a result newbies suffer from the kind of analysis paralysis that BDUF usually brings: should I be using a fake here, should I be using a mock? Instead we need to embrace the TDD solution to analysis paralysis of making it work and then making it right. Get your code under test, then worry about refactoring your test code. Over time you will learn enough to write better test code first time around. Your ability to see what Kent calls the ‘obvious implementation’ widens to include these new techniques.
We still need to do this today. On my current project we are still looking at the best way to test our controllers. Our existing test code works but is fragile and we need to review where we check the controller’s action by checking state and where we use mocks to check the interaction with the domain because the controller has no state that we can check. Right now, in some places, we use the state of the domain to check the controller’s action, and it is hurting us with test fragility as the test depends on too much. So we need to make it right. And in doing so we will increase our understanding of when to use mocks.
There is no shame in making a mistake, only in not from learning from it.
Don’t be put off by patterns and practices and assume you have to master all of them before you can begin work. They are there to help you when you need a solution.
Make it work, then make it right.