October 24, 2003
I've been doing test-driven development for several months now and am not looking back. I don't remember what first inspired me to read Kent Beck's book on the subject, but I sure am glad I did.
I was already sold on the idea of unit tests. (These are tests for individual units of code — be they methods, classes, or what have you. If you've ever installed a CPAN module or run make test
, you've seen unit tests at work.) However, they had not yet wormed their way into my actual coding practices. You know how it is: sure they're a good idea, but when would I have time? This was part of the initial appeal of TDD. By writing the tests before the code being tested, I'm guaranteed to have tests.
This is far from the only benefit, though, or even the most important one. Testing my code before I write it makes me carefully and continually work through the design, facilitating exploration of interesting ideas and discard clearly ridiculous ones before I get too far into them. I've found that TDD helps me develop cleaner, more robust code. Writing implementations for an interface before I've even written the interface helps expose flaws and vulnerabilities that I likely would not have discovered until much later.
It's difficult to understand until you've tried it for a while. But I really do write better code this way.
Sound confident? Yep. Beck writes a lot about how the test-driven cycle improves programmer confidence because of the immediate feedback and stream of small successes. Pragmatic Programmers Dave Hunt and Andrew Thomas also suggest test-first model as a way of breaking through the fear of the blank page (er, empty buffer). Start by writing a simple test, so simple that you wouldn't normally bother.
The advantage to starting with such a trivial test is that it helps fill in the blank canvas without facing the distraction of trying to write production code. By just writing this very simple test, you have to get a certain level of infrastructure in place and answer the dozen or so typical startup questions: What do I call it? Where do I put it in the development tree? You have to add it to version control, and possibly to the build and/or release procedures. Suddenly, a very simple test doesn't look so simple any more. So ignore the exquisite logic of the routine you are about to write, and get the one-line test to compile and work first. Once that test passes, you can now proceed to fill in the canvas — it's not blank anymore. You're not writing anything from scratch, you're just adding a few routines…. ("The Art in Computer Programming" (PDF).)
Kimbro Staken recently wrote about encountering programmer's block. TDD is one way I handle this situation. It just helps me start doing stuff. If I could convince someone to pair program with me, I think that would help, too.
A coworker agrees TDD is interesting and he's certainly all for unit tests, but considers TDD a waste of time when just experimenting. On the contrary, I think that experimentation is exactly when TDD is needed. Sure, if I'm writing a one-off script, there's no need to write a test. But if I'm putting some effort into experimenting, maybe half a day or more, TDD is worthwhile because of how it supports exploration of ideas. Rather than first writing a class, then discovering the mistakes I made in designing its interface, I implement the interface right away and figure out what makes sense and what doesn't. Once I fall into a rhythm (test, code, test, refactor), experimentation goes faster.
An essential part of TDD is refactoring to simplify and make designs cleaner. So TDD is also a useful introduction to refactoring (next up for me: read Fowler's Refactoring). Because of this, TDD also served as my entrée into agile software development, in particular Extreme Programming. I'll probably write more about this before long. Here's a quick list of current influences: