Skip to main content

Unit Testing Tip: Break steps into separate methods

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!