8.5.1 使用模拟 Web 服务器测试 REST 客户端
当应用程序依赖于下游服务时,你应该针对后者的 API 规范测试交互。在本节中,你将首先使用模拟 Web 服务器(充当 Catalog Service)测试 BookClient 类,以确保客户端的正确性。然后你将使用 @DataR2dbcTest 注解和 Testcontainers 通过切片测试测试数据持久化层,就像你在第 5 章中使用 @DataJdbcTest 所做的那样。最后,你将使用 @WebFluxTest 注解为 Web 层编写切片测试,它的工作方式与 @WebMvcTest 相同,但用于响应式应用程序。
你已经拥有了 Spring Boot 测试库和 Testcontainers 的必要依赖。缺少的是对 com.squareup.okhttp3:mockwebserver 的依赖,它将提供运行模拟 Web 服务器的工具。打开 Order Service 项目的 build.gradle 文件,添加缺少的依赖。
代码清单 8.27 添加 OkHttp MockWebServer 的测试依赖
dependencies {
// ... 其他依赖
testImplementation 'com.squareup.okhttp3:mockwebserver'
}
让我们从测试 BookClient 类开始。
使用模拟 Web 服务器测试 REST 客户端
OkHttp 项目提供了一个模拟 Web 服务器,你可以使用它来测试与下游服务的基于 HTTP 的请求/响应交互。BookClient 返回一个 Mono
首先,让我们设置模拟 Web 服务器并在新的 BookClientTests 类中配置 WebClient 使用它。
代码清单 8.28 使用模拟 Web 服务器准备测试设置
package com.polarbookshop.orderservice.book;
import java.io.IOException;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.jupiter.api.*;
import org.springframework.web.reactive.function.client.WebClient;
class BookClientTests {
private MockWebServer mockWebServer;
private BookClient bookClient;
@BeforeEach
void setup() throws IOException {
this.mockWebServer = new MockWebServer();
this.mockWebServer.start(); // 在运行测试用例之前启动模拟服务器
var webClient = WebClient.builder()
.baseUrl(mockWebServer.url("/").uri().toString()) // 使用模拟服务器 URL 作为 WebClient 的基础 URL
.build();
this.bookClient = new BookClient(webClient);
}
@AfterEach
void clean() throws IOException {
this.mockWebServer.shutdown(); // 完成测试用例后关闭模拟服务器
}
}
接下来,在 BookClientTests 类中,你可以定义一些测试用例来验证 Order Service 中客户端的功能。
代码清单 8.29 测试与 Catalog Service 应用程序的交互
package com.polarbookshop.orderservice.book;
// ... 其他导入
import okhttp3.mockwebserver.MockResponse;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
class BookClientTests {
private MockWebServer mockWebServer;
private BookClient bookClient;
// ... setup 和 clean 方法
@Test
void whenBookExistsThenReturnBook() {
var bookIsbn = "1234567890";
var mockResponse = new MockResponse() // 定义模拟服务器要返回的响应
.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody("""
{
"isbn": %s,
"title": "Title",
"author": "Author",
"price": 9.90,
"publisher": "Polarsophia"
}
""".formatted(bookIsbn));
mockWebServer.enqueue(mockResponse); // 将模拟响应添加到模拟服务器处理的队列中
Mono<Book> book = bookClient.getBookByIsbn(bookIsbn);
StepVerifier.create(book) // 使用 BookClient 返回的对象初始化 StepVerifier 对象
.expectNextMatches(b -> b.isbn().equals(bookIsbn)) // 断言返回的 Book 具有请求的 ISBN
.verifyComplete(); // 验证响应式流已成功完成
}
}
让我们运行测试并确保它们成功。打开终端窗口,导航到 Order Service 项目的根文件夹,运行以下命令:
$ ./gradlew test --tests BookClientTests
注意: 使用模拟时,可能会出现测试结果取决于测试用例执行顺序的情况,这在相同操作系统上往往相同。为了防止不需要的执行依赖,你可以使用 @TestMethodOrder(MethodOrderer.Random.class) 注解测试类,以确保每次执行时使用伪随机顺序。
测试完 REST 客户端部分后,你可以继续验证 Order Service 的数据持久化层。