Performing Requests
本部分展示了如何使用“MockMvcTester”执行请求及其与 AssertJ 的集成以验证响应。
“MockMvcTester”提供了一个流畅的 API 来组合与 Hamcrest 支持重复使用同一“MockHttpServletRequestBuilder”的请求,不同之处在于不需要导入静态方法。返回的生成器支持 AssertJ,因此将其包装在常规“assertThat()”工厂方法中会触发交换并提供对“MvcTestResult”的专用 Assert 对象的访问权限。
以下是一个简单的示例,它在“/hotels/42”上执行“POST”并将请求配置为指定“Accept”标头:
include-code::./HotelControllerTests[tag=post,indent=0]
AssertJ 通常包含多个“assertThat()”语句来验证交换的不同部分。与其像上面示例中那样使用单个语句,可以使用“exchange()”返回可以在多个“assertThat”语句中使用的“MvcTestResult”:
include-code::./HotelControllerTests[tag=post-exchange,indent=0]
您可以使用 URI 模板样式指定查询参数,如下例所示:
include-code::./HotelControllerTests[tag=query-parameters,indent=0]
您还可以添加 Servlet 请求参数,表示 query 或 formparameters,如下例所示:
include-code::./HotelControllerTests[tag=parameters,indent=0]
如果应用程序代码依赖于 Servlet 请求参数,并且没有明确检查 querystring(这种情况最常见),您使用哪种选项无关紧要。但请记住,使用 URI 模板提供的查询参数已解码,而通过 param(…)
方法提供的请求参数应已解码。
Async
如果异步完成请求的处理,exchange()
将等待请求完成,以便断言的结果实际上是不可变的。默认超时时间为 10 秒,但可以按请求逐个请求控制,如下例所示:
-
Java
-
Kotlin
assertThat(mockMvc.get().uri("/compute").exchange(Duration.ofSeconds(5)))
. // ...
assertThat(mockMvc.get().uri("/compute").exchange(Duration.ofSeconds(5)))
. // ...
如果您希望获取原始结果并自己管理异步请求的生命周期,请使用 asyncExchange
而不是 exchange
。
Multipart
您可以执行文件上传请求,内部使用 MockMultipartHttpServletRequest
,以便不会实际解析多部分请求。相反,您必须将其设置为类似于以下示例:
-
Java
-
Kotlin
assertThat(mockMvc.post().uri("/upload").multipart()
.file("file1.txt", "Hello".getBytes(StandardCharsets.UTF_8))
.file("file2.txt", "World".getBytes(StandardCharsets.UTF_8)))
. // ...
assertThat(mockMvc.post().uri("/upload").multipart()
.file("file1.txt", "Hello".toByteArray(StandardCharsets.UTF_8))
.file("file2.txt", "World".toByteArray(StandardCharsets.UTF_8)))
. // ...
Using Servlet and Context Paths
在大多数情况下,最好将上下文路径和 Servlet 路径从请求 URI 中排除出去。如果您必须使用完整的请求 URI 进行测试,请务必相应地设置 contextPath
和 servletPath
,以便请求映射正常工作,如下例所示:
-
Java
-
Kotlin
assertThat(mockMvc.get().uri("/app/main/hotels/{id}", 42)
.contextPath("/app").servletPath("/main"))
. // ...
assertThat(mockMvc.get().uri("/app/main/hotels/{id}", 42)
.contextPath("/app").servletPath("/main"))
. // ...
在前面的示例中,在每个执行的请求中设置 contextPath
和 servletPath
会很麻烦。相反,您可以设置默认请求属性,如下例所示:
-
Java
-
Kotlin
MockMvcTester mockMvc = MockMvcTester.of(List.of(new HotelController()),
builder -> builder.defaultRequest(get("/")
.contextPath("/app").servletPath("/main")
.accept(MediaType.APPLICATION_JSON)).build());
val mockMvc =
MockMvcTester.of(listOf(HotelController())) { builder: StandaloneMockMvcBuilder ->
builder.defaultRequest<StandaloneMockMvcBuilder>(
MockMvcRequestBuilders.get("/")
.contextPath("/app").servletPath("/main")
.accept(MediaType.APPLICATION_JSON)
).build()
}
前面的属性会影响通过 mockMvc
实例执行的每个请求。如果在给定的请求上也指定了相同的属性,它将覆盖默认值。这就是在默认请求中 HTTP 方法和 URI 无关紧要的原因,因为必须在每个请求中指定它们。