使用 Spring MVC Test 测试 Spring MVC @ExceptionHandler 方法

问题描述 投票:0回答:7

我有以下简单的控制器来捕获任何意外的异常:

@ControllerAdvice
public class ExceptionController {

    @ExceptionHandler(Throwable.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ResponseEntity handleException(Throwable ex) {
        return ResponseEntityFactory.internalServerErrorResponse("Unexpected error has occurred.", ex);
    }
}

我正在尝试使用 Spring MVC 测试框架编写集成测试。这是我到目前为止所拥有的:

@RunWith(MockitoJUnitRunner.class)
public class ExceptionControllerTest {
    private MockMvc mockMvc;

    @Mock
    private StatusController statusController;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.standaloneSetup(new ExceptionController(), statusController).build();
    }

    @Test
    public void checkUnexpectedExceptionsAreCaughtAndStatusCode500IsReturnedInResponse() throws Exception {

        when(statusController.checkHealth()).thenThrow(new RuntimeException("Unexpected Exception"));

        mockMvc.perform(get("/api/status"))
                .andDo(print())
                .andExpect(status().isInternalServerError())
                .andExpect(jsonPath("$.error").value("Unexpected Exception"));
    }
}

我在 Spring MVC 基础设施中注册了 ExceptionController 和模拟 StatusController。 在测试方法中,我设置了从 StatusController 抛出异常的期望。

异常被抛出,但 ExceptionController 没有处理它。

我希望能够测试 ExceptionController 是否获取异常并返回适当的响应。

关于为什么这不起作用以及我应该如何进行这种测试有什么想法吗?

谢谢。

spring spring-mvc mockito spring-mvc-test
7个回答
114
投票

我刚刚遇到了同样的问题,以下方法对我有用:

@Before
public void setup() {
    this.mockMvc = MockMvcBuilders.standaloneSetup(statusController)
         .setControllerAdvice(new ExceptionController())
        .build();
}

5
投票

此代码将添加使用异常控制建议的能力。

@Before
public void setup() {
    this.mockMvc = standaloneSetup(commandsController)
        .setHandlerExceptionResolvers(withExceptionControllerAdvice())
        .setMessageConverters(new MappingJackson2HttpMessageConverter()).build();
}

private ExceptionHandlerExceptionResolver withExceptionControllerAdvice() {
    final ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
        @Override
        protected ServletInvocableHandlerMethod getExceptionHandlerMethod(final HandlerMethod handlerMethod,
            final Exception exception) {
            Method method = new ExceptionHandlerMethodResolver(ExceptionController.class).resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(new ExceptionController(), method);
            }
            return super.getExceptionHandlerMethod(handlerMethod, exception);
        }
    };
    exceptionResolver.afterPropertiesSet();
    return exceptionResolver;
}

2
投票

由于您使用的是独立设置测试,因此您需要手动提供异常处理程序。

mockMvc= MockMvcBuilders.standaloneSetup(adminCategoryController).setSingleView(view)
        .setHandlerExceptionResolvers(getSimpleMappingExceptionResolver()).build();

几天前我也遇到了同样的问题,你可以在这里看到我自己回答的问题和解决方案Spring MVC控制器异常测试

希望我的回答对你有帮助


0
投票

使用 Spring MockMVC 模拟 servletContainer,以便您可以在单元测试套件中合并任何请求过滤或异常处理测试。

您可以使用以下方法配置此设置:

给出自定义 RecordNotFound 异常...

@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Record not found") //
public class RecordNotFoundException extends RuntimeException {

    private static final long serialVersionUID = 8857378116992711720L;

    public RecordNotFoundException() {
        super();
    }

    public RecordNotFoundException(String message) {
        super(message);
    }
}

...和 RecordNotFoundExceptionHandler

@Slf4j
@ControllerAdvice
public class BusinessExceptionHandler {

    @ExceptionHandler(value = RecordNotFoundException.class)
    public ResponseEntity<String> handleRecordNotFoundException(
            RecordNotFoundException e,
            WebRequest request) {
         //Logs
        LogError logging = new LogError("RecordNotFoundException",
                HttpStatus.NOT_FOUND, 
                request.getDescription(true));
        log.info(logging.toJson());

        //Http error message
        HttpErrorResponse response = new HttpErrorResponse(logging.getStatus(), e.getMessage());
        return new ResponseEntity<>(response.toJson(),
                HeaderFactory.getErrorHeaders(),
                response.getStatus());
    }
   ...
}

配置定制的测试上下文:设置 @ContextConfiguration 来指定测试所需的类。将 Mockito MockMvc 设置为 servlet 容器模拟器并设置测试装置和依赖项。

 @RunWith(SpringRunner.class)
@ContextConfiguration(classes = {
    WebConfig.class,
    HeaderFactory.class,
})
@Slf4j
public class OrganisationCtrlTest {

    private MockMvc mvc;

    private Organisation coorg;

    @MockBean
    private OrganisationSvc service;

    @InjectMocks
    private OrganisationCtrl controller = new OrganisationCtrl();

    //Constructor
    public OrganisationCtrlTest() {
    }
   ....

配置模拟 MVC“servlet 模拟器”:在上下文中注册处理程序 bean 并构建 mockMvc 模拟器(注意:有两种可能的配置:standaloneSetup 或 webAppContextSetup;请参阅 文档)。构建器正确地实现了构建器模式,因此您可以在调用 build() 之前链接异常解析器和处理程序的配置命令。

    @Before
    public void setUp() {
        final StaticApplicationContext appContext = new StaticApplicationContext();
        appContext.registerBeanDefinition("BusinessExceptionHandler",
                new RootBeanDefinition(BusinessExceptionHandler.class, null, null));

//InternalExceptionHandler extends ResponseEntityExceptionHandler to //handle Spring internally throwned exception
        appContext.registerBeanDefinition("InternalExceptionHandler",
                new RootBeanDefinition(InternalExceptionHandler.class, null,
                        null));
        MockitoAnnotations.initMocks(this);
        mvc = MockMvcBuilders.standaloneSetup(controller)
                .setHandlerExceptionResolvers(getExceptionResolver(appContext))
                .build();
        coorg = OrganisationFixture.getFixture("orgID", "name", "webSiteUrl");
    }
    ....

获取异常解析器

private ExceptionHandlerExceptionResolver getExceptionResolver(
        StaticApplicationContext context) {
    ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
    resolver.getMessageConverters().add(
            new MappingJackson2HttpMessageConverter());
    resolver.setApplicationContext(context);
    resolver.afterPropertiesSet();
    return resolver;
}

运行测试

    @Test
    public void testGetSingleOrganisationRecordAnd404() throws Exception {
        System.out.println("testGetSingleOrganisationRecordAndSuccess");
        String request = "/orgs/{id}";
        log.info("Request URL: " + request);

        when(service.getOrganisation(anyString())).
                thenReturn(coorg);
        this.mvc.perform(get(request)
                .accept("application/json")
                .andExpect(content().contentType(
                        .APPLICATION_JSON))
                .andExpect(status().notFound())
                .andDo(print());
    }
    ....
}

希望这有帮助。

杰克。


0
投票
@Before
public void setup() {
    this.mockMvc = MockMvcBuilders
         .standaloneSetup(YOUR_REST_CONTROLLER_INSTANCE)
         .setControllerAdvice(YOUR_EXCEPTION_HANDLER_INSTANCE)
         .build();
}

-2
投票

尝试一下;

@RunWith(value = SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { MVCConfig.class, CoreConfig.class, 
        PopulaterConfiguration.class })
public class ExceptionControllerTest {

    private MockMvc mockMvc;

    @Mock
    private StatusController statusController;

    @Autowired
    private WebApplicationContext wac;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void checkUnexpectedExceptionsAreCaughtAndStatusCode500IsReturnedInResponse() throws Exception {

        when(statusController.checkHealth()).thenThrow(new RuntimeException("Unexpected Exception"));

        mockMvc.perform(get("/api/status"))
                .andDo(print())
                .andExpect(status().isInternalServerError())
                .andExpect(jsonPath("$.error").value("Unexpected Exception"));
    }
}

-2
投票

这样更好:

((HandlerExceptionResolverComposite) wac.getBean("handlerExceptionResolver")).getExceptionResolvers().get(0)

并且不要忘记在 @Configuration 类中扫描 @ControllerAdvice beans:

@ComponentScan(basePackages = {"com.company.exception"})

...在 Spring 4.0.2.RELEASE 上测试

© www.soinside.com 2019 - 2024. All rights reserved.