Name your unit tests clearly

I advocate unit testing, and particularly test driven development, for several reasons. The most obvious is that unit testing helps prevent bugs. Good unit tests also help you design better – if it’s hard to test your class, you’ll refactor it so it is easier to test, and consequently wind up with a more loosely coupled design.

In addition, good unit tests can serve as excellent documentation of what the code is supposed to do. This is actually kind of hard to do well. One technique that helps me with this is to name my unit tests clearly. Each test correlates to one behavior of a class, and the name of the test reflects that behavior.

For instance, say you have an application that is responsible for order fulfillment and billing. An order has a purchaser, and optionally a shipping address. If the shipping address is null, you want to use the purchaser’s billing address. The code might look like this:

public class Order {
  private Address shippingAddress;
  private Purchaser purchaser;
  // ...
  public Address getShippingAddress() {
    if (shippingAddress != null) {
      return shippingAddress;
    }
    else {
      return purchaser.getBillingAddress();
    }
}

A typical way of writing unit tests (which I blame on Eclipse’s JUnit plugin) would be to write a single testGetShippingAddress test, and test both cases inside it:

public void testGetShippingAddress() {
  Address billing = new Address(...);
  Address shipping = new Address(...);
  Order o = new Order();
  Purchaser p = new Purchaser(billing,...);
  o.setPurchaser(p);
  o.setShippingAddress(shipping);
  assertEquals(shipping, o.getShippingAddress);

  // now test what happens if shipping not explicitly set
  o.setShippingAddress(null);
  assertEquals(billing, o.getShippingAddress);
}

This does effectively test the class, but it isn’t very clear about the behavior if the shipping address isn’t set – particularly if you don’t notice that the shipping address was being set back to null. Instead, we could write it like this:

public void testCanExplicitlySetShippingAddress() {
  Address billing = new Address(...);
  Address shipping = new Address(...);
  Order o = new Order();
  Purchaser p = new Purchaser(billing,...);
  o.setPurchaser(p);
  o.setShippingAddress(shipping);
  assertEquals(shipping, o.getShippingAddress);
}

public void testUsePurchasersBillingAddressIfShippingAddressNotSet() {
  Address billing = new Address(...);
  Order o = new Order();
  Purchaser p = new Purchaser(billing,...);
  o.setPurchaser(p);
  assertEquals(billing, o.getShippingAddress);
}

It’s true that there’s now some duplication between these methods, but that would be removed fairly easily and I only left it in for clarity. What we’re left with is two simple, clear tests that leave no doubt about what this class is supposed to do. If this class’s behavior changes in the future, seeing that testUsePurchasersBillingAddressIfShippingAddressNotSet() failed is going to make a lot more sense than seeing that testGetBillingAddress() failed somewhere, and digging through that test to figure out just what I broke. You can just drop the word “test” from the front of each test method and it reads like a simple spec.

This approach also works well with TDD’s “write a test, make it pass, refactor, check in” approach. Each test represents exactly one behavior and supports the iterative approach of building a class one failing test at a time. In fact, I suspect that using fewer, coarser-grained tests (as in my first example) is pretty good evidence that the coder didn’t do TDD.

Leave a Reply

Your email address will not be published. Required fields are marked *