Unit Test Writing Guideline
This guide provides valuable tips to anyone involved in creating unit tests. A good unit test should be easy to understand, reliable, quick to run, and without dependencies.
For general information about unit tests, see Unit Test Overview.
Intended for
Software engineers and test engineers.
Overview
When writing unit tests, the following four characteristics should be kept in mind:
- Readable
- Trustworthy
- Fast
- Maintainable
These characteristics are equally important and none of them can be removed without jeopardizing the result.
Readable
- Tests should be easy to locate (file structure, how to find a test for a class or method).
- Tests should be self-contained, like a little story. Write simple code: do this, call that, assert this.
- Test one thing only.
- Avoid magic values.
- Care a lot about naming the variables (examples…).
- Do not prefix with the test if not required by your framework.
- Method names for the test (are the only ones where we should not care about the length):
- You will never have to type them a second time
- Use underscore (if not forbidden by your coding guideline) rather than Camel Case (joiningWordsWithoutSpaces), it is easier to read when names get long.
- Naming the test is not that hard. It requires three things:
- The thing that is tested.
- Scenario, state under test.
- Expected behavior/return value.
- Where to put this information depends on the framework. Usually, the name of the method is good enough.
- Add an index for the same test but with different parameters.
Trustworthy
- Never accept a failing test.
- Never accept a sporadically failing test.
- Code coverage is worthless without test code review.
- Avoid any logic in test code.
- Avoid any logic in asserts (more often than anticipated, this is copy and paste business logic).
- Stupid, readable code. Hardcoded values (especially in asserts).
- Never use resources that keep changing (time, random, threads, etc.).
- A good strategy during peer review is to introduce a bug in the code and run the unit test, expecting it to fail.
Fast
This is obvious, but a critical property nonetheless to get fast turnaround times during development.
A slow unit test is unlikely to be called frequently, but it should!
Maintainable
- Starting from the public interface, they usually start some use cases in the system
- Testing private API makes the test brittle. Also, there needs to be a public method that eventually calls a private method anyway
There shall be no dependency between tests. Each test must be consistent, running alone, together, or in any order.
If there is common work for multiple tests:- Create objects using common methods (factory, make_XX()).
- Manipulate objects / initial state using common methods (init_XX()).
- Run common tests in common methods (verify_XX()).
- Separation:
- A single thing per test:
- Only one mock object per test (but you can have stub objects just for faking, not asserting).
- Tests are hard to name/describe when not separated.
- Multiple asserts are a code smell, especially on different objects.
- Each test should fail individually (do not exit early as soon as one fails; you always want to get the full picture. Other tests may pass and hint at the problem's nature). This will likely lead to more debugging if tests are not separated.
- A single thing per test:
References
Owner: Software Development Team