h1

Unit test coding practices

April 20, 2009

To clearly see the purpose of a unit test, I like to see something that’s short, concise and to the point. One or two lines of setup, one line to invoke the method under test, followed by one or two lines of assertion would be an ideal unit test and easy to follow. We would use several refactoring motifs to achieve this goal. I will use JUnit4.4 syntax to list a few of these motifs:

  • Use methods with the @Before and @After annotations for setup and teardown that’s common to all (or most) tests in the test class.
  • Refactor any long setup code into well-named helper methods so that the test method itself remains small.
  • Refactor any long assertion code into well-named helper methods.
  • Override the .equals() method on objects so that you could possibly compress several lines of assertions into one line: assertThat(result, equalTo(expected));

This is where I run into trouble however:

  • The test class gets so big that while writing a new test, I have trouble figuring out if the setup I need (or even a part of it) has already been written in the utility methods already present. This results in either a big waste of time while I figure out what each utility method does, or duplication of code.
  • When an existing test class already contains tests which don’t have helper methods, what should I do? Should I copy-paste setup from other tests to be done with my new test, or should I take the time to do a proper refactor so that I clean up the existing tests before starting my new one? If I go with the second option, I have to be careful of slight differences in setup code from one test to the next. Since the test code isn’t clean to begin with, it is also difficult to determine the purpose of each test, making it harder to simplify.

Obviously time needs to be taken out to refactor the test class (and quite possibly the class under test) and make it easier to read / modify, possibly by splitting it into multiple smaller classes. Unfortunately, it is a lot harder to justify devoting time to refactoring test classes than it is to refactoring production code. But is the added technical debt any less significant?

2 comments

  1. Is this a sign that the class/method being tested is too complex with too many things that need setting up? Perhaps it’s one of those code “smells”?


  2. It is a code smell – and yes, what you suggest could definitely be a possibility!



Leave a reply to Adrian Cancel reply