Search code examples
javaspringunit-testingmockitopowermockito

Testing Annotation based RequestInterceptor


I wanted to do so custom logic(Record the request and response) on some routes. Based on some research I came decided to use AnnotationBased RequestInterceptor. This is my code for interceptor.

public class CustomInterceptor extends HandlerInterceptorAdapter {
@Override
public void afterCompletion(final HttpServletRequest request,
                            final HttpServletResponse response,
                            final Object handler,
                            final Exception ex) {
     if (handler != null && handler instanceof  HandlerMethod) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        CustomRecord annotation = AnnotationUtils.getAnnotation(handlerMethod.getMethod(), CustomRecord.class);
        if (annotation != null) {
              // Record Request and Response in a File.
        }
}

Now this class is working as expected but I was unable to unit test this function.

  • I first thought of trying a creating an HandlerMethod Object but I did not get anywhere.
  • Second I tried to use PowerMokito. This was my test code:

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(CustomInterceptor.class)
    @PowerMockIgnore("javax.management.*")
    public class CustomInterceptorTest {
    @Test
    public void restAnnotationRecording_negetivecase() {
      HandlerMethod mockHandler = mock(HandlerMethod.class);
      PowerMockito.mockStatic(AnnotationUtils.class);
      when(AnnotationUtils.getAnnotation(mockHandler.getMethod(), 
           CustomRecord.class).thenReturn(null);
      // Verify file is not saved
    }
     // A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies - - with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() methodcannot be saved.
    @Test
    public void restAnnotationRecording_happycase() {
        HandlerMethod mockHandler = mock(HandlerMethod.class);
        PowerMockito.mockStatic(AnnotationUtils.class);
        when(AnnotationUtils.getAnnotation(mockHandler.getMethod(), CustomRecord.class).thenReturn(mockAnnotation);
        // Verify file is saved
    }
    }
    
    • This gives an Error A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies with doReturn|Throw() family of methods.

I wanted to check if there is any method to test the Interceptor. I am a newbie in Java, thanks for help.


Solution

  • You can easily create your own HandlerMethod without mocking. There's a constructor that accepts an Object (the controller) and a Method (the controller method). The easiest way to get a Method is to simply call Class.getMethod(). What you want to do is just create a dummy controller class, and then use that class to get the method. For example

    class TestController {
        @Custom
        public void testMethod() {}
    }
    
    Method method = TestController.class.getMethod("testMethod");
    TestController controller = new TestController();
    HandlerMethod handlerMethod = new HandlerMethod(controller, method);
    
    Custom annotation = handlerMethod.getMethodAnnotation(Custom.class);
    

    It's that easy. Below is a complete test.

    public class HandlerInterceptorTest {
    
        @Target(ElementType.METHOD)
        @Retention(RetentionPolicy.RUNTIME)
        private @interface Custom {
        }
    
        private static class MyHandlerInterceptor implements HandlerInterceptor {
    
            @Override
            public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
                if (handler instanceof HandlerMethod) {
                    HandlerMethod handlerMethod = (HandlerMethod) handler;
                    Custom annotation = handlerMethod.getMethodAnnotation(Custom.class);
                    if (annotation != null) {
                        return true;
                    }
                }
                return false;
            }
        }
    
        private static class TestController {
            @Custom
            public void testMethodWithAnnotation() {}
    
            public void testMethodWithoutAnnotation() {}
        }
    
        @Test
        public void testMethodWithAnnotation() throws Exception {
            Method method = TestController.class.getMethod("testMethodWithAnnotation");
            TestController controller = new TestController();
            HandlerMethod handlerMethod = new HandlerMethod(controller, method);
    
            MyHandlerInterceptor interceptor = new MyHandlerInterceptor();
            boolean result = interceptor.preHandle(null, null, handlerMethod);
    
            assertTrue(result);
        }
    
        @Test
        public void testMethodWithoutAnnotation() throws Exception {
            Method method = TestController.class.getMethod("testMethodWithoutAnnotation");
            TestController controller = new TestController();
            HandlerMethod handlerMethod = new HandlerMethod(controller, method);
    
            MyHandlerInterceptor interceptor = new MyHandlerInterceptor();
            boolean result = interceptor.preHandle(null, null, handlerMethod);
    
            assertFalse(result);
        }
    }