One year ago I finally awakened and started to use TDD whenever I could. It took some time to get used to not trying to design an architecture from the start, but it was great for smaller example programs.
To get a head start I participated in the Stockholm Software Craftsmanship meetups and read a lot of books while practicing. I am far from an expert and need a lot more practice to feel completely confident working this way, but so far I can see a definite improvement in the results.
So what do I see as the benefit of TDD?
Now, if you have read any books by Uncle Bob, Kent Beck or anyone else of the well-known authors in this field, my list will be pretty much the same thing…
- Pair programming does not require TDD, but TDD makes pair programming much easier.
- The design is automatically testable. A common problem in top-down design is that it can be very hard to implement unit tests afterwards. With TDD, the test put constraints on how (badly) you can design your system.
- The design will also be more modular. My own experience is that the classes and methods will be smaller since the tests makes it very obvious when you break the SRP.
- You will have test for all methods. This is vital to be able to maintain it and add new features later.
But is there anything I didn’t like?
Not really. TDD is a really good strategy for writing software. But there are some misconceptions that create problems. One of the fundamental ideas in TDD is to let the architecture grow in small increments. This is sometimes interpreted as if no design is necessary. But that is not what it means. If you don’t do any design at all, you might have to constantly rewrite all code and test cases due to bad design choices. Then they claim that TDD doesn’t work and that it leads to bad design.
But that is not how it is done. You need to think about your design from the beginning. The secret here is to only do enough upfront architectural design to be able to start the implementation of the first test case. What you have to aim for is enough design to be able to start implementation in a direction that is likely to be ok. During implementation, you will realize that the original architectural design needs to be changed. You don’t want to do too much upfront design since much of it will be thrown away when you find better ways to do thing. You need to find a balance here.
Another issue I have is not really related to TDD. It’s more about unit testing in C++ in general. Don’t get me wrong. I want to have unit tests for all classes, but I must admit that I’m lazy sometimes…In C++ this means adding a lot of virtual base classes where you can replace objects with your mock objects. This is no reason to avoid writing tests, but I envy the Java world where you can dynamically mock objects without using inheritance everywhere. Many mock frameworks in Java uses Reflection to accomplish this.
But maybe we will see this soon also in C++. C++14 is just around the corner and Reflection is one of the new things that might be introduced there!
Leave a Reply