Best practices for writing unit tests

Jonas Schaude
4 min readSep 27, 2022

Writing useful unit tests will help you to save time and money. With this article, I want to give you best practices how to write useful unit tests. The following best practices are based on my own experience. They are platform and programming language independent and can easily be adapted in any software project.

What is a unit test?

To understand what a unit test is, we first of all have to clarify what exactly a unit it. In an object oriented programming language, a unit can be seen as a single function. It is the smallest piece of code in a system. A unit test is thereby testing this single unit. This single unit is often also called service under test.
There are in general different types of tests. The most popular ones are unit tests, integration tests and ui tests. They all can be grouped based on their granularities (level of isolation as well as execution time) into different clusters as you can see in the test pyramid. Within this article, I am going to focus on unit tests.

Why should we write unit tests?

Writing unit tests brings a lot of advantages. The most obvious advantage is that they are helping to quickly figure out if our units are working as intended or if they are broken.
In addition to this, they can also be seen as documentation for our source code. Reading unit tests will help us to understand what the units are supposed to do.
Furthermore they can help us to detect code smell in our source code. Having issues writing unit tests could be a sign that the unit we want to test might be too complex. It could then make sense to simplify the whole unit by splitting it up into different (smaller) units.

Best practices writing unit tests

1. Write isolated unit tests

Unit tests have to be fast and deterministic. They should provide fast and reliable feedback if a unit is working or not. If the test passes, it should always pass. If it fails, it should always fail. Independent of the daytime or the computers performance. That is why we need to make sure that our unit is highly isolated and is not affected by any kind of side effects. To setup an isolated unit, you can use mocks and stubs. Mocks and stubs are for example replacing real functions with fake versions of the same thing. The fake version is acting like the real thing but answers with responses that are predefined in the test setup of the unit test.

If you have problems setting up an isolated unit test, think about refactoring your source code. The unit you are trying to test might be too complex and could be splitted up. Keep clean code in mind.

Another great way that helps you writing isolated tests is test driven development. Test driven development means that you are first writing your test, then adding the source code to make the test pass and afterwards refactor the source code. After that, you will continue with the next unit test and start with the same process again until you implemented the whole business logic. By doing so, you will create well tested source code that behaves as you like.

2. Write well structured unit tests

Make your unit tests easy to understand by using the Arrange-Act-Assert pattern. The idea is to break down the unit test into three different parts by using arranging and formatting:

  • Arrange: Set up the test data and define the expected result. Defining the expected result in the arrange part forces you to think about the expected result before calling the service unter test and have the input with the expected result in one central place.
  • Act: Call your service unter test and store its result.
  • Assert: Validate that your received result equals your expected result.

Let us have a look at an example.

Implementation that we are going to unit test

An unit test verifying that the add function works as expected could then look like the following snipped:

Unit test example using the Arrange-Act-Assert pattern

As you can see, following the Arrange-Act-Assert pattern makes the unit test easy to read and understand.

3. Write readable and self explaining unit tests

The testing name will be the first thing that you will see when your unit test is failing. By reading the testing name, you should easily understand what exactly the unit test is supposed to do and why it is failing. Because of that, it is important to put enough information into the test name. Do not be afraid of long and descriptive test names.

Unit tests should also serve as code documentation for the software project. Keep them small, simple and easy to understand. Your colleagues should be able to quickly understand what the unit is supposed to do. In addition, think about clean code when writing unit tests. Try to avoid complex logic in unit tests. Sometimes, it might be necessary to have complex assertions in place. It could then make sense to create your own custom assertion functions for the unit test assertions. When doing so, keep in mind to also write unit tests for those.

4. Test only one single condition per unit test

If possible, avoid having multiple assertions in your unit test. Unit tests should help us to quickly identify broken source code. Therefore it is important to keep them readable and easy to analyse.
Some testing frameworks are for example aborting after the first broken assertion. Imagine an unit test having multiple different assertions in place. It would then be unnecessary difficult to analyse what exactly is broken.

Thanks for reading and I hope this helps you. Feel free to share and tap on clap button, if you find it useful.

--

--