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 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.
Related
I am having trouble mocking a service dependency that accepts a functional interface in a method. I have simplified the problem to the follow test application. Mockito is not matching on the passed in lambda method reference. I have tried various ways of passing in a reference to the stubbing.
package com.example.test;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
#FunctionalInterface
interface MyCallback<T> extends Function<String, T> {
T apply(String s);
}
interface MyDependency {
<T> T submit(String id, MyCallback<T> myCallback);
}
class MyService {
private final MyDependency myDependency;
MyService(MyDependency myDependency) {
this.myDependency = myDependency;
}
String run() {
MyCallback<String> myCallback = getStringMyCallback();
return myDependency.submit("myId", myCallback);
}
public MyCallback<String> getStringMyCallback() {
return s -> "hello " + s;
}
}
#ExtendWith(MockitoExtension.class)
class TestUtil {
#Mock
private MyDependency myDependency;
#InjectMocks
private MyService myService;
#Test
void test() {
MyCallback<String> myCallback = myService.getStringMyCallback();
when(myDependency.submit("id", myCallback)).thenReturn("world");
String value = myService.run();
assertThat(value).isEqualTo("world");
}
}
The desired outcome is to pass a method lambda to the myDependency and stub different results. In this code the contents of 'value' is null.
I have a java class whose constructor looks like this
private SomeManager someManager;
public MyService() {
this.someManager = ManagerHandler.getDefault();
}
The issue is that while testing ManagerHandler is not initialised so I am not able to create new object of this class to test its method. I am using mockito for testing. I am not able to understand How to mock a parameter which I am not passing in the constructor.
Is there anyway I can mock someManager using PowerMock?
You can use InjectMocks annotation. See below example:
MyService class:
public class MyService {
ManagerHandler someManager;
public MyService() {
this.someManager = ManagerHandler.getDefault();
}
// other methods
public String greetUser(String user) {
return someManager.sayHello(user) + ", Good Morning!";
}
}
ManagerHandler class:
public class ManagerHandler {
public static ManagerHandler getDefault() {
return new ManagerHandler();
}
public String sayHello(String userName) {
return "Hello " + userName;
}
}
TestClass:
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class TestClass {
#Mock
ManagerHandler someManager;
#InjectMocks
MyService myService = new MyService();
#Test
public void test() {
//mock methods of ManagerHandler
Mockito.when(someManager.sayHello("Alice")).thenReturn("Hello Alice");
assertEquals("Hello Alice, Good Morning!", myService.greetUser("Alice"));
}
}
In my integration tests I use custom annotations to start part of my application. I definitively need to use these annotations for my tests. Therefore, a typical integration test looks like this:
#Test
#MyAnnotation(a = MyEnum.B, b = someOtherConstant)
public void test() {
}
What I'd like to do is to run the tests for all enums in MyEnum. I thought about using a parameterized tests. But since the value I pass into the annotation must be constant, I think this is not an option. What other options do I have?
I don't think there is a need for an annotation here.
A very simple enum:
package stackoverflow52828976;
public enum MyEnum
{
A,
B,
C;
public boolean isA()
{
return this == A;
}
}
The test class:
package stackoverflow52828976;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
#RunWith( Parameterized.class )
public class MyEnumTest
{
#Parameterized.Parameters
public static Collection<Object[]> data()
{
final List<Object[]> list = new LinkedList<>();
list.add(new Object[]{ MyEnum.A, true});
list.add(new Object[]{ MyEnum.B, false});
list.add(new Object[]{ MyEnum.C, false});
return list;
}
private final MyEnum e;
private final boolean testResult;
public MyEnumTest
( final MyEnum anEnum
, final boolean aTestResult
)
{
this.e = anEnum;
this.testResult = aTestResult;
}
#Test
public void testIsA() throws Exception
{
assertEquals(testResult, e.isA());
}
}
(Update: After I reported this, the TestNG team confirmed the bug.)
Normally, ignoring a class can be done with #Ignore or enabled=false
This does not work on a subclass where test methods are defined in its superclass (and where the subclass defines its specific functionality in hook methods). See ChildClassTest below.
Note that #Ignore is specific to JUnit whereas TestNG uses enabled.
Base class
import org.testng.annotations.Test;
public class ParentClassTest {
#Test
public void test1() {
hook();
}
protected void hook() {};
}
Child class
import org.testng.Reporter;
import org.testng.annotations.Ignore;
#Ignore
public class ChildClassTest extends ParentClassTest {
#Override
protected void hook() {
Reporter.log("ChildClassTest#hook()");
}
}
Been doing some brainstorming out of curiosity, and came up with the workarounds below tested with v6.14.2. I personally prefer the first one, being cleaner, more elegant, flexible and easier to maintain and extend.
context
import org.testng.annotations.Test;
import static org.testng.Assert.assertTrue;
public class MyTest {
#Test
public void shouldRun() {
assertTrue(true);
}
#Test
public void shouldNotRun() {
assertTrue(true);
}
#Test
public void shouldNotRunEither() {
assertTrue(true);
}
}
1) Using listeners - create a TestListenerAdapter & annotation to skip methods with certain names: flexible, clear, easy to reuse and identify for removal. The only downside is that you have to pay attention to method names for typos.
Annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
public #interface SkipMethods {
String[] value() default {};
}
TestListenerAdapter
import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.TestListenerAdapter;
public class TestSkippingListener extends TestListenerAdapter {
#Override
public void onTestStart(ITestResult result) {
// get the skip annotation
SkipMethods skipAnnotation = result.getMethod().getInstance().getClass().getAnnotation(SkipMethods.class);
// if the annotation exists
if (skipAnnotation != null) {
for (String skippableMethod : skipAnnotation.value()) {
// and defines the current method as skippable
if (skippableMethod.equals(result.getMethod().getMethodName())) {
// skip it
throw new SkipException("Method [" + skippableMethod + "] marked for skipping");
}
}
}
}
}
Test Subclass
import org.testng.annotations.Listeners;
// use listener
#Listeners(TestSkippingListener.class)
// define what methods to skip
#SkipMethods({"shouldNotRun", "shouldNotRunEither"})
public class MyTestSkippingInheritedMethods extends MyTest {
}
Result
2) Override methods from superclass and throw SkipException: pretty clear, no possibility of typo, but not reusable, not easily maintainable & introduces useless code:
import org.testng.SkipException;
public class MyTestSkippingInheritedMethods extends MyTest {
#Override
public void shouldNotRun() {
throw new SkipException("Skipped");
}
#Override
public void shouldNotRunEither() {
throw new SkipException("Skipped");
}
}
Result
I've been able to inject into my jersey resource from a filter as per How to inject an object into jersey request context?. This allows me to successfully inject into a method parameter:
#GET
public Response getTest(#Context MyObject myObject) { // this works
However, for setter/field/constructor injection, the HK2 Factory is invoked before the jersey filter, which means the provide() method returns null:
#Override
public MyObject provide() {
// returns null because the filter has not yet run,
// and the property has not yet been set
return (MyObject)context.getProperty("myObject");
}
Is there a way to define when the HK2 Factory will run so that it is invoke after the filter runs? If not, then the workaround is to define MyObject as an interface and define an additional implementation that takes a ContainerRequestContext in its constructor; any attempt to actually use the instance would then lazily delegate to the implementation that gets set on the ContainerRequestContext's property (presumably you wouldn't actually use the instance until after the filter runs -- at which point the property would be set).
But I would like to understand if it is possible to delay the point at which the HK2 Factory runs so that it runs after the filter (it already runs after the filter in the case of method parameter injection). If it is not possible, then I would like to understand if there is a fundamental reason why.
Oddly it only works for me with #PreMatching on the filter (which limits access to some things you may or may not need). Not quite sure what's going on under the hood, that cause it not to work without it :-(. Below is a complete test using Jersey Test Framework.
import java.io.IOException;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Assert;
import org.junit.Test;
public class FilterInjectionTest extends JerseyTest {
private static final String MESSAGE = "Inject OK";
private static final String OBJ_PROP = "myObject";
public static class MyObject {
private final String value;
public MyObject(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
#PreMatching
#Provider
public static class MyObjectFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext context) throws IOException {
MyObject obj = new MyObject(MESSAGE);
context.setProperty(OBJ_PROP, obj);
}
}
public static class MyObjectFactory
extends AbstractContainerRequestValueFactory<MyObject> {
#Override
#RequestScoped
public MyObject provide() {
return (MyObject) getContainerRequest().getProperty(OBJ_PROP);
}
#Override
public void dispose(MyObject t) {
}
}
#Path("method-param")
public static class MethodParamResource {
#GET
public String getResponse(#Context MyObject myObject) {
return myObject.getValue();
}
}
#Path("constructor")
public static class ConstructorResource {
private final MyObject myObject;
#Inject
public ConstructorResource(#Context MyObject myObject) {
this.myObject = myObject;
}
#GET
public String getResponse() {
return myObject.getValue();
}
}
#Path("field")
public static class FieldResource {
#Inject
private MyObject myObject;
#GET
public String getResponse() {
return myObject.getValue();
}
}
#Override
public Application configure() {
ResourceConfig config = new ResourceConfig();
config.register(MethodParamResource.class);
config.register(MyObjectFilter.class);
config.register(ConstructorResource.class);
config.register(FieldResource.class);
config.register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(MyObjectFactory.class)
.to(MyObject.class).in(Singleton.class);
}
});
return config;
}
#Test
public void methoParamInjectionOk() {
String response = target("method-param").request().get(String.class);
Assert.assertEquals(MESSAGE, response);
System.out.println(response);
}
#Test
public void costructorInjectionOk() {
String response = target("constructor").request().get(String.class);
Assert.assertEquals(MESSAGE, response);
System.out.println(response);
}
#Test
public void fieldInjectionOk() {
String response = target("field").request().get(String.class);
Assert.assertEquals(MESSAGE, response);
System.out.println(response);
}
}
UPDATE
The solution, without having to make it a #PreMatching filter, is to inject with javax.inject.Provider. This will allow you to lazily retrieve the object. I guess what happens with the constructor and field injection is that right after matching the resource class, it it immediately created and injected. Because the filter hasn't been called yet, there is no object for the factory. It works for the method injection, because it is just like any other method call. The object is passed to it when the method is called. Below is the example with the javax.inject.Provider
#Path("constructor")
public static class ConstructorResource {
private final javax.inject.Provider<MyObject> myObjectProvider;
#Inject
public ConstructorResource(javax.inject.Provider<MyObject> myObjectProvider) {
this.myObjectProvider = myObjectProvider;
}
#GET
public String getResponse() {
return myObjectProvider.get().getValue();
}
}
#Path("field")
public static class FieldResource {
#Inject
private javax.inject.Provider<MyObject> myObjectProvider;;
#GET
public String getResponse() {
return myObjectProvider.get().getValue();
}
}