Skip to main content

JUnit Jupiter best practices

After migrating to JUnit 5, there are several common patterns that should be cleaned up to make tests more modern and maintainable.

Remove test prefix

In JUnit 3, test methods had to start with test because the framework used reflection to discover tests by name. JUnit 4 and 5 use annotations, so this prefix is no longer necessary and just adds noise.

CalculatorTest.java
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class CalculatorTest {

@Test
void testAddition() {
assertEquals(4, 2 + 2);
}

@Test
void testSubtraction() {
assertEquals(0, 2 - 2);
}

@Test
void testMultiplication() {
assertEquals(4, 2 * 2);
}
}
warning

The test prefix is a remnant from JUnit 3 and adds unnecessary noise to test method names.

Remove public modifier

JUnit 4 required test classes and methods to be public, but JUnit 5 removed this requirement. Package-private (default) visibility is sufficient and reduces boilerplate.

CalculatorTest.java
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class CalculatorTest {

private Calculator calculator;

@BeforeEach
public void setUp() {
calculator = new Calculator();
}

@Test
public void addition() {
assertEquals(4, calculator.add(2, 2));
}

@Test
public void subtraction() {
assertEquals(0, calculator.subtract(2, 2));
}
}
warning

The public modifiers on the class and test methods are unnecessary in JUnit 5 and add visual clutter.

Apply best practices

Beyond basic cleanup, there are many other best practices to apply when modernizing tests.

UserServiceTest.java
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

class UserServiceTest {

@Test
void testUserCreation() {
UserService service = new UserService();
User user = service.createUser("John", "Doe");

assertNotNull(user);
assertEquals("John", user.getFirstName());
assertEquals("Doe", user.getLastName());
assertTrue(user.getId() > 0);
}
}
info

While this test works, it could be improved with better naming, display names, and more expressive assertions.

Automated modernization

OpenRewrite provides recipes to automatically apply these modernizations.

The Moderne CLI allows you to run OpenRewrite recipes on your project without needing to modify your build files, against serialized Lossless Semantic Tree (LST) of your project for a considerable performance boost & across projects.

You will need to have configured the Moderne CLI on your machine before you can run the following command.

  1. If project serialized Lossless Semantic Tree is not yet available locally, then build the LST. This is only needed the first time, or after extensive changes:
shell
mod build ~/workspace/
  1. If the recipe is not available locally yet, then you can install it once using:
shell
mod config recipes jar install org.openrewrite.recipe:rewrite-testing-frameworks:LATEST
  1. Run the recipe.
shell
mod run ~/workspace/ --recipe org.openrewrite.java.testing.junit5.JUnit5BestPractices
tip

OpenRewrite recipes automatically handle: