Writing good unit tests, Part 1: Follow your GUTs

Best practices and tools for high-quality test code

1 2 3 4 5 Page 2
Page 2 of 5

The good unit test

"The modern programming professional has GUTs." That declaration from Alistair Cockburn refers to a February 2008 interview in which Jim Coplien and Bob Martin talked about contract-driven development (CDD) and TDD. The core of the debate is the relationship between TDD and unit testing; Cockburn claimed that, for many people, TDD is just a synonym for having good unit tests, GUTs. However, in his opinion, if GUTs were an accepted term, then people would brag about having GUTs without implying whether they were written before or after the code -- that is, their pride would be distinct from when the tests were written.

I myself am not a TDD evangelist and -- you can quote me -- my personal opinion is that it's better to have good unit tests written just in time with the production code than to have bad unit tests written in a TDD manner, before the code materializes. In this sense I agree with Vikas Hazrati and Deborah Hartmann when they conclude that "development teams might want to practice TDD or write test cases after the code, as per their comfort. What really matters is that they should have GUTs."

But I still haven't answered the central question yet: What is a good unit test? Even Kevlin Henney cannot give us the ultimate answer in his article "Programming with GUTs" -- but he does have some good hints that are worth mentioning here. Henney states that unit tests are commonly viewed in terms of offering quantitative feedback on the presence or absence of defects in specific situations. But, in his opinion, they are in a position to offer much more than this. They can also provide feedback on how loosely coupled code is, and they can tell you a lot about code coverage -- that is, about how much production code (quantitative feedback) and which part of the production code (qualitative feedback) is tested at all.

Henney really focuses on the the "style and substance" of good unit tests. Good unit tests should communicate functionality to their readers -- that is, they should explain how to use a class or method in a specific environment or situation. You can choose between a per-method or procedural testing style, where every public method has a corresponding test method, and a more behavior-oriented style, where each unit test illustrates and defines the behavioral contract of the unit in question. Because behavior consists of more than just individual methods, Henney argues that you need a style that cuts across the interface, a style where each test case is structured in terms of a meaningful and specific behavioral goal. In his opinion, that's the style that characterizes good unit tests. Furthermore, good unit tests have good substance -- that is, they illustrate and define the behavioral contract of the unit under test.

In my opinion, it's worth having both the procedural style and the behavior-driven style that tells us a kind of user (or usage) story. Consider a large framework where you have a large public API. Because thousands of combinations of public methods will be possible (although not meaningful), I think the goal should be to test every single public method first, in isolation (excluding trivial getter/setter methods, of course). If this is still not possible, you should think about reducing a potentially large number number of tests by using risk-based methods (which I'll discuss in more detail later). Then you can create test scenarios that describe a specific program behavior.

1 2 3 4 5 Page 2
Page 2 of 5