Unit Tests Guidelines

Paul Stanescu
3 min readDec 7, 2022

Importance of Unit Tests

Covering your code with unit tests is a way to prove that you managed to handle all the proposed scenarios. You may have complex cases hard to test manually or cases that imply time-consuming steps in order to create data in the system so that you can replicate the scenario. The best approach to tackle these issues is by writing unit tests so that you have no test case limits and you can cover everything.

From time to time, all of us, are doing refactoring, for different reasons. How can we ensure that the code works as before (in terms of covering user cases) and that we managed to touch only the intended area? Again by unit test, it will be our quality gate. Once you found an issue/bug write a unit test that will fail until you solve the problem. As soon as the issue is solved the test will automatically pass and you will make sure that this scenario will not fail again in the future.

For writing quality unit tests you have to follow a few simple rules:

Clarity

Test functions should clearly state what we are testing.
One of the most readable, used and recommended format is the one with GIVEN — WHEN — THEN. Any other forms are accepted, as long as it is clear what we are testing.

Simplicity

The test code should be simple. Writing complex tests shows that the actual code that needs to be tested needs to be refactored.

The body of the test function should have 3 sections:

  • initialization section, where we prepare the data
  • execution section, where we call the function we are testing
  • assertion section, where we assert the result against the expected data or where we check if mocked functions are correctly called.

Mocking

We should never mock the class which we are testing because it would defeat the purpose of testing. We should only mock the dependencies of the tested class so that we would be able to “force out” the needed values that the mocked function should provide for our tests.

Test the real flow

Let’s assume that you have a public method/function which calls a few private methods/functions. Private methods/functions should be NEVER tested independently. By testing the public method/function we will reach anyway the private ones and the code that is calling the public method/function doesn’t have access to the private one, then the real flow is the one mentioned.

One Assert per Test

According to Uncle Bob, “There is a school of thought that says that every function in a JUnit test should have one and only one assert statement”. This also applies when checking the call of mocked functions. For example, if we have the following code:

class MyMapper {

fun map(input: Int) {
when (input) {
INTERIOR -> MyEnum.MY_INTERIOR
EXTERIOR -> MyEnum.MY_EXTERIOR
else -> MyEnum.INVALID
}
}
}

our tests could look like this:

class MyMapperTest {

/**
* GIVEN:
* INTERIOR
* WHEN:
* map is called for given value
* THEN:
* the result should be MY_INTERIOR
*/
@Test
fun testMapperInterior_expectingMyInterior() {
val result = myMapper.map(INTERIOR)

assertEquals(MyEnum.MY_INTERIOR, result)
}

Most of the time it is in our habit to put everything together, therefore we are tempted to write these into a single test. It’s highly recommended to separate them into different tests as each input could be seen as a different scenario or use case.

F.I.R.S.T principles

Fast

Tests should run quickly.

Independent

Tests should not depend on each other; one test should not set up the conditions for the next test.

Repeatable

Tests should be repeatable in any environment.

Self-Validating

Tests should have a boolean output: they either pass or fail, it cannot be in between. The rule is simple, green or red…

Timely

Tests need to be written in a timely fashion and just before the production code that makes them pass.

Just code it!

--

--