Issue
I have some custom rule using junit4 which I would like to convert to junit5. However I'm not able to find good documentation on migrating a MethodRule implementation other than that I should be using junit5 extension instead of rule.
public class MyRule implements MethodRule {
private static final Logger LOGGER = LoggerFactory.getLogger(MyRule.class);
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation { }
@Override
public Statement apply(final Statement statement, final FrameworkMethod frameworkMethod, final Object o) {
Statement result = statement;
if (hasMyAnnotation(frameworkMethod)) {
result = new Statement() {
@Override
public void evaluate() {
LOGGER.info("Skipping test");
}
};
}
}
return result;
}
private static boolean hasMyAnnotation(final Annotatable frameworkMethod) {
return frameworkMethod.getAnnotation(MyAnnotation.class) != null;
}
My class is using junit4 Statement
, FrameworkMethod
etc to find out if my method has an annotation... then to skip it. How can I convert this?
Solution
Solution 1, Disable test with custom annotation
JUnit 5 provides a type of extension that can control whether or not a test should be run. This is defined by implementing the ExecutionCondition interface.
Extension implementation:
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.commons.logging.Logger;
import org.junit.platform.commons.logging.LoggerFactory;
import org.junit.platform.commons.util.AnnotationUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
public class SkipConditionExtension implements ExecutionCondition {
private static final Logger LOGGER = LoggerFactory.getLogger(SkipConditionExtension.class);
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
AnnotatedElement element = context.getElement().orElse(null);
if (hasMyAnnotation(element, MyAnnotation.class)) {
LOGGER.info(() ->"Skipping test");
return ConditionEvaluationResult.disabled(String.format("Skipped test: %s by @MyAnnotation", element));
}
return ConditionEvaluationResult.enabled("Test enabled");
}
private <T extends Annotation> boolean hasMyAnnotation(final AnnotatedElement element, Class<T> annotation) {
return element != null && AnnotationUtils.findAnnotation(element, annotation).isPresent();
}
}
Registring extenssion:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(SkipConditionExtension.class)
public class TestObject {
@Test
public void test1() {
}
@Test
@MyAnnotation
public void test2() {
}
}
Output:
INFO: Skipping test
Skipped test: public void com.test.TestObject.test2() by @MyAnnotation
Solution 2, Skip test via invocation interseptor
InvocationInterceptor iterface defines the API for Extensions that wish to intercept calls to test.
Current implementation will behave exactly like your previous Rule
.
Extension implementation:
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
import org.junit.platform.commons.logging.Logger;
import org.junit.platform.commons.logging.LoggerFactory;
import org.junit.platform.commons.util.AnnotationUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
public class SkipCondition implements InvocationInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(SkipConditionExtension.class);
@Override
public void interceptTestMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
AnnotatedElement element = extensionContext.getElement().orElse(null);
if (hasMyAnnotation(element, MyAnnotation.class)) {
LOGGER.info(() ->"Skipping test");
invocation.skip();
} else {
invocation.proceed();
}
}
private <T extends Annotation> boolean hasMyAnnotation(final AnnotatedElement element, Class<T> annotation) {
return element != null && AnnotationUtils.findAnnotation(element, annotation).isPresent();
}
}
Registring extenssion:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(SkipCondition.class)
public class TestObject {
@Test
public void test1() {
}
@Test
@MyAnnotation
public void test2() {
}
}
Please note, you can perform automatic extension registration according to documentation.
Answered By - Eugene
Answer Checked By - Mary Flores (JavaFixing Volunteer)