What’s in a (test) name?

My friend and erstwhile colleague Jay Fields recently tweeted about the tyranny of “should” in unit testing. To paraphrase Jay, he’s suggesting that test names are simply comments and as such don’t add much value to code because there’s not referenced around the code base in the same way that class names or methods are. Jay goes on to say that once you realise the relative unimportance of test names then you can excuse yourself from the tyranny of trying to figure out how to name tests with conventions like “given A when B then C should do D” and the correspondingly long method names that creates.

From my lastcouple of posts, you can see that my headspace is close to Jay’s, but I think I disagree with him on the importance of test names, which is slightly scary since Jay writes much more code than me, and happens to have written a book on refactoring, but hey I’m game.

After tweeting his initial missive, Jay followed up with some more context. Since he’s working in a trusted, closed context where he’s able to write concise and descriptive unit tests, his assertion is probably valid. However in the general case of teams of differently skilled developers with transient membership, I think test names do matter. A lot.

Recall that unit tests start their lives not as tests, but as design aids –  behavioural contracts over components under test in a context. With that understanding, a set of unit tests like the following (using JUnit 4) makes it quite difficult to interpret that behaviour:

public FluxCapacitorTest {
  @Test
  public void test0001() {
    //… some test code
  }

  @Test
  public void test0002() {
    //… some test code
  }

  // … other tests

  @Test
  public void test0055() {
    //… some test code
  }
}

Granted this is an extreme case of poor naming of tests, but other than the name of the component under test (FluxCapacitor) no behavioural contract leaps out at the reader. Of course the code in the tests may be excellent in which case I can read the methods to get an idea of the intended behaviour of the component under test, but don’t the following tests convey much more to the reader? I’d say that even if you just collapsed the implementation in your IDE and read the names, you’d get an idea of the behaviour of the classes under test (which is, to bang the drum again, what unit tests are really all about).

public FluxCapacitorTest {
  @Test
  public capacitorShouldNotActivateBelow88MPH() {
    //… some test code
  }
  @Test
  public capacitorShouldSafelyMeltAt150Mph() {
    //… some test code
  }

  // … other tests

  @Test
  public capacitorShouldSupportMultipleFuelSources() {
    //… some test code
  }
}

The effort in writing more expressive names (and keeping those names in sync with what the test code actually does) certainly takes effort. But such effort not only helps structure the design process by focusing on the behaviours the FluxCapacitor has to support, but also provides solid documentation once the unit tests cease to be a design artifact and become real tests.

In an email exchange on the topic, Jay suggested that tests like the following are preferable:

@Test
public void blah() {
 
FluxCapacitor capacitor = aNew().fluxCapacitor().build();
  Car car = aNew().car().with(capacitor).build();
  car.accelerateTo(88);
  assertTrue(capacitor.isActive());
}

I definitely agree that builders are are really neat way of expressing tests (something to which Pat Kua and Alistair Jones have used to great effect for functional tests on recent projects I’ve worked on), but I remain convinced that the cherry on top comes with a good test name, so taking Jay’s beautiful builder code, I think more intent is conveyed with the most minor of tweaks:

@Test
public void capacitorShouldNotActivateBelow88MPH() {
  FluxCapacitor capacitor = aNew().fluxCapacitor().build();
  Car car = aNew().car().with(capacitor).build();
  car.accelerateTo(88);
  assertTrue(capacitor.isActive());
}

While it may be plausible in certain environments to abbreviate test names, in the general case I’d advocate for naming your tests with as much care and attention as your regular classes and methods. What Jay says about test method names being comments is true of course and like any other comments they need to be kept in sync with your code and ruthlessly removed out when they’re no longer needed. This of course takes that little extra love and devotion, but if you love your tests, they’ll love you right back.

Posted in Programming, Testing

Leave a Reply

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

*