Unit Testing Guidelines

Aug 06, 2018 - 3 min read

Goal of unit testing is fundamentally different than with other kinds of tests, such as integration or feature tests. Many resources on the web already cover basic principles and benefits of unit testing, so there is no need to go into much detail here.

Before moving on to guidelines, let’s quickly recap on:

What unit tests do for youWhat unit tests DON’T do for you
Detect issues early in the development cycleGuarantee bug-free code
Provide a safety net when refactoringImprove quality of the codebase
Ease integration of componentsEliminate the need for QA
Document how classes and methods should be used

Guidelines

Not everything needs to be tested

Most important things you should test are: business logic, utilities and helpers.

You should NOT test vendor libraries and framework components. For example, if:

  • Database engine stores/updates data successfully
  • REST client sends a request to the server
  • A List contains an item after calling List.Add(item) (unless it’s your own list implementation!)

Vendors write their own tests to make sure their library works, so there is no need for you to do it.

Code coverage is not an indicator of test quality

Code coverage is an indicator of whether your code was executed by unit tests, not if it works properly.

I’ve worked on a project where 95% of the code were tool-generated classes for a custom language parser. There was no need to test them, so the code coverage was low. Even though our code was covered, bosses weren’t happy.

The solution?

By running example files through the parser, we boosted code coverage to more than 90%, but it added no value except for pretty numbers in the reports.

Don’t test too much in a single test

If you have many different assertions inside a unit test that fails, it won’t be obvious what exactly went wrong. Consider splitting large tests into multiple smaller ones.

  • A unit test should be focused on a single functionality
  • Tests should be easy to read and self-contained

Note: Forking logic means more tests, not more assertions. If a method can take more than one path with an if, switch or by other means, every path should be tested with separate tests.

Unit Tests must be repeatable

A test needs to be completely isolated from other tests (not require other tests to run in order for it to pass).

Test for 0, 1 and 3 cases

If arrays are involved, a good starting point is to test with 0, 1, and 3 items. Those are common edge cases, and you can add more if required.

Write testable code

Writing tests first, or at least thinking about testability when writing code goes a long way:

  • Prefer short methods that do only one thing
  • Prefer pure methods (ones that do not touch shared state)
  • Accept only what you actually need as parameters to a method
  • Separate common code into helper classes

Final word

Treat your tests as regular production code, and apply best practices when writing them. Unit tests should be maintained, refactored, documented, reviewed, and regularly executed to make sure they always pass.