Migrate to JUnit Jupiter from JUnit 4
In this tutorial, we'll use OpenRewrite to perform an automated migration from the venerable JUnit 4 testing framework to its successor JUnit Jupiter. JUnit is a popular tool that many other libraries and frameworks interact with. OpenRewrite supports some of the popular integrations, such as Mockito and Spring-Boot, out of the box.
Example Configuration
If your project is a Spring or Spring-Boot project, activate the SpringBoot2JUnit4to5Migration recipe:
This recipe has no required configuration options. It can be activated by adding a dependency on `org.openrewrite.recipe:rewrite-spring` in your build file or by running a shell command (in which case no build changes are needed):
- Gradle
- Gradle init script
- Maven POM
- Maven Command Line
- Moderne CLI
- Add the following to your
build.gradlefile:build.gradleplugins {
id("org.openrewrite.rewrite") version("latest.release")
}
rewrite {
activeRecipe("org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration")
}
repositories {
mavenCentral()
}
dependencies {
rewrite("org.openrewrite.recipe:rewrite-spring:6.28.0")
} - Run
gradle rewriteRunto run the recipe.
- Create a file named
init.gradlein the root of your project.init.gradleinitscript {
repositories {
maven { url "https://plugins.gradle.org/m2" }
}
dependencies { classpath("org.openrewrite:plugin:7.29.0") }
}
rootProject {
plugins.apply(org.openrewrite.gradle.RewritePlugin)
dependencies {
rewrite("org.openrewrite.recipe:rewrite-spring:6.28.0")
}
rewrite {
activeRecipe("org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration")
}
afterEvaluate {
if (repositories.isEmpty()) {
repositories {
mavenCentral()
}
}
}
} - Run the recipe.shell
gradle --init-script init.gradle rewriteRun
- Add the following to your
pom.xmlfile:pom.xml<project>
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>6.35.0</version>
<configuration>
<activeRecipes>
<recipe>org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration</recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-spring</artifactId>
<version>6.28.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project> - Run
mvn rewrite:runto run the recipe.
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 --define rewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:RELEASE --define rewrite.activeRecipes=org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration
You will need to have configured the Moderne CLI on your machine before you can run the following command.
mod run . --recipe SpringBoot2JUnit4to5Migration
If the recipe is not available locally, then you can install it using:
mod config recipes jar install org.openrewrite.recipe:rewrite-spring:6.28.0
SpringBoot2JUnit4to5Migration is a superset of the normal JUnit 4 to JUnit Jupiter and Mockito 1 to 3 recipes, with some additional Spring-specific functionality. If you activate this recipe it is not necessary to also activate the base JUnit or Mockito migration recipes.
If your project is not a Spring or Spring-Boot project, activate the JUnit5BestPractices recipe:
This recipe has no required configuration options. It can be activated by adding a dependency on `org.openrewrite.recipe:rewrite-testing-frameworks` in your build file or by running a shell command (in which case no build changes are needed):
- Gradle
- Gradle init script
- Maven POM
- Maven Command Line
- Moderne CLI
- Add the following to your
build.gradlefile:build.gradleplugins {
id("org.openrewrite.rewrite") version("latest.release")
}
rewrite {
activeRecipe("org.openrewrite.java.testing.junit5.JUnit5BestPractices")
}
repositories {
mavenCentral()
}
dependencies {
rewrite("org.openrewrite.recipe:rewrite-testing-frameworks:3.31.0")
} - Run
gradle rewriteRunto run the recipe.
- Create a file named
init.gradlein the root of your project.init.gradleinitscript {
repositories {
maven { url "https://plugins.gradle.org/m2" }
}
dependencies { classpath("org.openrewrite:plugin:7.29.0") }
}
rootProject {
plugins.apply(org.openrewrite.gradle.RewritePlugin)
dependencies {
rewrite("org.openrewrite.recipe:rewrite-testing-frameworks:3.31.0")
}
rewrite {
activeRecipe("org.openrewrite.java.testing.junit5.JUnit5BestPractices")
}
afterEvaluate {
if (repositories.isEmpty()) {
repositories {
mavenCentral()
}
}
}
} - Run the recipe.shell
gradle --init-script init.gradle rewriteRun
- Add the following to your
pom.xmlfile:pom.xml<project>
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>6.35.0</version>
<configuration>
<activeRecipes>
<recipe>org.openrewrite.java.testing.junit5.JUnit5BestPractices</recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-testing-frameworks</artifactId>
<version>3.31.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project> - Run
mvn rewrite:runto run the recipe.
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 --define rewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-testing-frameworks:RELEASE --define rewrite.activeRecipes=org.openrewrite.java.testing.junit5.JUnit5BestPractices
You will need to have configured the Moderne CLI on your machine before you can run the following command.
mod run . --recipe JUnit5BestPractices
If the recipe is not available locally, then you can install it using:
mod config recipes jar install org.openrewrite.recipe:rewrite-testing-frameworks:3.31.0
At this point, you're ready to execute the migration by running mvn rewrite:run or gradlew rewriteRun. After running the migration you can inspect the results with git diff (or equivalent), manually fix anything that wasn't able to be migrated automatically, and commit the results.
Before and After
For the full list of changes, this recipe will make, see its reference page.
- JUnit 4 Test Class (Before)
- JUnit Jupiter Test Class (After)
package org.openrewrite.example;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.Test;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.Timeout;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class ExampleJunitTestClass {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Before
public void before() {
}
@AfterClass
public static void afterClass() {
}
@Test(expected = RuntimeException.class)
public void foo() throws IOException {
File tempFile = folder.newFile();
File tempFile2 = folder.newFile("filename");
File tempDir = folder.getRoot();
File tempDir2 = folder.newFolder("parent", "child");
File tempDir3 = folder.newFolder("subdir");
File tempDir4 = folder.newFolder();
String foo = "foo";
throw new RuntimeException(foo);
}
@Test(expected = IndexOutOfBoundsException.class)
public void foo2() {
int arr = new int[]{}[0];
}
@Rule
public ExpectedException throwz = ExpectedException.none();
@Test
public void foo3() {
throwz.expect(RuntimeException.class);
throw new RuntimeException();
}
@Test
public void assertsStuff() {
Assert.assertEquals("One is one", 1, 1);
Assert.assertArrayEquals("Empty is empty", new int[]{}, new int[]{});
Assert.assertNotEquals("one is not two", 1, 2);
Assert.assertFalse("false is false", false);
Assert.assertTrue("true is true", true);
Assert.assertEquals("foo is foo", "foo", "foo");
Assert.assertNull("null is null", null);
Assert.fail("fail");
}
@Test(timeout = 500)
public void bar() { }
}
package org.openrewrite.example;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.io.TempDir;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ExampleJunitTestClass {
@TempDir
File folder;
@BeforeEach
void before() {
}
@AfterAll
static void afterClass() {
}
@Test
void foo() throws IOException {
assertThrows(RuntimeException.class, () -> {
File tempFile = File.createTempFile("junit", null, folder);
File tempFile2 = newFile(folder, "filename");
File tempDir = folder;
File tempDir2 = newFolder(folder, "parent", "child");
File tempDir3 = newFolder(folder, "subdir");
File tempDir4 = Files.createTempDirectory(folder.toPath(), "junit").toFile();
String foo = "foo";
throw new RuntimeException(foo);
});
}
@Test
void foo2() {
assertThrows(IndexOutOfBoundsException.class, () -> {
int arr = new int[]{}[0];
});
}
@Test
void foo3() {
assertThrows(RuntimeException.class, () -> {
throw new RuntimeException();
});
}
@Test
void assertsStuff() {
Assertions.assertEquals(1, 1, "One is one");
Assertions.assertArrayEquals(new int[]{}, new int[]{}, "Empty is empty");
Assertions.assertNotEquals(1, 2, "one is not two");
Assertions.assertFalse(false, "false is false");
Assertions.assertTrue(true, "true is true");
Assertions.assertEquals("foo", "foo", "foo is foo");
Assertions.assertNull(null, "null is null");
Assertions.fail("fail");
}
@Test
@Timeout(500)
void bar() {
}
private static File newFile(File root, String fileName) throws IOException {
File file = new File(root, fileName);
file.createNewFile();
return file;
}
private static File newFolder(File root, String ... folders) throws IOException {
File result = new File(root, String.join("/", folders));
if (!result.mkdirs()) {
throw new IOException("Couldn't create folders " + root);
}
return result;
}
}
- Maven pom (Before)
- Maven pom (After)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.openrewrite.example</groupId>
<artifactId>integration-testing</artifactId>
<version>1.0</version>
<name>integration-testing</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.openrewrite.example</groupId>
<artifactId>integration-testing</artifactId>
<version>1.0</version>
<name>integration-testing</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Dependency management for Gradle is not currently available but this feature is on OpenRewrite's roadmap.
Known Limitations
Not every JUnit 4 feature or library has a direct JUnit Jupiter equivalent. In these cases, manual changes will be required after the automation has run. This list is not exhaustive. See the rewrite-testing-frameworks issue tracker.
| Unsupported Functionality |
|---|
| PowerMock has no JUnit Jupiter equivalent |
| The JUnit Jupiter equivalent to JUnit 4 ClassPathSuite is not yet released |
| org.junit.ComparisonFailure |
| org.junit.MethodRule |
| TestRule, TestWatcher, and Description |
Your codebase may also have custom JUnit 4 Rules or Runners that will not be migrated automatically by our recipes. If your codebase has a lot of customized JUnit 4 extensions, consider writing your own recipe to handle those and running it alongside this migration.
If you discover a shortcoming of this migration that should be covered, file an issue or submit a pull request on the rewrite-testing-frameworks GitHub project.
See how this recipe works across multiple open-source repositories
Run this recipe on OSS repos at scale with the Moderne SaaS.

The community edition of the Moderne platform enables you to easily run recipes across thousands of open-source repositories.
Please contact Moderne for more information about safely running the recipes on your own codebase in a private SaaS.