测试客户端应用程序
要测试使用 RestClient 或 RestTemplate 的代码,您可以使用模拟 Web 服务器,例如 OkHttp MockWebServer 或 WireMock。模拟 Web 服务器像普通服务器一样通过 HTTP 接受请求,这意味着您可以使用与生产环境配置方式相同的 HTTP 客户端进行测试,这很重要,因为不同客户端处理网络 I/O 的方式通常存在细微差异。模拟 Web 服务器的另一个优点是能够结合生产环境中使用的客户端,在传输层模拟特定的网络问题和条件。
除了专门的模拟 Web 服务器,Spring 框架历史上还提供了一个内置选项,通过 MockRestServiceServer 来测试 RestClient 或 RestTemplate。这依赖于使用一个自定义的 ClientHttpRequestFactory 来配置被测试的客户端,该工厂由模拟服务器支持,而模拟服务器又被设置为预期请求并发送“存根”响应,这样您就可以专注于隔离测试代码,而无需运行服务器。
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 测试的核心类)使用自定义的 ClientHttpRequestFactory 配置 RestTemplate,该工厂根据预期断言实际请求并返回“存根”响应。在这种情况下,我们期望收到对 /greeting 的请求,并希望返回一个带有 text/plain 内容的 200 响应。我们可以根据需要定义额外的预期请求和存根响应。当我们定义了预期请求和存根响应后,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()
在前面的例子中,我们使用来自 RestTemplate 的 ClientHttpRequestFactory 创建了 ExecutingResponseCreator,在 MockRestServiceServer 用一个模拟响应的工厂替换它之前。然后我们定义了两种响应的预期:
-
针对
/profile端点的存根200响应(不会执行实际请求) -
通过调用
/quoteOfTheDay端点获得的响应
在第二种情况下,请求通过之前捕获的 ClientHttpRequestFactory 执行。这会生成一个响应,例如,可能来自实际的远程服务器,具体取决于 RestTemplate 最初的配置方式。
静态导入
与服务器端测试一样,客户端测试的流式 API 需要一些静态导入。通过搜索 MockRest* 很容易找到它们。Eclipse 用户应将 MockRestRequestMatchers.* 和 MockRestResponseCreators.* 添加为 Eclipse 偏好设置中 Java → 编辑器 → 内容辅助 → 收藏夹下的“收藏静态成员”。这允许在键入静态方法名称的第一个字符后使用内容辅助。其他 IDE(例如 IntelliJ)可能不需要任何额外的配置。请检查对静态成员代码完成的支持。
客户端 REST 测试的更多示例
Spring MVC Test 自己的测试包括 客户端 REST 测试的示例。