Migrate to JUnit 5
JUnit 5 (also known as JUnit Jupiter) was released in 2017 and introduced significant improvements over JUnit 4. Migrating from JUnit 4 to JUnit 5 involves updating imports, annotations, and taking advantage of new features.
Package changes
JUnit 5 uses different package names and imports compared to JUnit 4.
- Before
- After
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.Ignore;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;
public class CalculatorTest {
@BeforeClass
public static void setUpClass() {
// Runs once before all tests
}
@Before
public void setUp() {
// Runs before each test
}
@Test
public void testAddition() {
assertEquals(4, 2 + 2);
}
@Ignore("Not implemented yet")
@Test
public void testSubtraction() {
assertTrue(true);
}
@After
public void tearDown() {
// Runs after each test
}
@AfterClass
public static void tearDownClass() {
// Runs once after all tests
}
}
JUnit 4 uses org.junit.*
packages and requires public visibility for test classes and methods.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Disabled;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class CalculatorTest {
@BeforeAll
static void setUpClass() {
// Runs once before all tests
}
@BeforeEach
void setUp() {
// Runs before each test
}
@Test
void testAddition() {
assertEquals(4, 2 + 2);
}
@Disabled("Not implemented yet")
@Test
void testSubtraction() {
assertTrue(true);
}
@AfterEach
void tearDown() {
// Runs after each test
}
@AfterAll
static void tearDownClass() {
// Runs once after all tests
}
}
JUnit 5 uses org.junit.jupiter.api.*
packages, allows package-private visibility, and has more consistent naming:
@Before
→@BeforeEach
@After
→@AfterEach
@BeforeClass
→@BeforeAll
@AfterClass
→@AfterAll
@Ignore
→@Disabled
Exception testing
JUnit 5 introduces assertThrows()
to replace the awkward @Test(expected=...)
pattern from JUnit 4.
- Before
- After
import org.junit.Test;
public class ExceptionTest {
@Test(expected = IllegalArgumentException.class)
public void shouldThrowException() {
throw new IllegalArgumentException("Invalid argument");
}
}
The @Test(expected=...)
pattern doesn't allow you to verify the exception message or other properties.
Any code in the test method can throw the exception, making it unclear which statement is expected to fail.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class ExceptionTest {
@Test
void shouldThrowException() {
Exception ex = assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("Invalid argument");
});
assertEquals("Invalid argument", ex.getMessage());
}
}
JUnit 5's assertThrows()
makes it clear which code is expected to throw the exception and allows you to verify exception properties.
Automated migration
Manual migration can be tedious and error-prone. OpenRewrite provides automated migration recipes.
- Moderne CLI
- Maven Command Line
- Maven POM
- Gradle init script
- Gradle
- IntelliJ IDEA Ultimate
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.
- 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:
mod build ~/workspace/
- If the recipe is not available locally yet, then you can install it once using:
mod config recipes jar install org.openrewrite.recipe:rewrite-testing-frameworks:LATEST
- Run the recipe.
mod run ~/workspace/ --recipe org.openrewrite.java.testing.junit5.JUnit4to5Migration
You will need to have Maven installed on your machine before you can run the following command.
mvn -U org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-testing-frameworks:RELEASE -Drewrite.activeRecipes=org.openrewrite.java.testing.junit5.JUnit4to5Migration -Drewrite.exportDatatables=true
You may add the plugin to your pom.xml
file, so that it is available for all developers and CI/CD pipelines.
- Add the following to your
pom.xml
file:
<project>
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>LATEST</version>
<configuration>
<exportDatatables>true</exportDatatables>
<activeRecipes>
<recipe>org.openrewrite.java.testing.junit5.JUnit4to5Migration</recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-testing-frameworks</artifactId>
<version>LATEST</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
- Run the recipe.
mvn rewrite:run
Gradle init scripts are a good way to try out a recipe without modifying your build.gradle
file.
- Create a file named
init.gradle
in the root of your project.
initscript {
repositories {
maven { url "https://plugins.gradle.org/m2" }
}
dependencies { classpath("org.openrewrite:plugin:latest.release") }
}
rootProject {
plugins.apply(org.openrewrite.gradle.RewritePlugin)
dependencies {
rewrite("org.openrewrite.recipe:rewrite-testing-frameworks:latest.release")
}
rewrite {
activeRecipe("org.openrewrite.java.testing.junit5.JUnit4to5Migration")
setExportDatatables(true)
}
afterEvaluate {
if (repositories.isEmpty()) {
repositories {
mavenCentral()
}
}
}
}
- Run the recipe.
gradle --init-script init.gradle rewriteRun
You can add the plugin to your build.gradle
file, so that it is available for all developers and CI/CD pipelines.
- Add the following to your
build.gradle
file:
plugins {
id("org.openrewrite.rewrite") version("latest.release")
}
rewrite {
activeRecipe("org.openrewrite.java.testing.junit5.JUnit4to5Migration")
setExportDatatables(true)
}
repositories {
mavenCentral()
}
dependencies {
rewrite("org.openrewrite.recipe:rewrite-testing-frameworks:latest.release")
}
- Run
gradle rewriteRun
to run the recipe.
You can run OpenRewrite recipes directly from IntelliJ IDEA Ultimate, by adding a rewrite.yml
file to your project.
---
type: specs.openrewrite.org/v1beta/recipe
name: com.github.timtebeek.AdoptJUnitJupiter
displayName: Adopt JUnit Jupiter
description: Adopt JUnit Jupiter and apply best practices to assertions.
recipeList:
- org.openrewrite.java.testing.junit5.JUnit4to5Migration
After adding the file, you should see a run icon in the left margin offering to run the recipe.
OpenRewrite's JUnit4to5Migration recipe automatically handles:
- Updating imports and packages
- Converting annotations (
@Before
→@BeforeEach
, etc.) - Migrating exception testing patterns
- Updating assertions to JUnit 5 equivalents
- Converting JUnit 4 rules to JUnit 5 extensions