The Five Habits of Maintainable Unit Tests

I like unit tests. They’re often the best documentation of a project’s behavior. They provide assurance that code modifications haven’t broken anything.

But too many or poorly-written tests can have the undesirable effect of cementing code in place, making it more difficult to change. The following are some practical things I keep in mind when writing unit tests.

1. Be Descriptive

As usual, naming things is hard. But a test name that concisely describes everything that the test will assert is helpful in two ways. It serves to document the case, and it keeps the test focused. (If your test name is becoming too long to include everything, that’s a sign that the test is doing too much and you should think about breaking it up into multiple tests.)

2. Stay Focused

Unit tests are also subject to the single-responsibility principle. Each test case should cover one thing and one thing only. That doesn’t mean that it should have only a single assert statement, just that it should verify one aspect of expected behavior.

3. Clean ’em Up

When starting out with a blank file, it’s easy to just start writing test cases as you think of them. This may be fine for a handful of tests, but as the class grows, it will become difficult to figure out what tests have already been written.

I’ve found it works well to group tests by method name. (Even if the test calls multiple methods, there’s usually one that’s the primary focus.) Some test frameworks support this easily using statements like “context” or “describe,” while others may require using nested classes. Prefixing the test name also works; e.g. “MyMethodName_ExcellentDescriptionOfTestCaseHere.”

4. Make Stuff Up

Substitute obviously fake or random data for any values that are required but irrelevant for the case being tested, and move this sort of stuff out into helper methods so that the test case is as light as possible. Extraneous or real-looking setup may confuse the intention of the test. Just be careful to not hide important details in helper methods.

5. Don’t Repeat Yourself

A unit test should involve more than repeating the implementation using different words. You should also avoid using production code as test helpers. This will help you avoid situations where you have to figure out whether a failure is due to a bad implementation or a bad test helper.

What other habits have you found to keep your tests in good shape?