Thursday, January 21, 2010

TDD Calculator kata: thoughts on day 6

I've been doing the TDD Calculator kata (as per Roy Osherove: http://osherove.com/tdd-kata-1/) the last 6 days. Today was the first day where I did the complete kata. I got the first section down to 22 minutes, but by the time I tackled the final issue (recognizing and processing multiple custom delimiters) the frustration had kicked in and I had metaphorically rolled up my sleeves: staring at output in debug mode, watching side-effects break 4 of the previous tests, and feeling stress levels go up as the clock ticked. I finally resolved all issues, had all tests passing, and did final refactoring in just under 45 minutes.

Temporary stress levels aside, this has been a very productive experience. I've seen a number of patterns emerging with each successive repetition of the practise:

1) Faster and faster interaction with Resharper
I have had friends recommending Resharper to me for a couple of years now, but it was actually working with Eclipse & Java again to build the sample code in "Growing Object-Oriented Software, Guided by Tests" that reminded me about all the tools that Eclipse provides to assist with code generation as you build test-first. When I returned to Visual Studio to build the equivalent code in C#, it quickly became apparent that Resharper was the Eclipse-ification of Visual Studio. With the beginning of the TDD kata practise, the usage of Resharper has become even more prominent, with reliance on it for class and interface geneation, constant reference to its recommendations for improving the code, and quick in-browser test runs with NUnit.

2) Getting serious about Visual Studio (and Resharper) keyboard shortcuts
I have been quite happy to mouse along in Visual Studio, but watching some of the kata samples out there have brought home the usefulness (first for the kata, but already quickly apparent in my daily work) of using keyboard shortcuts to stay caught up to the train of thought. The 10 most-useful that I have started using regularly are:
* Ctrl-Alt-L to jump to Solution Explorer (and down and left hours to collapse projects)
* Ctrl-Tab to move between tests and code ("Active Files") and to other Visual Studio windows
* Shift-F10 instead of right-click (yes, I had to google that one)
* Option-R-U-N to run all tests in the Resharper window
* Alt-Enter to look at contextual Resharper recommendations (typically to invert if conditionals or switch declarative types to var)
* Alt-Shift-F10 to look at contextual Visual Studio recommendations (typically to either reference a using statement for a class, or to cascade a renaming across the code)
* F9 to set a breakpoint, and Ctrl-Shift-F9 to clear all breakpoints
* Ctrl-K-C and Ctrl-K-U to comment/uncomment code
* Ctrl-R-M to extract a method, and
* Ctrl-R-O to move a class to a difference namespace

3) Learning to use the simplest solution possible
It felt gimmicky at first, but solving two tests passing either "" or "3" to return 0 from the first, and 3 from the second, is most appropriately solved with:

return inputString.Length > 0 ? 3 : 0;

Of course that isn't "real", but it forces me to not overbuild. I notice that when I hit the more challenging issues to resolve (eg. the final requirement in the kata) that it's very tempting to start building the Sistine Chapel, but of course then the question becomes how do I test a coding monstrosity?

I'll come back to this one at the end, with it's implications for the coding of larger projects

4) Correspondence of test names and one-at-a-time issues to resolve
The Calculator kata states a series of issues to resolve. Each test is named with the resolution of that issue, for example:


[Test]
public void Calculator_Allows_Multiple_MultiChar_Custom_Delimiter()


It's very, very clear, and is a excellent parallelism to the original stated issues.

Overall Conclusions
I've written lots of unit tests over the last 3 years, and they have been extremely useful and provided signifigant code coverage (on my current project, I just passed the 300th unit test), but they, and the classes they address, tend to be large. I am using a domain model with NHibernate, I always practise moving business logic down (when it slips into the client or a service layer) into the domain object where it belongs, but then it tends to stop there. The domain model is correct, the logic is inside the entity, it prevents redundancy, all good things, but it makes for large entities and large tests, as opposed to more incremental tests shaping the design. In re-reading Refactoring [Fowler] this past November, I saw that smaller classes, with increasing delegation (eg. a domain entity calling out to a strategy class) makes for greater granularity, and with it, smaller tests. Starting from the tests-first, with the goal to keep the tests small and simple, helps to keep the classes small and delegating naturally.

No comments: