使用@ControllerAdvice测试@RestController

8

我的问题与测试使用@ExceptionHandler的Spring @RestController有关,这也使用了@ControllerAdvice。以下是代码:

@ControllerAdvice类:

@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();
    }
}

在应用程序中使用时一切正常,可以完美地获得404响应和JSON解释,但是在测试过程中尝试使用它时会出现问题。

我的测试类:

@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());
    }
}

尝试运行时:

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

我认为可能在测试我的 @ExceptionHandler 时 MappingJackson2HttpMessageConverter 没有被加载(尽管它在WebConfig.class中进行了配置并且在尝试执行典型测试 - 不抛出任何异常的测试时,一切都正常运行)。

感谢您的帮助。


你的WebConfig文件内容是什么? - Denis C de Azevedo
你提出了一个问题,人们花时间回答了它。请接受一个答案。 - Matt Byrne
2个回答

14
我不确定这是否是最好的解决方案(我希望能听到其他意见),但这就是我解决你所面临问题的确切方法:
将此行代码添加到您的createExceptionResolver()中:
exceptionResolver.getMessageConverters().add(
        new MappingJackson2HttpMessageConverter());

类似这样:

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;
}

因为某种我不知道的原因,Spring 没有加载我的 MappingJackson2HttpMessageConverter
那一行代码解决了我的问题。


3
请注意,在构建器中设置HandlerExceptionResolvers会清除所有默认的解析器,并使用您自己的解析器替换它们。如果您的任何测试使用验证注释并期望从Spring获得适当的响应,则可能会导致问题。我已经在Spring上提出了一个问题,要求他们允许我们添加解析器而不是覆盖现有的解析器:https://jira.spring.io/browse/SPR-12751 - Matt Byrne
1
今天我花了3个小时才解决了问题,就像你一样使用exceptionResolver.getMessageConverters().add(...)。这太疯狂了!默认情况下,在ExceptionHandlerExceptionResolver构造函数中只添加了4个转换器,并且没有包含Jackson。 - Simon Logic

3

MockMvcBuilders的standaloneSetup是手动设置的,这意味着它不会像在Spring配置中发现@ ControllerAdvice等感兴趣的Spring bean一样,而是让您手动设置测试所需的内容。因此,standaloneSetup还应允许您手动注册@ControllerAdvice bean。有一个票据来实现这一点。请参见https://jira.spring.io/browse/SPR-12751


感谢@Rossen的实现。Spring 4.2已经包含了这个改进,Spring Boot 1.3.0也有这个改进,因为它依赖于Spring 4.2+。 - Matt Byrne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接