This book has been on my radar for some time, and I recently acquired it when a work college passed the library loan on to me.
The authors place a lot of value in testing the communication protocols between objects, rather than the internals of the object, and as such, their tests rely heavily on the use of mocks. There is some arguments that a reliance on mocks represents a code smell to be refactored out, but I think that the authors code avoids the traps that less experienced developers (and I’m including myself here) make when using mocks (mostly missing opportunities to refactor because you can make horrid designs work with an abundance of mocks).
In particular the authors make use of the ‘Tell don’t ask’ school of software development, which naturally leads to using mocks but ultimately tends to lead to good, encapsulated, OO code (where as no-mocks code tends to work its way towards being more functional than OO).
The book starts with a brief overview of TDD and the benefits it can give, and includes this rather nice quote which sums up the 3 levels of testing they engage in:
Acceptance Tests - Does the whole system work?
Integration Tests - Does our code work against the code we can’t change?
Unit Tests - Do our objects do the right thing, are they convenient to work with?
That last part in particular shows off the value of the design feedback TDD can give.
A large amount of the book focuses on growing a system, one small requirement at a time. This is an example that is bigger than what is found in most books, and it really gives you an insight into the process, definitely worth reading for anyone who hasn’t done any ATDD (Acceptance Test Driven Development) before.
The step-by-step walkthrough starts off with the concept of a ‘walking skeleton’ which is the simplest amount of code that proves the simplest complete feature, but working out the whole pipeline of developing, testing and deploying. Most of this code is refactored away but getting the whole process started early helps to answer any remaining questions and highlights pain points.
The latter part of the books is a goldmine of information and techniques about keeping TDD sustainable, going in to detail about how to keep both the code and the tests clean and prevent the project from being bogged down with brittle tests.
It starts off with a section called ‘Listening to the Tests’ which shows various ‘test smells’ and methods to fix them, and goes on to more advanced topics.
One technique that I’ll definitely be using is making use of ‘builder’ object to supply complex data to the tests. So instead of using different helper methods for the different tests, like so:
1 2 3 4
createBillWithItem(); createBillWithItems(); createBillWithItemAndDiscount(); createBillWithItemsAndDiscount();
You end up with something that looks like this:
1 2 3 4
createBill() .withItem("foo") .withItem("bar") .applyDiscount(10.0).build();
This allows you to make the test clearer by specifying only the parts of the data that the test is interested in, whilst removing the redundancies the mass of helper methods created.
Finally there’s a section covering advanced testing topics such as testing persistence, threading, and testing asynchronous code.
I wouldn’t recommend this book to people just starting out with TDD (I’d recommend The Art of Unit Testing, but I’d be recommending it to them soon after, I think that the step-by-step demonstration on building a non-trivial system test first would be very valuable for beginners, and the latter parts of the book are useful to more experienced developers too.