跳到主要内容

执行请求

DeepSeek V3 中英对照 Performing Requests

本节展示了如何使用 MockMvcTester 执行请求,并如何与 AssertJ 集成以验证响应。

MockMvcTester 提供了一个流式 API 来构建请求,它重用了与 Hamcrest 支持相同的 MockHttpServletRequestBuilder,只不过无需导入静态方法。返回的构建器是 AssertJ 感知的,因此将其包装在常规的 assertThat() 工厂方法中会触发交换,并提供对 MvcTestResult 专用断言对象的访问。

这是一个简单的示例,它在 /hotels/42 上执行 POST 请求,并配置请求以指定 Accept 头:

fetch('/hotels/42', {
method: 'POST',
headers: {
'Accept': 'application/json'
}
});
javascript
assertThat(mockMvc.post().uri("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON))
. // ...
java

AssertJ 通常由多个 assertThat() 语句组成,用于验证交换的不同部分。与上面例子中只有一个语句不同,你可以使用 .exchange() 返回一个 MvcTestResult,该结果可以在多个 assertThat 语句中使用:

MvcTestResult result = mockMvc.post().uri("/hotels/{id}", 42)
.accept(MediaType.APPLICATION_JSON).exchange();
assertThat(result). // ...
java

你可以在 URI 模板样式中指定查询参数,如下例所示:

assertThat(mockMvc.get().uri("/hotels?thing={thing}", "somewhere"))
. // ...
java

你也可以添加代表查询或表单参数的 Servlet 请求参数,如下例所示:

assertThat(mockMvc.get().uri("/hotels").param("thing", "somewhere"))
. // ...
java

如果应用程序代码依赖于 Servlet 请求参数并且没有显式检查查询字符串(大多数情况下都是如此),那么使用哪种选项并不重要。但请记住,通过 URI 模板提供的查询参数会被解码,而通过 param(…​) 方法提供的请求参数则预期已经解码。

异步

如果请求的处理是异步完成的,exchange() 会等待请求完成,以便断言的结果实际上是不可变的。默认的超时时间为 10 秒,但可以按请求进行控制,如下例所示:

assertThat(mockMvc.get().uri("/compute").exchange(Duration.ofSeconds(5)))
. // ...
java

如果你更倾向于获取原始结果并自行管理异步请求的生命周期,请使用 asyncExchange 而不是 exchange

多部分请求

你可以执行文件上传请求,这些请求在内部使用 MockMultipartHttpServletRequest,因此实际上不会解析多部分请求。相反,你需要将其设置为类似于以下示例:

assertThat(mockMvc.post().uri("/upload").multipart()
.file("file1.txt", "Hello".getBytes(StandardCharsets.UTF_8))
.file("file2.txt", "World".getBytes(StandardCharsets.UTF_8)))
. // ...
java

使用 Servlet 和上下文路径

在大多数情况下,最好将上下文路径和 Servlet 路径从请求 URI 中排除。如果必须使用完整的请求 URI 进行测试,请确保相应地设置 contextPathservletPath,以便请求映射正常工作,如下例所示:

assertThat(mockMvc.get().uri("/app/main/hotels/{id}", 42)
.contextPath("/app").servletPath("/main"))
. // ...
java

在前面的示例中,每次执行请求时设置 contextPathservletPath 会非常繁琐。相反,你可以设置默认的请求属性,如下例所示:

MockMvcTester mockMvc = MockMvcTester.of(List.of(new HotelController()),
builder -> builder.defaultRequest(get("/")
.contextPath("/app").servletPath("/main")
.accept(MediaType.APPLICATION_JSON)).build());
java

上述属性会影响通过 mockMvc 实例执行的每个请求。如果在某个请求上指定了相同的属性,它将覆盖默认值。这就是为什么默认请求中的 HTTP 方法和 URI 并不重要,因为它们必须在每个请求中指定。