Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing @RestController with @ControllerAdvice

Tags:

java

junit

spring

My problem is connected with testing Spring @RestController which is also using @ControllerAdvice with @ExceptionHandler. Here is the code:

@ControllerAdvice class:

@ControllerAdvice
public class MyAppExceptionHandler {

    @ExceptionHandler({ NoSuchEntityException.class })
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    public @ResponseBody
    ErrorDTO handleNotFoundException(Exception ex) throws IOException {

        return new ErrorDTO.Builder().setStatus(HttpStatus.NOT_FOUND)
                .setCause(ex.getClass().getName())
                .setThrowable(ex).build();
    }
}

When using it in application everything works fine - perfectly getting 404 response with JSON explanation, but when trying to use it during tests - bad things happen.

My test class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { WebConfig.class })
@WebAppConfiguration
public class SomeTest {

    @Mock
    private SomeService service;

    @InjectMocks
    private SomeController controller;

    private MockMvc mvc;

    private ExceptionHandlerExceptionResolver createExceptionResolver() {
        ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
            @Override
            protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
                HandlerMethod handlerMethod, Exception exception) {
                Method method = new ExceptionHandlerMethodResolver(
                        MyAppExceptionHandler.class).resolveMethod(exception);
                return new ServletInvocableHandlerMethod(
                    new MyAppExceptionHandler(), method);
            }
        };
        exceptionResolver.afterPropertiesSet();
        return exceptionResolver;
    }

    @Before
    public void setup() {

        MockitoAnnotations.initMocks(this);
        mvc = MockMvcBuilders.standaloneSetup(controller)
                .setHandlerExceptionResolvers(createExceptionResolver())
                .build();
    }

    @Test
    public void thatExceptionHappens() throws Exception {

        when(service.get(10)).thenThrow(new NoSuchEntityException(Some.class, 10));

        mvc.perform(get("/api/some/10")).andExpect(status().isNotFound());
    }
}

When trying to run it:

2014-07-15 19:35:01.376 [main] ERROR com.package.SomeTest$1 - Failed to invoke @ExceptionHandler method: public com.package.ErrorDTO com.package.MyAppExceptionHandler.handleNotFoundException(java.lang.Exception) throws java.io.IOException
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation

I think that probably MappingJackson2HttpMessageConverter is not being loaded during testing mine @ExceptionHandler (however it is configured in WebConfig.class and when trying to perform typical test - one not throwing any exception - everything works fine).

Thanks in advance for your help.

like image 573
tep Avatar asked Jul 15 '14 18:07

tep


People also ask

How do I test a rest controller in spring?

React + Spring Boot Microservices and Spring Spring Boot provides an easy way to write a Unit Test for Rest Controller file. With the help of SpringJUnit4ClassRunner and MockMvc, we can create a web application context to write Unit Test for Rest Controller file.

What is difference between @ControllerAdvice and ExceptionHandler?

@ControllerAdvice is not specific to the exception handling , its also used for handling property, validation or formatter bindings at the global level. @ControllerAdvice in the context of exception handling is just another way of doing exception handling at a global level using @Exceptionhandler annotation.

What is the use of @ControllerAdvice in spring boot?

@ControllerAdvice is a specialization of the @Component annotation which allows to handle exceptions across the whole application in one global handling component. It can be viewed as an interceptor of exceptions thrown by methods annotated with @RequestMapping and similar.


1 Answers

I'm not sure if this is the best solution for this (I'd like to hear from another one),
but this is how I fix this exactly issue you are facing:

Add this line to your createExceptionResolver():

exceptionResolver.getMessageConverters().add(
        new MappingJackson2HttpMessageConverter());

Something like this:

private ExceptionHandlerExceptionResolver createExceptionResolver() {
    ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
        @Override
        protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
            HandlerMethod handlerMethod, Exception exception) {
            Method method = new ExceptionHandlerMethodResolver(
                    MyAppExceptionHandler.class).resolveMethod(exception);
            return new ServletInvocableHandlerMethod(
                new MyAppExceptionHandler(), method);
        }
    };
    exceptionResolver.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
    exceptionResolver.afterPropertiesSet();
    return exceptionResolver;
}

For some reason that I don't know, Spring was not loading my MappingJackson2HttpMessageConverter.
That line fixed my problem.

like image 75
Denis C de Azevedo Avatar answered Sep 27 '22 19:09

Denis C de Azevedo