First I thought TDD was about testing, then that it was about designing. Now I’m convinced it’s about thinking at a sustainable pace.
First ascent of the Matterhorn.
My name is Uberto and I write tests.
I write many types of tests: unit tests, integration tests, acceptance tests, tests for performance, for design, for reliability, etc. This post will be about validating design writing unit tests before the code- in a word, TDD, as defined by Kent Beck.
I heard about automatic testing for the first time around 2000, and I immediately got excited about it. I also co-authored the first open source xUnit framework for Borland Delphi, my main language at the time.
At the beginning, I was concentrating on the automatic validation aspect. With my tests, I could increase my confidence that everything was working as expected. I used to design a new software using UML, and then start writing tests to verify each class before writing code.
Later, I learned how to use the tests themselves to drive and explore the overall design. Writing tests with interfaces and mocks, before writing a single line of code, allows me to experiment and really feeling how the code would work, without wasting time with paper and diagrams. Later, while I am writing code to make tests green, I can understand the critical points of the design and improve it before completing the first implementation.
As an aside: I still use paper and whiteboards for high-level architectural diagrams, but not for class/object level design.
But recently, I realized this was not the main benefit of Test Driven Development.
Let’s take a step back and imagine we have to design the implementation of a new functionality. Even if it’s just writing few classes to work together, there are lots and lots of possibilities. We can use several design patterns, we can use off-the-shelf libraries, we have to sort out which data structures and how to name them to reflect our domain.
But as we start writing code, our alternatives dwindle. It's a bit like the game of Go, where you have a lot of possibilities in the opening, a few in the middle of the game, and then only one in the end.
But what if we end up stuck in a bad position? Different from the game of Go, writing code allows us to go back on our steps and try a different solution if we find a problem. Going back and forth between different solutions makes it very easy to forget some details along the way, realize it later, and correct for that, then try it again without luck, and finally spend hours going in circles.
And here, TDD really shines! We can retrace our steps, try other ways, and still be safe with our test harness in place. If I don’t use tests for design, I find myself writing code in a hurry, because I cannot keep all the details that I have in my mind for long. Instead, TDD gives me the pleasure of writing code at my pace, with the ease of mind that I don’t risk forgetting anything.
For complex problems, I routinely try at least three different approaches. Tacking a problem from different angles, I finally feel much more confident about my solution.
If Waterfall is like expedition-style and Cowboy coding is like free climbing, TDD is like alpinism. Safe, but still quick enough. Unfortunately, this is something I really struggle to communicate properly when I do courses about TDD. Even when people learn and practice TDD, they mostly use it just to validate their first design idea. If it proves hard to test, well… we are prepared to write tons of mocks if necessary!
So if you are practicing TDD, please listen to your tests and try different solutions before committing to one. In this way, you will improve your design skills every time, and for what it is worth Uberto, will be proud of you.