So we are TDD. We proudly announce the number of unit tests and the percentage coverage as part of the scrum achievements. We make demands on minimum coverage (for a brief while when we had TFS, it was a check-in constraint). But, what do we actually gain by testing? Is there a law of diminishing returns in testing?
Uncle Bob in his first presentation at our company demonstrated the bowling example. It is such a simple, eye opening experience to see how easy it is to over specify.
As I mentioned earlier, we were used to very delayed gratification. There were no demands on checking in code, actually we encouraged private branches for doing stuff. Sometimes it takes months before the changes could get in to the build.
The fun part about unit testing (especially if you have a “run this test” context menu) is the instant gratification, even in failure. You should first write a test that fails, so says TDD.
In a non TDD style of development we always expect to succeed. The first time I press build after a series of changes, I expect it to pass. The first time I run the application after a change, it better not crash. Even under the best circumstances a full build and run of the application, which was required if the change was in any of the core units, takes quite a while. Once the application comes up, we need to login and navigate to the view where the change is to see its effect. If we were to find something not working it would be a downer. So, we expected to succeed all the time, there by accumulating heart breaks upon heart breaks.
What changes when tests becomes the primary focus of development? When you write a unit test to model a new behavior, the first attempt is not even supposed to compile. Sometimes, if you are just fixing a behavior, we might have a unit test that can be compiled successfully without changes, but it definitely should not be passing. So, most times, we are specifically looking for a failure. It grows on you. I am no longer ashamed by a compilation error. It is a piece of information, sometimes quite valuable insight into the change that I am going to make. Since when a unit test breaks no airplanes fall from the sky (or angels die), we can afford to do this over and over. Every failing test gives us yet another insight into the problem, one more thing to do; every passing test makes us look for the next best way to fail.
Accepting failure as not just a normal outcome but as a desired outcome makes things much less stressful. If we fear failure, we will build safety measures for every imaginable way something can fail. The problem here is that, there are more imaginable ways to fail than plausible. And there are far more even plausible ways to fail than probable.
In a very fast pace environment things do go wrong from time to time. Since failure is welcome, there need to be a way to celebrate it. This is why we invented the Blame Gametm. When something goes wrong, when the build turns red, when a test “works in my machine” but no where else, when you wipe out the changes for 50 Fitnesse scripts because of one wrong merge, we blame. Of course, the blamee doesn’t have to accept it. There can always be come backs as long as they are more logically consistent and evidenced than “dog ate my hard drive”. The key is to embrace the failure.
What TDD, not just unit testing but aggressive acceptance testing teaches us is to fail often and fail gracefully. As we all know, if your millions of assertions never fail, they are as good as absent. The value of a test is when it fails.
*This is one of the best memes to come out of MythBusters promoted constantly by Adam Savage. There is one podcast where he describes why it is a fundamental principle for him. I hope it is not copyrighted by Adam or Discovery channel.