Why do I practice TDD you ask?
There’s a number of paths I could’ve taken (or combinations of the four).
1) Write no automated tests.
2) Write tests before writing code (in small iterations).
3) Write tests at the same time as code (in small iterations).
4) Write tests after writing code.
Writing no tests
On a project that’s worth practicing DDDesign on, having no tests is not an option. Continuous refactoring towards deeper insights is the foundation of DDD and it’s too scary to refactor with no tests.
As a result, refactoring won’t happen without tests and code will rot. Over time our code and our understanding of the domain will either diverge, or we’ll stop looking for insights to avoid change.
Tests are critical to embracing change. Tests give me the confidence and a peace of mind.
Writing tests first
Note that it's not about writing ALL the tests before writing any code. In fact, it's about writing as little test code as possible to see a failure. Then make it pass. Then improve the design. Then write the next test case. Wash. Rinse. Repeat.
Writing tests first encourages me to take small steps in an iterative and incremental fashion. It’s a very organised way to develop that helps me focus. It keeps me disciplined and productive.
Without this workflow I tend to get distracted, loose the discipline, or start taking steps that are too big.
Big steps mean my code is longer in a “broken” state and it’s harder to get it back to “workable” state. It can go really messy really quick.
Discipline and short cycles encouraged by TDD make me very productive indeed.
Writing tests first leads to a better design. TDD helps me to create modular, loosely coupled and cohesive components. It helps me to come up with simple abstractions and good separation of concerns.
It doesn’t mean there’s no upfront design. Whiteboard is my friend. However, TDD helps me to validate the design I previously thought of and improve on it.
Obviously, as I gain experience, I will intuitively know what good design looks like and very often get it right on the whiteboard. It doesn’t mean TDD stops being useful. On the contrary, the process keeps validating my design when I’m not looking or have a bad day.
Occasionally, especially when working in an unfamiliar territory, I will fall off track. It’s liberating to know TDD will have my back. It’s like double-entry bookkeeping (to quote Uncle Bob).
Writing tests later
Writing tests after the code would give me some of TDD benefits. Feedback on testability and quality is still there, even if it’s late. If code I wrote turns out to be hard to test I need to go back and revisit the design. That’s counter-productive.
Writing tests after the code tends to produce some amount of unnecesary code.
Writing tests and code together?
As for writing the code and the test at the same time, it’s kind of what Test && Commit || Revert (TCR) is about. It’s a technique that’s suppose to encourage us to take even smaller steps, as there’s always the risk of loosing the code and the test we’ve just written.
I’m not really convinced by TCR enough to incorporate it into my regular workflow. I’m afraid it would bring the stress back to my development process. However, it’s a technique that might teach me something I’m sure.
Why do you practice TDD? Why don't you?