Testing in the Trenches (TinT) is a series of posts adapted from real-world discussions and advice I have given to teams and individuals struggling to adopt Unit Testing habits and best-practices.
The Method is an important basic unit of unit-testing. If you
have a tendency to write entire processes or algorithms as single, large
methods, you will find a lot of benefits to breaking them into smaller steps in
separate methods. Unit tests for these smaller methods are easier to write, and
are key for proving the validity of each step in the larger process.
For example: We have an Enhancement Specification that says
the program should behave one way if a given Rule has never yet been applied,
and behave another way if the Rule has already been applied one or more times.
This distinction of "already used or not" is a key
condition that appears many times in this spec.
We can check for the condition with something like this:
if
(myrecord.getAsString(lastPledgeDate).isEmpty())
// do the Never Yet Used thing
else
// do the Already Used thing
We could use that condition right in the algorithm whenever
it is needed, but there are some good reasons not to (see below). Instead, pull
that condition into a separate method:
public boolean
hasRuleBeenUsedAtLeastOnce()
{
return !myrecord.getString(lastPledgeDate).isEmpty();
}
In the above method, the check has been inverted, since it
lets the calling code express a positive (hasRuleBeenUsedAtLeastOnce()) rather
than a negative (e.g. hasRuleNeverBeenUsed() )
This method is short and sweet. Is it too simple? Maybe, but see below
for its benefits. To unit-test this method, we would need at least one case
where the data field has data and one where it does not. So after creating a
jUnit test class, add:
@Test
public
void hasRuleBeenUsedAtLeastOnce_BlankDate_NeverUsed()
{
setLastPledgeDate("");
Rule r = new Rule();
assertFalse(r.hasRuleBeenUsedAtLeastOnce());
}
@Test
public
void hasRuleBeenUsedAtLeastOnce_NonBlankDate_HasBeenUsed()
{
setLastPledgeDate("20160810");
Rule r = new Rule();
assertTrue(r.hasRuleBeenUsedAtLeastOnce());
}
Does this seem like too simple and trivial a chunk of code
to pull into a separate method? Here are some benefits to doing so:
1. It makes the calling code more readable and therefore
easier to understand, maintain and debug.
Consider the difference between these two conditional
statements, when you read them in some code,:
if
(myrecord.getString(lastPledgeDate).isEmpty())
vs.
if
(rule.hasRuleBeenUsedAtLeastOnce())
The first is at a lower level of detail. Reading it drags
our brain into the guts of data structures and data types. The second, with a higher level of abstraction, lets our
thinking stay focused on the algorithm we are reading.
2. It is re-usable.
Imagine that, next year, the definition changes for how we
know if a Rule has ever been used. With this method, the change this would
require happens in one place. Without it, we'd have to hunt for and change the
dozens of places that did the check the low-level way. And if we miss any,
we'll introduce a new bug.
3. It is more Object-Oriented.
Now the responsibility for telling if the Rule has been
applied yet or not belongs to the Rule class itself. None of the calling
classes need to know or care about how it is determined.
4. And of course it is more testable!