As described before, Jupiter does not support JUnit 4 rules natively. Nevertheless, the JUnit 5 team realized that JUnit 4 rules are widely adopted in many test codebases nowadays. In order to provide a seamless migration from JUnit 4 to JUnit 5, the JUnit 5 team implemented the junit-jupiter-migrationsupport module. If this module is going to be used in a project, the module dependency should be imported. Examples for Maven are shown here:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
The Gradle declaration for this dependency is like this:
dependencies {
testCompile("org.junit.jupiter:junit-jupiter-
migrationsupport:${junitJupiterVersion}")
}
The rule support in JUnit 5 is limited to those rules semantically compatible with the Jupiter extension model, including the following rules:
- junit.rules.ExternalResource (including org.junit.rules.TemporaryFolder).
- junit.rules.Verifier (including org.junit.rules.ErrorCollector).
- junit.rules.ExpectedException.
In order to enable these rules in Jupiter tests, the test class should be annotated with the class-level annotation @EnableRuleMigrationSupport (located in the package org.junit.jupiter.migrationsupport.rules). Let us see several examples. First, the following test case defines and uses a TemporaryFolder JUnit 4 rule within a Jupiter test:
package io.github.bonigarcia;
import java.io.IOException;
import org.junit.Rule;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;
import org.junit.rules.TemporaryFolder;
@EnableRuleMigrationSupport
class TemporaryFolderRuleTest {
@Rule
TemporaryFolder temporaryFolder = new TemporaryFolder();
@BeforeEach
void setup() throws IOException {
temporaryFolder.create();
}
@Test
void test() {
System.out.println("Temporary folder: " +
temporaryFolder.getRoot());
}
@AfterEach
void teardown() {
temporaryFolder.delete();
}
}
When executing this test, the path of the temporary folder will be logged on the standard output:
The following test demonstrates the use of the ErrorCollector rule in a Jupiter test. Notice that the collector rule allows the execution of a test to continue after one or more problems are found:
package io.github.bonigarcia;
import static org.hamcrest.CoreMatchers.equalTo;
import org.junit.Rule;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;
import org.junit.rules.ErrorCollector;
@EnableRuleMigrationSupport
class ErrorCollectorRuleTest {
@Rule
public ErrorCollector collector = new ErrorCollector();
@Test
void test() {
collector.checkThat("a", equalTo("b"));
collector.checkThat(1, equalTo(2));
collector.checkThat("c", equalTo("c"));
}
}
These problems are reported together at the end of the test:
Finally, the ExpectedException rule allows us to configure a test to anticipate a given exception to be thrown within the test logic:
package io.github.bonigarcia;
import org.junit.Rule;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;
import org.junit.rules.ExpectedException;
@EnableRuleMigrationSupport
class ExpectedExceptionRuleTest {
@Rule
ExpectedException thrown = ExpectedException.none();
@Test
void throwsNothing() {
}
@Test
void throwsNullPointerException() {
thrown.expect(NullPointerException.class);
throw new NullPointerException();
}
}
In this example, even when the second test raises a NullPointerException, the test will be marked as having succeeded since that exception was expected.