JUnit Jupiter Migration Support provides support for JUnit 4 rules within JUnit Jupiter, enabling legacy test infrastructure to work with JUnit 5.
npx @tessl/cli install tessl/maven-org-junit-jupiter--junit-jupiter-migrationsupport@5.12.0JUnit Jupiter Migration Support provides support for JUnit 4 rules within JUnit Jupiter, enabling legacy test infrastructure to work with JUnit 5. This module facilitates the migration of large JUnit 4 codebases by allowing existing rules to continue working unchanged within JUnit Jupiter test classes.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<version>5.12.2</version>
<scope>test</scope>
</dependency>Gradle:
testImplementation 'org.junit.jupiter:junit-jupiter-migrationsupport:5.12.2'// Main annotations for enabling migration support
import org.junit.jupiter.migrationsupport.EnableJUnit4MigrationSupport;
import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;
// Core extension classes (STABLE API)
import org.junit.jupiter.migrationsupport.rules.ExternalResourceSupport;
import org.junit.jupiter.migrationsupport.rules.VerifierSupport;
import org.junit.jupiter.migrationsupport.rules.ExpectedExceptionSupport;
import org.junit.jupiter.migrationsupport.conditions.IgnoreCondition;
// Advanced adapter classes (INTERNAL API - for advanced usage)
import org.junit.jupiter.migrationsupport.rules.adapter.GenericBeforeAndAfterAdvice;
import org.junit.jupiter.migrationsupport.rules.adapter.AbstractTestRuleAdapter;
import org.junit.jupiter.migrationsupport.rules.adapter.ExternalResourceAdapter;
import org.junit.jupiter.migrationsupport.rules.adapter.VerifierAdapter;
import org.junit.jupiter.migrationsupport.rules.adapter.ExpectedExceptionAdapter;
// Rule member classes (INTERNAL API - for advanced usage)
import org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedMember;
import org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedField;
import org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedMethod;import org.junit.jupiter.migrationsupport.EnableJUnit4MigrationSupport;
import org.junit.jupiter.api.Test;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
@EnableJUnit4MigrationSupport
public class MyMigratedTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
@Test
public void testWithRule() {
// Your existing JUnit 4 test code using tempFolder rule
// continues to work unchanged
}
}import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;
import org.junit.jupiter.api.Test;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
@EnableRuleMigrationSupport
public class MySelectiveTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
@Test
public void testWithRule() {
// JUnit 4 rules work, but @Ignore annotation support not enabled
}
}JUnit Jupiter Migration Support is built around several key components:
The module exports 5 packages:
org.junit.jupiter.migrationsupport - Core annotationsorg.junit.jupiter.migrationsupport.conditions - Condition supportorg.junit.jupiter.migrationsupport.rules - Rule support extensionsorg.junit.jupiter.migrationsupport.rules.adapter - Internal adapter classesorg.junit.jupiter.migrationsupport.rules.member - Internal member classesThe module supports both @Rule-annotated fields and methods, maintaining backward compatibility with existing JUnit 4 test infrastructure.
Enables complete JUnit 4 migration support including all rule types and @Ignore annotation support.
/**
* Class-level annotation that enables all JUnit 4 migration support within JUnit Jupiter.
* This composed annotation registers all extensions supported by @EnableRuleMigrationSupport
* and provides support for JUnit 4's @Ignore annotation.
*
* API Status: STABLE (since 5.7)
* Since: 5.4
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@EnableRuleMigrationSupport
@ExtendWith(IgnoreCondition.class)
public @interface EnableJUnit4MigrationSupport {
}Enables native JUnit 4 rule support for Verifier, ExternalResource, and ExpectedException rules.
/**
* Class-level annotation that enables native JUnit 4 rule support within JUnit Jupiter.
* Supports rules of type Verifier, ExternalResource, and ExpectedException.
*
* API Status: STABLE (since 5.7)
* Since: 5.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(ExternalResourceSupport.class)
@ExtendWith(VerifierSupport.class)
@ExtendWith(ExpectedExceptionSupport.class)
public @interface EnableRuleMigrationSupport {
}Provides native support for subclasses of the ExternalResource rule from JUnit 4.
/**
* Extension providing native support for subclasses of ExternalResource rule from JUnit 4.
* Supports both @Rule-annotated fields and methods.
*
* API Status: STABLE (since 5.7)
* Since: 5.0
*/
public class ExternalResourceSupport implements BeforeEachCallback, AfterEachCallback {
/**
* Creates a new ExternalResourceSupport instance.
*/
public ExternalResourceSupport();
/**
* Called before each test method execution to set up external resources.
* @param context the current extension context
* @throws Exception if resource setup fails
*/
@Override
public void beforeEach(ExtensionContext context) throws Exception;
/**
* Called after each test method execution to clean up external resources.
* @param context the current extension context
* @throws Exception if resource cleanup fails
*/
@Override
public void afterEach(ExtensionContext context) throws Exception;
}Provides native support for subclasses of the Verifier rule from JUnit 4.
/**
* Extension providing native support for subclasses of Verifier rule from JUnit 4.
* Supports both @Rule-annotated fields and methods.
*
* API Status: STABLE (since 5.7)
* Since: 5.0
*/
public class VerifierSupport implements AfterEachCallback {
/**
* Creates a new VerifierSupport instance.
*/
public VerifierSupport();
/**
* Called after each test method execution to perform verification.
* @param context the current extension context
* @throws Exception if verification fails
*/
@Override
public void afterEach(ExtensionContext context) throws Exception;
}Provides native support for the ExpectedException rule from JUnit 4.
/**
* Extension providing native support for the ExpectedException rule from JUnit 4.
* Handles test execution exceptions and validates expected exceptions.
*
* API Status: STABLE (since 5.7)
* Since: 5.0
*/
public class ExpectedExceptionSupport implements AfterEachCallback, TestExecutionExceptionHandler {
/**
* Creates a new ExpectedExceptionSupport instance.
*/
public ExpectedExceptionSupport();
/**
* Handles test execution exceptions, checking against expected exceptions.
* @param context the current extension context
* @param throwable the exception that occurred during test execution
* @throws Throwable if the exception should not be handled
*/
@Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable;
/**
* Called after each test method execution to validate expected exceptions.
* @param context the current extension context
* @throws Exception if expected exception validation fails
*/
@Override
public void afterEach(ExtensionContext context) throws Exception;
}ExecutionCondition that supports JUnit 4's @Ignore annotation for disabling test classes and methods.
/**
* ExecutionCondition that supports JUnit 4's @Ignore annotation.
* Containers/tests are disabled if @Ignore is present on the test class or method.
*
* API Status: STABLE (since 5.7)
* Since: 5.4
*/
public class IgnoreCondition implements ExecutionCondition {
/**
* Creates a new IgnoreCondition instance.
*/
public IgnoreCondition();
/**
* Evaluates the execution condition based on the presence of @Ignore annotation.
* @param context the current extension context
* @return ConditionEvaluationResult indicating whether execution should proceed
*/
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context);
}Note: The following adapter classes are part of the INTERNAL API but are exported by the module for advanced usage scenarios.
Base interface for generic before/after advice used by rule adapters.
/**
* Interface for generic before/after advice used by rule adapters.
*
* API Status: INTERNAL (since 5.0)
*/
public interface GenericBeforeAndAfterAdvice {
/**
* Called before test execution. Default implementation does nothing.
*/
default void before();
/**
* Handles test execution exceptions. Default implementation does nothing.
* @param cause the exception that occurred during test execution
* @throws Throwable if the exception should be rethrown
*/
default void handleTestExecutionException(Throwable cause) throws Throwable;
/**
* Called after test execution. Default implementation does nothing.
*/
default void after();
}Base adapter class for converting JUnit 4 TestRule to JUnit Jupiter extensions.
/**
* Base adapter class for converting JUnit 4 TestRule to JUnit Jupiter extensions.
*
* API Status: INTERNAL (since 5.0)
*/
public abstract class AbstractTestRuleAdapter implements GenericBeforeAndAfterAdvice {
/**
* Creates a new adapter for the specified rule-annotated member.
* @param annotatedMember the @Rule-annotated field or method
* @param adapteeClass the expected rule class type
*/
public AbstractTestRuleAdapter(TestRuleAnnotatedMember annotatedMember, Class<? extends TestRule> adapteeClass);
/**
* Executes a method on the target rule with no parameters.
* @param name the method name to execute
* @return the method result
*/
protected Object executeMethod(String name);
/**
* Executes a method on the target rule with specified parameters.
* @param methodName the method name to execute
* @param parameterTypes the parameter types
* @param arguments the method arguments
* @return the method result
*/
protected Object executeMethod(String methodName, Class<?>[] parameterTypes, Object... arguments);
}Adapter for ExternalResource rules.
/**
* Adapter for ExternalResource rules.
*
* API Status: INTERNAL (since 5.0)
*/
public class ExternalResourceAdapter extends AbstractTestRuleAdapter {
/**
* Creates a new ExternalResourceAdapter for the specified annotated member.
* @param annotatedMember the @Rule-annotated field or method containing an ExternalResource
*/
public ExternalResourceAdapter(TestRuleAnnotatedMember annotatedMember);
/**
* Called before test execution to set up the external resource.
*/
@Override
public void before();
/**
* Called after test execution to clean up the external resource.
*/
@Override
public void after();
}Adapter for Verifier rules.
/**
* Adapter for Verifier rules.
*
* API Status: INTERNAL (since 5.0)
*/
public class VerifierAdapter extends AbstractTestRuleAdapter {
/**
* Creates a new VerifierAdapter for the specified annotated member.
* @param annotatedMember the @Rule-annotated field or method containing a Verifier
*/
public VerifierAdapter(TestRuleAnnotatedMember annotatedMember);
/**
* Called after test execution to perform verification.
*/
@Override
public void after();
}Adapter for ExpectedException rules.
/**
* Adapter for ExpectedException rules.
*
* API Status: INTERNAL (since 5.0)
*/
public class ExpectedExceptionAdapter extends AbstractTestRuleAdapter {
/**
* Creates a new ExpectedExceptionAdapter for the specified annotated member.
* @param annotatedMember the @Rule-annotated field or method containing an ExpectedException
*/
public ExpectedExceptionAdapter(TestRuleAnnotatedMember annotatedMember);
/**
* Handles test execution exceptions, checking against expected exceptions.
* @param cause the exception that occurred during test execution
* @throws Throwable if the exception should not be handled
*/
@Override
public void handleTestExecutionException(Throwable cause) throws Throwable;
/**
* Called after test execution to validate expected exceptions.
*/
@Override
public void after();
}Note: The following member classes are part of the INTERNAL API but are exported by the module for advanced usage scenarios.
Interface representing a @Rule-annotated member (field or method).
/**
* Interface representing a @Rule-annotated member (field or method).
* Used internally by the migration support system.
*
* API Status: INTERNAL (since 5.0)
*/
public interface TestRuleAnnotatedMember {
/**
* Returns the TestRule instance from this annotated member.
* @return the TestRule instance
*/
TestRule getTestRule();
}Represents a @Rule-annotated field containing a TestRule instance.
/**
* Represents a @Rule-annotated field containing a TestRule instance.
*
* API Status: INTERNAL (since 5.1)
*/
public class TestRuleAnnotatedField implements TestRuleAnnotatedMember {
/**
* Creates a new TestRuleAnnotatedField for the specified field.
* @param testInstance the test instance containing the field
* @param field the @Rule-annotated field
*/
public TestRuleAnnotatedField(Object testInstance, Field field);
}Represents a @Rule-annotated method that returns a TestRule instance.
/**
* Represents a @Rule-annotated method that returns a TestRule instance.
*
* API Status: INTERNAL (since 5.1)
*/
public class TestRuleAnnotatedMethod implements TestRuleAnnotatedMember {
/**
* Creates a new TestRuleAnnotatedMethod for the specified method.
* @param testInstance the test instance containing the method
* @param method the @Rule-annotated method
*/
public TestRuleAnnotatedMethod(Object testInstance, Method method);
}// Core JUnit Jupiter extension interfaces used by migration support
// From org.junit.jupiter.api.extension package
interface BeforeEachCallback extends Extension {
/**
* Callback that is invoked before each test is invoked.
* @param context the current extension context
* @throws Exception if callback execution fails
*/
void beforeEach(ExtensionContext context) throws Exception;
}
interface AfterEachCallback extends Extension {
/**
* Callback that is invoked after each test has been invoked.
* @param context the current extension context
* @throws Exception if callback execution fails
*/
void afterEach(ExtensionContext context) throws Exception;
}
interface TestExecutionExceptionHandler extends Extension {
/**
* Handle the supplied throwable.
* @param context the current extension context
* @param throwable the Throwable to handle
* @throws Throwable if handling fails or the exception should be rethrown
*/
void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable;
}
interface ExecutionCondition extends Extension {
/**
* Evaluate the execution condition for the supplied ExtensionContext.
* @param context the current extension context
* @return the result of evaluating the condition
*/
ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context);
}
interface ExtensionContext {
/**
* Get the AnnotatedElement corresponding to the current extension context.
*/
Optional<AnnotatedElement> getElement();
/**
* Get the test instance associated with the current test or container.
*/
Object getRequiredTestInstance();
/**
* Get the Class associated with the current test or container.
*/
Class<?> getRequiredTestClass();
/**
* Get the unique ID of the current test or container.
*/
String getUniqueId();
/**
* Get the Store for the supplied Namespace.
*/
Store getStore(Namespace namespace);
}
interface ConditionEvaluationResult {
/**
* Whether the container or test should be disabled.
*/
boolean isDisabled();
/**
* Get the reason why the container or test should be disabled or enabled.
*/
Optional<String> getReason();
/**
* Factory method for creating enabled results.
*/
static ConditionEvaluationResult enabled(String reason);
/**
* Factory method for creating disabled results.
*/
static ConditionEvaluationResult disabled(String reason);
}// JUnit 4 types referenced by migration support
// From org.junit.rules package
interface TestRule {
/**
* Modifies the method-running Statement to implement this test-running rule.
* @param base the Statement to be modified
* @param description a Description of the test implemented in base
* @return a new statement, which may be the same as base
*/
Statement apply(Statement base, Description description);
}
abstract class Verifier implements TestRule {
/**
* Override this to add verification logic.
* @throws Throwable if verification fails
*/
protected abstract void verify() throws Throwable;
}
abstract class ExternalResource implements TestRule {
/**
* Override to set up your specific external resource.
* @throws Throwable if setup fails
*/
protected void before() throws Throwable;
/**
* Override to tear down your specific external resource.
*/
protected void after();
}
class ExpectedException implements TestRule {
/**
* Returns a Rule that expects no exception to be thrown.
*/
public static ExpectedException none();
/**
* Specifies the failure message for tests that are expected to throw an exception but do not.
* @param message exception message
*/
public void reportMissingExceptionWithMessage(String message);
/**
* Specifies that the test is expected to throw an exact type of exception.
* @param type the exception type
*/
public void expect(Class<? extends Throwable> type);
/**
* Specifies that the test is expected to throw an exception with the given message.
* @param message the expected message
*/
public void expectMessage(String message);
}// Java reflection types used by migration support
// From java.lang.reflect package
class Field extends AccessibleObject implements Member {
// Standard Java reflection Field class
}
class Method extends Executable {
// Standard Java reflection Method class
}
interface AnnotatedElement {
// Standard Java reflection AnnotatedElement interface
}The migration support extensions handle various error scenarios:
org.junit.rules.Verifier, org.junit.rules.ExternalResource, and org.junit.rules.ExpectedExceptionorg.junit.rules.TestRule implementations is not possible within the JUnit Jupiter extension modelorg.junit.Rule annotations