|
此版本仍在开发中,尚未被视为稳定版。如需最新稳定版本,请使用 Spring Framework 7.0.6! |
测试客户端应用程序
要测试使用 RestClient 或 RestTemplate 的代码,您可以使用模拟 Web 服务器,例如
OkHttp MockWebServer 或
WireMock。模拟 Web 服务器像常规服务器一样通过 HTTP 接收请求,这意味着您可以使用与生产环境中配置完全相同的 HTTP 客户端进行测试,这一点非常重要,因为不同的客户端在处理网络 I/O 时往往存在细微差异。使用模拟 Web 服务器的另一个优势是,结合生产环境中使用的客户端,可以在传输层模拟特定的网络问题和条件。
除了专用的模拟 Web 服务器之外,Spring Framework 历来还提供了一种内置选项,通过 RestClient 来测试 RestTemplate 或 MockRestServiceServer。
该方式依赖于为被测客户端配置一个自定义的 ClientHttpRequestFactory,
该工厂由模拟服务器支持,而该模拟服务器则被设置为预期接收请求并发送“桩”(stub)响应,
从而让你能够专注于隔离测试代码,而无需实际运行服务器。
MockRestServiceServer 的出现早于模拟 Web 服务器。目前,我们建议使用模拟 Web 服务器来更全面地测试传输层和网络状况。 |
以下示例展示了如何使用 MockRestServiceServer:
-
Java
-
Kotlin
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess());
// Test code that uses the above RestTemplate ...
mockServer.verify();
val restTemplate = RestTemplate()
val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess())
// Test code that uses the above RestTemplate ...
mockServer.verify()
在前面的示例中,MockRestServiceServer(用于客户端 REST 测试的核心类)通过一个自定义的 RestTemplate 对 ClientHttpRequestFactory 进行配置,该工厂会将实际请求与预期进行比对,并返回“模拟”(stub)响应。在此例中,我们期望收到一个发往 /greeting 的请求,并希望返回一个状态码为 200、内容类型为 text/plain 的响应。我们可以根据需要定义更多预期的请求和模拟响应。在定义好这些预期请求和模拟响应之后,RestTemplate 在客户端代码中可以像平常一样使用。测试结束时,可以调用 mockServer.verify() 来验证所有预期是否都已满足。
默认情况下,请求应按照声明期望的顺序到达。你可以在构建服务器时设置 ignoreExpectOrder 选项,这样系统会按顺序检查所有期望,以找到与给定请求匹配的项。这意味着请求可以以任意顺序到达。以下示例使用了 ignoreExpectOrder:
-
Java
-
Kotlin
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build()
即使默认情况下请求是无序的,每个请求也只允许执行一次。
expect 方法提供了一个重载变体,该变体接受一个 ExpectedCount 参数,
用于指定调用次数的范围(例如 once、manyTimes、max、min、
between 等)。以下示例使用了 times:
-
Java
-
Kotlin
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(times(2), requestTo("/something")).andRespond(withSuccess());
mockServer.expect(times(3), requestTo("/somewhere")).andRespond(withSuccess());
// ...
mockServer.verify();
val restTemplate = RestTemplate()
val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
mockServer.expect(times(2), requestTo("/something")).andRespond(withSuccess())
mockServer.expect(times(3), requestTo("/somewhere")).andRespond(withSuccess())
// ...
mockServer.verify()
请注意,当未设置 ignoreExpectOrder(默认情况)时,请求将按照声明的顺序进行匹配,但该顺序仅适用于每个预期请求的第一次出现。例如,如果先预期两次对 "/something" 的请求,然后预期三次对 "/somewhere" 的请求,那么在任何对 "/somewhere" 的请求之前,必须先有一次对 "/something" 的请求;除此之外,后续的 "/something" 和 "/somewhere" 请求可以在任意时间发生。
作为上述所有方法的替代方案,客户端测试支持还提供了一个 ClientHttpRequestFactory 实现,你可以将其配置到 RestTemplate 中,
从而将其绑定到一个 MockMvc 实例。这样可以在不启动服务器的情况下,使用实际的服务器端逻辑来处理请求。以下示例展示了如何实现这一点:
-
Java
-
Kotlin
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
this.restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));
// Test code that uses the above RestTemplate ...
val mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build()
restTemplate = RestTemplate(MockMvcClientHttpRequestFactory(mockMvc))
// Test code that uses the above RestTemplate ...
在某些情况下,可能需要实际调用远程服务,而不是模拟响应。以下示例展示了如何通过 ExecutingResponseCreator 来实现这一点:
-
Java
-
Kotlin
RestTemplate restTemplate = new RestTemplate();
// Create ExecutingResponseCreator with the original request factory
ExecutingResponseCreator withActualResponse = new ExecutingResponseCreator(restTemplate.getRequestFactory());
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(requestTo("/profile")).andRespond(withSuccess());
mockServer.expect(requestTo("/quoteOfTheDay")).andRespond(withActualResponse);
// Test code that uses the above RestTemplate ...
mockServer.verify();
val restTemplate = RestTemplate()
// Create ExecutingResponseCreator with the original request factory
val withActualResponse = new ExecutingResponseCreator(restTemplate.getRequestFactory())
val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
mockServer.expect(requestTo("/profile")).andRespond(withSuccess())
mockServer.expect(requestTo("/quoteOfTheDay")).andRespond(withActualResponse)
// Test code that uses the above RestTemplate ...
mockServer.verify()
在前面的示例中,我们在 ExecutingResponseCreator 将 ClientHttpRequestFactory 的 RestTemplate 替换为另一个用于模拟响应的工厂 之前,先使用该 MockRestServiceServer 创建了 4。
然后,我们通过两种类型的响应来定义预期行为:
-
针对
200端点的一个存根/profile响应(不会执行实际的请求) -
通过调用
/quoteOfTheDay端点获得的响应
在第二种情况下,请求通过之前捕获的 ClientHttpRequestFactory 执行。这会生成一个响应,该响应可能来自实际的远程服务器,具体取决于 RestTemplate 最初是如何配置的。
静态导入
与服务端测试类似,客户端测试的流式 API 需要导入一些静态方法。这些静态导入很容易通过搜索 MockRest* 找到。Eclipse 用户应在 Eclipse 的偏好设置中(路径为 Java → Editor → Content Assist → Favorites)将 MockRestRequestMatchers.* 和 MockRestResponseCreators.* 添加为“收藏的静态成员”。这样,在输入静态方法名的第一个字符后,即可使用内容辅助功能。其他 IDE(例如 IntelliJ)可能无需任何额外配置。请查阅相关 IDE 对静态成员代码补全的支持情况。
客户端 REST 测试的更多示例
Spring MVC Test 自身的测试包含客户端 REST 测试的示例。