Writing Clean Tests - Best Practices

As with writing clean code, there are some widely recognised best practices for writing clean Tests. It’s advised to follow such rules so that our tests look clear and cohesive, which makes code analysis for new team members much easier since analysis of application functionalities through clearly written tests is the best way to get a grasp on the applications logic.

In this article we will cover how to structure tests, best naming conventions and other general advice on Unit testing with Java in Spring.

General

Separate package

It’s a good idea to keep the test classes in a separate package from the source code.

Package naming convention

The package name of the test class should match the package name of the source class.

Write small and specific tests

Use helper functions, parametrized tests, AssertJ’s powerful assertions, asserting only what’s relevant and avoiding one test for all corner cases.

Write self-contained tests

Reveal all relevant parameters, insert data right in the test and prefer composition over inheritance.

Test close to production

Focus on testing a complete vertical slide. Testing each class in isolation by using mocks is a common testing recommendation. However, it has drawbacks: You are not testing all classes in integration and refactorings of the internals will break all tests, because there is a test for each internal class. And finally, you have to write and maintain multiple tests.

A careful balance should be made when deciding which part of the code to cover with Unit and which with Integration tests.

Mock external services

Although unit tests concentrate on specific and smaller pieces of code, there is a chance that the code is dependent on external services for some logic. Therefore, we should mock the external services and merely test the logic and execution of our code for varying scenarios.

We can use Mockito for mocking external services.

Avoid In-memory DB

Using an in-memory database (e.g. H2) for tests reduces the reliability and scope of your tests. The in-memory database and the database used in production behave differently and may return different results.

Structure

Arrange, Act, Assert (AAA)

A test should contain three blocks. Each block of code should be as short as possible. Use subfunctions to shorten these blocks.

  • Arrange (Input): Test data preparation.

  • Act (Action): Call the method or action that you like to test, mock the services you need to mock.

  • Assert (Output): Assertions to verify the actual against expected result.

Expected vs Actual

Prefix the variables with “actual” and “expected”. This increases the readability and clarifies the intention of the variable. Moreover, it’s harder to mix them up.

Specific Unit Tests

Avoid asserting multiple scenarios in the same test. Moreover, keep in mind what you have already tested in former tests. You usually don’t have to repeat assertions.

Avoid asserting multiple scenarios in the same test so in the case of test failures, it’ll be easier to determine which specific scenario failed. Therefore, write a unit test to test a single specific scenario.

Naming

Test case naming convention

The test names should be insightful, and users should understand the behavior and expectation of the test by just glancing at the name itself.

For example, the name of our unit test was testUserAccess doesn’t provide any meaningful information on what is actually being tested.

However, if we rename the test method into ifUserAccessAdmin_thenOk we know what the test method is testing just by looking at the method name which increases readability.

Refer to this article for additional Unit test naming conventions: 7 Popular Strategies: Unit Test Naming Conventions - DZone

Last updated