Migrate to JUnit 5 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 5. 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.
If your project is a Spring or Spring-Boot project take a dependency on rewrite-spring and activate the SpringBoot2JUnit4to5Migration recipe:
Maven
Gradle
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>4.46.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>4.35.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
<build>
build.gradle
plugins {
id("java")
id("org.openrewrite.rewrite") version("5.40.0")
}
rewrite {
activeRecipe("org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration")
}
repositories {
mavenCentral() // rewrite-spring is published to Maven Central
}
dependencies {
rewrite(platform("org.openrewrite.recipe:rewrite-recipe-bom:1.19.0"))
rewrite("org.openrewrite.recipe:rewrite-spring")
// Other project dependencies
}
SpringBoot2JUnit4to5Migration
is a superset of the normal JUnit 4 to 5 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 take a dependency on rewrite-testing-frameworks and activate the JUnit5BestPractices recipe:
Maven
Gradle
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>4.46.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>1.36.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
build.gradle
plugins {
id("java")
id("org.openrewrite.rewrite") version("5.40.0")
}
rewrite {
activeRecipe("org.openrewrite.java.testing.junit5.JUnit5BestPractices")
}
repositories {
mavenCentral() // rewrite-testing-frameworks is published to Maven Central
}
dependencies {
implementation(platform("org.openrewrite.recipe:rewrite-recipe-bom:1.19.0"))
rewrite("org.openrewrite.recipe:rewrite-testing-frameworks")
// Other project dependencies
}
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.JUnit 4 Test Class (Before)
JUnit 5 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.7.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Dependency management for Gradle is not currently available but this feature is on OpenRewrite's roadmap.
Not every JUnit 4 feature or library has a direct JUnit 5 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 |
---|
The JUnit5 equivalent to JUnit4 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.
Last modified 7d ago