Spring MockMvc 和异步控制器的 HTTP 状态码
Posted
技术标签:
【中文标题】Spring MockMvc 和异步控制器的 HTTP 状态码【英文标题】:Spring MockMvc and async controller's HTTP status code 【发布时间】: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 的回答。 :) 【参考方案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"))
【讨论】:
以上是关于Spring MockMvc 和异步控制器的 HTTP 状态码的主要内容,如果未能解决你的问题,请参考以下文章
Spring - 如何正确使用@Autowired 来防止控制器/ MockMvc 为空?
MockMvc 和 Spring Security - Null FilterChainProxy
spring boot 测试无法注入 TestRestTemplate 和 MockMvc