name: Growing_Object_Oriented_Software isbn: 0321503627 title: Growing Object-Oriented Software, Guided by Tests author: Steve Freeman author: Nat Pryce publisher: Addison-Wesley year: 2010 acquired: 2010-06-03 start: 2010-12-20 stop: 2011-02-09
Came recommended by Kent Beck. The authors wrote jMock. This book explains how they approach writing tests.
Well worth the read, whether you're new to TDD or if you're a seasoned practitioner. Parts I and II lay out how TDD works and the authors' philosophy with regard to writing software. Many other books describe TDD and the authors are candid when it comes to their personal practices. YMMV. Part III is a very comprehensive case study where they use the practices in anger, that is, trying to achieve some goal and not just as a toy exercise. Part IV revisits some of the concepts illustrated in the case study. Part V pays special attention to some advanced concerns when writing tests, such as testing persistence or multithreaded code.
Ever since I began using JUnit, I've been adamant about
initializing the fixture in the setUp()
method and not in the constructor.
Early JUnit would create all test instances before it ran the first test, so you
didn't want to reserve any resources in the constructor. A class with 20 tests
would reserve and hold 20 resources for the duration of the test run. Instead,
you reserve the resource in setUp()
and free it in tearDown()
. This way,
instead of holding up 20 resources, at most one is held at any given time. And
the best way to remember to reserve expensive resources in setUp()
only is to
do all fixture initialization there rather than split it up between
constructor and setUp()
.
So you can imagine my surprise when I see the authors initialize their fixtures
in their test class constructors. What are they thinking?!?! I had to go back
and write some sample tests using the latest JUnit and print traces in
constructors, setUp()
, tests, and tearDown()
to validate my assumptions. Lo
and behold, at some point in time, JUnit got it's act together and now creates
test instances as it runs tests, not all of them at the beginning of the test
run. For a moment, I was afraid that Grails might leave
me stuck in the dark ages of setUp()
-only initialization, but I'm safe there
too. Now, I can update my habits.
The sample problem had lots of complexity: GUI, asynchronous communication, Jabber-based RPC, etc. Some of the code transformations are sometimes hard to follow, especially the further you get into the case study and they pick up the pace. Some side-by-side comparisons would have been welcome, but maybe hard to do in the limited space of the book.
The authors used a neat trick to track their development progress through the sample problem. They kept a hand-written list of tasks on a sheet of paper, crossed out items as they completed them, and added tasks as they happened upon them. Here's a snapshot from the somewhere halfway through:
Todo
single item - join, lose without bidding
single item - join, bid & lose
single item - join, bid & win
single item - show price details
multiple items
add new item through the GUI
stop bidding at stop price
translator - invalid message from Auction
translator - incorrect message version
handle XMPException on send
I used a similar technique a long time ago to implement a feature with great success. I'm going to re-introduce it to my toolset going forward.
I really liked the chapter on test data builders. I already grew more comfortable using test helper methods to clarify the text of a given test after reading Gerard Meszaros' xUnit Test Patterns. The builders in this book take it to another level. The authors use builders and Hamcrest matchers to create DSLs that really make tests easy to read and makes their purpose crystal clear. Groovy and Grails already have support for writing builders and I'll have to spend some time adapting the examples in the book to the Groovy way.