Kent Beck's Test Driven Development: by example is a great tutorial on how to do TDD: do we need another?
Well, yes. Beck's book is good, but it looks at the process from one direction: writing small tests, writing the simplest possible code to make them pass, then refactoring to clean code. Here Freeman and Pryce take a more systems-level approach: writing tests as part of the design process, test that help grow the overall code into the desired system. They have their own approach to this process, making heavy use of "mock" test objects (rather than having to have the surrounding implementation in place to run a test), which also supports their particular style of iteratively adding code ("growing" it) to make a test pass.
The first part of the book explains their object-oriented design and development philosophy (for example, for query methods: ask the question we want answered, rather than asking for the information to let us figure it out; also build new functionality by developing from the inputs to the outputs, which gives a uniform development style that iteratively discovers and implements the needed services), and how their testing process can help support this style. In particular, the TDD approach helps the design process. Whole system level acceptance tests specify what functionality is to be built (and since they specify what, rather than how, they can be written in a higher level, declarative style), and can help stop any unnecessary code being developed.
Unit testing tests the underlying objects in isolation, and also helps the design process. (Of course, objects are not truly isolated: in an application they communicate with other objects to fulfil their responsibilities. This is where the mock objects approach comes in, here illustrated with the jMock2 framework.) The philosophy is the same higher level one: unit test behaviour, not methods.
The tests also help document the system design. The code captures only the class structure; the tests can capture and make visible the dynamic structure of communication patterns (or protocols).
[This has resonances with complex systems and emergent properties: the communication patterns are emergent (since not explicitly represented the the code), and the communications are "more important" than the "things" doing the communication.]
Adding acceptance tests for new functionality is all very well, but how do you get started, what the very first step? The authors make used of what is memorably called a "walking skeleton":
So there is always a working system: not just working in terms of its functionality (no matter how limited in the early stages), but in terms of an automated build and deploy cycle, too. This pragmatic software engineering approach carries on through the book. The second part is an extended case study, where we get to see how to grow a non-trivial application using system-level TDD to support the authors' OO design style.
And finally they discuss some more advanced things (the way state transition diagrams map directly onto tests; to use domain-specific types rather than domain-neutral types such as collections and strings; how test code tends to have concrete values and an abstract "how", whereas production code tends to have abstract values and a concrete "how"), and things that make testing difficult, like persistence, threads, and distribution.
Also of interest is Tim Mackinnon's Afterword on the history of Mock Objects: how the pattern was noticed, gradually refined, and implemented into a powerful testing framework. It's always good to see the genesis of these powerful ideas: they didn't spring fully formed from someone's brain, but developed slowly, iteratively, through practice, over the years.
This is an excellent book, full of good sense about object orientation, about design, about testing, and about developing solid code that the user wants. Recommended.