【问题标题】:Spring MockMvc and async controller's HTTP status codeSpring MockMvc 和异步控制器的 HTTP 状态码
【发布时间】:2015-10-31 19:39:36
【问题描述】:

当我的控制器具有异步 servlet 特性时,如何验证/测试 MockMvc 中的 500 内部服务器错误?

我正在为我的 REST 端点编写单元测试用例,作为测试用例的一部分,我需要验证服务器是否将 500 个内部错误作为 http 代码和适当的错误消息发送。

这是我基于 Spring Boot 的应用程序: (为了更好的可读性,所有的导入都被省略了)

@RestController
@RequestMapping("/user")
@EnableAutoConfiguration
@SpringBootApplication
public class App 
{
    @RequestMapping(method = RequestMethod.GET, value = "/{name}", 
            produces = MediaType.APPLICATION_JSON_VALUE)
    private DeferredResult<String> greetByJson(@PathVariable("name") final String name){
        DeferredResult<String> dResult = new DeferredResult<String>();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    dResult.setErrorResult(new RuntimeException("Boom!!! time for Internal server error"));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        return dResult;
    }

    public static void main( String[] args )
    {
        SpringApplication.run(App.class);
    }
}

这是我的 MovkMvc JUnit 测试用例:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MockServletContext.class)
@WebAppConfiguration
public class AppTest {

    private final MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new App())
            .build();

    @Test
    public void testAsyncInternalServerError() {
        try {
            MvcResult mvcResult = mockMvc.perform(
                    get("/user/naveen").accept(MediaType.APPLICATION_JSON_VALUE))
                    .andExpect(request().asyncStarted())
                    .andReturn();

            System.out.println("Http Response Content = " + mvcResult.getAsyncResult());
            System.out.println("Http Response Status Code = " + mvcResult.getResponse().getStatus());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

下面是控制台打印:

2015-08-08 18:11:51.494  INFO 10224 --- [           main] o.s.w.c.s.GenericWebApplicationContext   : Refreshing org.springframework.web.context.support.GenericWebApplicationContext@a82c5f1: startup date [Sat Aug 08 18:11:51 IST 2015]; root of context hierarchy
2015-08-08 18:11:51.526  INFO 10224 --- [           main] o.e.j.i.junit.runner.RemoteTestRunner    : Started RemoteTestRunner in 0.258 seconds (JVM running for 1.131)
Http Response Content = java.lang.RuntimeException: Boom!!! time for Internal server error
Http Response Status Code = 200
2015-08-08 18:11:56.584  INFO 10224 --- [       Thread-1] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@a82c5f1: startup date [Sat Aug 08 18:11:51 IST 2015]; root of context hierarchy

从上面的日志中可以明显看出,MockMvc 返回的 http 状态代码是 200 而不是 500。错误消息很好。

当我使用 Chrome 邮递员调用端点时,我看到图像中附有 500 内部服务器错误

【问题讨论】:

  • 你真的应该接受@mzc 的回答。 :)

标签: spring-mvc junit spring-boot spring-mvc-test mockmvc


【解决方案1】:

您必须执行异步调度并在之后测试状态:

@Test
public void testMethod() throws Exception {

    MvcResult mvcResult = mockMvc.perform(get("/your/endpoint"))
            .andExpect(request().asyncStarted())
            .andExpect(request().asyncResult(notNullValue()))
            .andReturn();

    mockMvc.perform(asyncDispatch(mvcResult))
            .andExpect(status().isInternalServerError())
            .andReturn();

}

【讨论】:

  • 嗨,对我来说,这一行的断言失败:.andExpect(request().asyncStarted()) 有什么想法吗?
【解决方案2】:

处理同步和异步请求的自定义perform 辅助方法:

ResultActions perform(MockHttpServletRequestBuilder builder) throws Exception {
    ResultActions resultActions = mockMvc.perform(builder);
    if (resultActions.andReturn().getRequest().isAsyncStarted()) {
      return mockMvc.perform(asyncDispatch(resultActions
          .andExpect(request().asyncResult(anything()))
          .andReturn()));
    } else {
      return resultActions;
    }
}

Longer answer with example here

【讨论】:

    【解决方案3】:

    以下是工作示例(Groovy Spock 规范),方法为 asyncPerform(builder)

    import static org.hamcrest.core.IsNull.notNullValue
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
    import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
    
    @ContextConfiguration(classes = [MyConfig])
    @WebAppConfiguration
    class MyControllerComponentSpec extends Specification {
    
        @Autowired
        WebApplicationContext webApplicationContext
    
        MockMvc endpoint
    
        def setup() {
            endpoint = MockMvcBuilders.webAppContextSetup(webApplicationContext).build()
        }
    
        ResultActions asyncPerform(MockHttpServletRequestBuilder builder) throws Exception {
             ResultActions resultActions = endpoint.perform(builder);
             asyncDispatch(resultActions.andExpect(request()
                            .asyncResult(notNullValue()))
                            .andReturn()));
         }
    
        def "accepts valid request and responds with 200 status code and response body"() {
            when:
            def response = asyncPerform(post("/my_async_endpoint")
                .content("""{"correlationID": "fe5d1699-20e3-4502-bf51-b947e6b9e51a"}""")
                .header("Content-Type", "application/json"))
                .andDo(print())
    
            then:
            response.andExpect(status().is(200))
                .andExpect(jsonPath("body.correlationID").value("fe5d1699-20e3-4502-bf51-b947e6b9e51a"))
    
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-22
      • 2019-09-27
      相关资源
      最近更新 更多