8

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.

2
  • What's the content of your WebConfig? Commented Sep 17, 2014 at 0:24
  • You asked a question and people have taken the time to answer. Please accept an answer. Commented Feb 25, 2015 at 19:20

2 Answers 2

14

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Beware that when you set the HandlerExceptionResolvers in your builder you will wipe all of the default ones and use yours in place of theirs. This can cause you issues if you have any tests that use validation annotations for example and expect the appropriate response from Spring. I've raise an issue on Spring requesting that they allow us to add a resolver instead of overwriting: jira.spring.io/browse/SPR-12751
Spent 3 hours today until I solved the problem with exceptionResolver.getMessageConverters().add(...) as you did. This is sick! By default only 4 converters are added within ExceptionHandlerExceptionResolver constructor and Jackson was not there.
3

The MockMvcBuilders standaloneSetup is manual by definition meaning that rather than discovering Spring beans of interest like @ControllerAdvice in Spring configuration, it lets you manually set up what you need for the test. Instead the standaloneSetup should let you also manually register @ControllerAdvice beans. There is a ticket for making that happen. See https://jira.spring.io/browse/SPR-12751.

1 Comment

Thanks to @Rossen for implementing this. Spring 4.2 is shipped with this improvement and Spring Boot 1.3.0 also has this since it has a dependency on Spring 4.2+.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.