I've been working on a kata called "Tennis"*, which I find interesting, because it is quite quick to code, yet is a big enough problem to be worth doing. It's also possible to enumerate pretty much all the allowed scores, and get very comprehensive test coverage.
What I've found is that when I'm using TDD to solve the Kata, I tend to only enumerate actually a very small number of the test cases. I generally end up with something like:
Win for Player1
I think that's enough to test drive a complete implementation, built up in stages. I thought it would be enough tests to also support refactoring the code, but I actually found it wasn't. After I'd finished my implementation and mercilessly refactored it for total readability, I went back and implemented exhaustive tests. To my horror I found three (of 33) that failed! I'd made a mistake in one of my refactorings, and none of my original tests found it. The bug only showed up with scores like Fifteen-Forty, Love-Thirty and Love-Forty, where my code instead reported a win for Player 2. (I leave it as an exercise for the reader to identify my logic error :-)
So what's the point of TDD? Is it to help you make your design good, or to protect you from introducing bugs when refactoring? Of course it should help with both, but I think doing this practice exercise showed me (again!) that it really is worth being disciplined and careful about refactorings. I also think I need to develop a better sense for which refactorings might not be well covered by the tests I have, and when I should add more.
This is something that my friend Andrew Dalke brings up when he criticises TDD. The red-green-refactor iterative, incremental rhythm can lull you into a false sense of security, and means you forget to stop and look at the big picture, and analyze if the tests you have are sufficient. You don't get reminded to add tests that should pass straight away, but might be needed if you refactor the code.
So in any case, I figured I needed to practice my refactoring skills. I've created comprehensive tests and three different "defactored" solutions to this kata, in Java and Python. You can get the starting code here. You can use this to practice refactoring with a full safety net, or if you feeling brave, without. Try commenting out a good percentage of the tests, and do some major refactoring. When you bring all the tests back, will they still all pass?
I'm planning to try this exercise with my local python user group, GothPy, in a few weeks time. I think it's going to be fun!
* Tennis Kata: write a program that if you tell it how many points each player has won in a single game of tennis, it will tell you the score.