Wednesday, April 18, 2007

TDD: The Bowling Tracker

Some years ago I purchased Bob Martin's book Agile Software Development, Principles, Patterns, and Practices. At the start of the book, he brings up a TDD exercise using 10 pin bowling. At the time I thought it would be a good idea to try my hand at the exercise so I avoided reading that chapter in the book; looking at someone else's ready-made solution to a problem tends to create mental blinders so I always try to do things on my own first, then look at the solution in the back, so to speak. Also, if my solution turns out to be inferior, I can learn a lot more from thinking about the differences between the two. I can't remember any more if I ever actually did the exercise or just forgot about it. Recently however, I was thinking about developing this example for potential use in a presentation or seminar about TDD for Eamug. Here then is my implementation along with all of the tests - so feel free to have a look!

Here are my general observations about the process:
  • It took longer than I thought! I expected to spend a couple of hours, but it actually took over a day. I estimate the total development time was about 10 hours!
  • Because all of the classes except BowlingLane emerged from refactoring, none of the classes used by BowlingLane had unit tests for their methods. Since none of those methods was public - they were only ever called by BowlingLane which provides the public API, I suppose that's ok. I think my rule is that if a method of a class comes about as a result of refactoring, then there should already be a test for that functionality at a higher level - so one doesn't need to worry about writing a specific test for that method.
  • Despite the simplicity of the problem, I was surprised to find quite a few gotchas that broke my tests. I definitely think that using TDD to generate this functionality both produced better code and saved time over trying to hack the entire thing out.
PS: Coincidentally Curtis Schofield made a link available to Bob Martin and Robert Koss' original bowling TDD example so feel free to have a look their implementation as well. They focus on just getting the scoring right, so they don't distinguish between an unplayed frame and a scratch where both throws were gutter balls; they also don't seem to handle displaying an empty cumulative score if the player makes a strike or a spare but hasn't made the subsequent throws required to settle the score for the current frame. I suppose that logic could go into display-specific code they didn't bother writing since their emphasis was just on the scoring itself. I might try their approach of just having an array of ints of size 21, which would make the scoring code a lot simpler. Then I'd have to put the smarts around displaying frames properly and displaying an empty cumulative score when appropriate into code that is specific to the display. The nice thing is that now that I have my external API I could in principle completely re-write the back end and as long as I my tests passed, I'd be all right.

PPS: For bonus marks, I just had a look over my code and I'm pretty sure there is at least one bug! Can you find it? :) Sadly even TDD doesn't remove the possibility of bugs, but I can think of a test that would expose it!