Wednesday, October 31, 2012

Literals and variables in unit testing

Today at the Path11 Book Club we talked about chapters 21 and 22 of the book "Growing Object-Oriented Systems Guided By Tests" by S. Freeman and Nat Pryce.

In the part Literals and Variables of the chapter 21 the authors advise to use variables/constants in place of meaningless literals:
...test code tends to be more concrete than production code, which means it has more literal values. Literal values without explanation can be difficult to understand because the programmer has to interpret whether a particular value is significant (e.g. just outside the allowed range) or just an arbitrary placeholder to trace behavior (e.g. should be doubled and passed on to a peer).
...
One solution is to allocate literal values to variables and constants with names that describe their function.
While this rule is rather self-explanatory, I have noticed some interesting pattern. When I do TDD pretty often in the refactoring phase I go through the test code and replace literals with constants (unless it obscures the readability). What happens is 2 kinds of constants emerge:
  • Example Constants (one value out of many possible)
public static final String USER_NAME = "Joe"; //in fact it could be also "John" or "Sue"
public static final int INVALID_ID = 666; //in fact it could be also 667, 668, 669 and so on...
  • Significant Constants (concrete value having special meaning)
public static final String ATTR_EVENT_ID = "eventId"; // significant value
public static final int AGE_OF_CONSENT = 18; // significant value
Significant Constants will be very often needed in both test and implementation. So they can be moved to the implementation class and then referred to in the test class.

On the other hand, Example Constants will usually stay only in the test code.

Here is an example using Spring MVC:

Update: as pointed out in the comments, sharing Significant Constants in both tests and implementations carries the risk of uncaught errors when editing the constant value. The safest way is indeed to have the test class be entirely a specification, thus defining its own constants/variables and not refer to the implementation class.

PS: don't forget to visit awesome Path11 Book Club :)