The most perfect expression of human behavior is a string quartet. — Jeffrey Tate
I recently had to work on some code I wrote before I had really understood test-driven development. I had written the tests first and the code had 100% test coverage, but the tests were not very useful in helping me understand the code.
The problem was that I had written tests for each method, testing pre- and postconditions and class invariants. This is the wrong approach to writing programmer tests. Instead, each test case should specify a describable aspect of the functionality an object provides to its clients. That aspect will probably involve multiple methods of the class. As a maintenance programmer using the tests as documentation you want to know how the behaviour of those methods is interrelated, not how each method acts individually.
Before writing that code I had recently written an application in Eiffel and the Design by Contract approach was fresh in my mind. This is one of the reasons I don't really like Design by Contract: it doesn't help you understand your code because the DbC specifications are attached to each feature of a class, instead of specifying aspects of a class' functionalty that each involve the interplay of multiple features. Using DbC to specify the interrelation of methods requires the programmer add features to the class that are otherwise unnecessary, and has the result of actually making the class harder to understand.
A real failure does not need an excuse. It is an end in itself. — Gertrude Stein
I ported some old code that I wrote in my branch of the mockobjects.com dynamic mock packages to the new jMock codebase. Looking through the test suite I found some tests that didn't actually test anything! They just passed without bothering to check the behaviour of the object being tested. Hmmm... I must have written the test and the code to be tested at the same time, and so expected the test to pass. Which of course it appeared to do with flying colours. What a stupid thing to do. I should have written each test, watched it fail and only then written the code to make it pass.
Lesson: always make your tests fail before you make them pass.
Or, as is already written on the C2 Wiki, "never write a line of code without a failing test.