Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing ContainerRequestFilter which uses ResourceInfo with mockito

I'm trying to use Mockito to unit test a ContainerRequestFilter, which is applied using @NameBinding. The filter checks an annotation field to determine what to do. See sample code:

Annotation

@Target({TYPE, METHOD})
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    MyEnum info() default MyEnum.DEFAULT;
}

MyEnum

public enum MyEnum {
    VALUE1,
    VALUE2,
    DEFAULT
}

Annotated Filter which uses MyEnum as conditional

@MyAnnotation
public class MyFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext containerRequestContext) throws IOException {

        if (resourceInfo.getResourceMethod().getAnnotation(MyAnnotation.class).info().equals(MyEnum.VALUE1)) 
        {
            // set some value or throw some exception (this can be verified in the test)
        }

        if (resourceInfo.getResourceMethod().getAnnotation(MyAnnotation.class).info().equals(MyEnum.VALUE2))
        {
           // set some value or throw some exception (this can be verified in the test)
        }
    }
}

Annotated Resource methods

@Path("/somepath1")
public class MyResource1
{
    @GET
    @MyAnnotation(info = MyEnum.VALUE1)
    public Response someResourceMethod()
    {
        // return response
    } 
}


@Path("/somepath2")
public class MyResource2
{
    @GET
    @MyAnnotation(info = MyEnum.VALUE2)
    public Response someResourceMethod()
    {
        // return response
    } 
}

This design makes it easy to just add enum values when there are new conditions to be added to the filter.

How can I unit test MyFilter, by varying the values in the conditional?

One approach I tried was to mock ResourceInfo and then return mock Method when resourceInfo.getResourceMethod(), but this cannot be done since Method is a final class.

Also mocking objects you don't own is not recommended, so is there a different approach to test this? I am also not wedded to Mockito, so open to any other suggestions.

like image 686
bdeo Avatar asked Aug 08 '17 19:08

bdeo


People also ask

Is Mockito used for unit testing?

Mockito is a mocking framework, JAVA-based library that is used for effective unit testing of JAVA applications. Mockito is used to mock interfaces so that a dummy functionality can be added to a mock interface that can be used in unit testing.

Can we use JUnit without Mockito?

In other words: you can definitely use JUnit without using a mocking framework. Same is true for the reverse direction; but in reality, there are not many good reasons why you would want to use Mockito for anything else but unit testing.

Can we test all method with Mockito?

Mockito requires you to provide all arguments either by matchers or by exact values. So if a method has more than one argument and you want to use argument matchers for only some of its arguments, forget it.

What can you mock with Mockito?

The Mockito. mock() method allows us to create a mock object of a class or an interface. We can then use the mock to stub return values for its methods and verify if they were called. We don't need to do anything else to this method before we can use it.


1 Answers

The easiest way is to just create a dummy class for the test, and do a little reflection to get the Method on the class

@Test
public void testEnumOne() throws Exception {
   Method methodOne = MockClass.class.getDeclaredMethod("enumOne");
   Mockito.when(resourceInfo.getResourceMethod()).thenReturn(methodOne);
}

private static class MockClass {
    @MyAnnotation(info=MyEnum.VALUE1)
    public void enumOne() {}
    @MyAnnotation(info=MyEnum.VALUE2)
    public void enumTwo() {}
}

Here's a complete test.

@RunWith(MockitoJUnitRunner.class)
public class FilterAnnotationTest {

    @Mock
    private ResourceInfo resourceInfo;

    @Mock
    private ContainerRequestContext context;

    @Spy
    private Service service;

    private MyFilter filter;

    @Before
    public void setUp() {
        filter = new MyFilter(resourceInfo, service);
    }

    @Test
    public void testEnumOne() throws Exception {
       Method methodOne = MockClass.class.getDeclaredMethod("enumOne");
       Mockito.when(resourceInfo.getResourceMethod()).thenReturn(methodOne);

       filter.filter(context);
       Mockito.verify(service).methodOne();
    }

    @Test
    public void testEnumTwo() throws Exception {
        Method methodTwo = MockClass.class.getDeclaredMethod("enumTwo");
        Mockito.when(resourceInfo.getResourceMethod()).thenReturn(methodTwo);

        filter.filter(context);
        Mockito.verify(service).methodTwo();
    }


    private enum MyEnum {
        VALUE1, VALUE2
    }

    @Target({ METHOD })
    @Retention(RUNTIME)
    private @interface MyAnnotation {
         MyEnum info();
    }

    private static class MyFilter implements ContainerRequestFilter {

        private final ResourceInfo resourceInfo;
        private final Service service;

        @Inject
        public MyFilter(ResourceInfo resourceInfo, Service service) {
            this.resourceInfo = resourceInfo;
            this.service = service;
        }

        @Override
        public void filter(ContainerRequestContext containerRequestContext) throws IOException {
            MyAnnotation anno = resourceInfo.getResourceMethod().getAnnotation(MyAnnotation.class);
            if (anno.info() == MyEnum.VALUE1) {
                service.methodOne();
            } else if (anno.info() == MyEnum.VALUE2) {
                service.methodTwo();
            }
        }
    }

    private static class MockClass {
        @MyAnnotation(info=MyEnum.VALUE1)
        public void enumOne() {}
        @MyAnnotation(info=MyEnum.VALUE2)
        public void enumTwo() {}
    }

    public interface Service {
        void methodOne();
        void methodTwo();
    }
}
like image 60
Paul Samsotha Avatar answered Oct 13 '22 01:10

Paul Samsotha