如何集成测试client模块调用运行中的web模块控制器?
跨Maven模块的WebClient集成测试方案
你的场景是:Maven多模块项目中,web是Spring MVC的控制器模块,client是基于Spring WebFlux的WebClient调用模块,当前web模块依赖client,需要实现client模块的集成测试使其能从运行中的web控制器获取数据,同时希望找到替代当前依赖方式的方案。以下是几种可行的实现方式:
一、解耦模块依赖 + 测试级依赖(推荐)
当前web依赖client的方式会导致模块耦合,更好的做法是抽离公共API层,让两个模块独立依赖API层,仅在测试阶段让client依赖web模块以启动服务。
步骤说明:
- 新建
api模块:将两个模块共用的DTO、请求/响应模型、接口定义等放在这里,比如DataResponse.java这类公共对象。 - 调整模块依赖:
web模块依赖api,控制器返回api中定义的模型;client模块依赖api,用WebClient封装对web接口的调用逻辑;
- client模块集成测试:在
client的测试中启动web模块的Spring上下文,通过随机端口获取服务地址,初始化WebClient进行调用。
代码示例:
client模块的测试类:
@SpringBootTest(classes = WebApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class WebClientIntegrationTest { @LocalServerPort private int serverPort; private WebClient webClient; @BeforeEach void initWebClient() { webClient = WebClient.builder() .baseUrl("http://localhost:" + serverPort) .build(); } @Test void testFetchDataFromWebController() { // 调用web模块的接口 DataResponse response = webClient.get() .uri("/api/data") .retrieve() .bodyToMono(DataResponse.class) .block(); // 断言响应结果符合预期 assertNotNull(response); assertEquals("expected-data", response.getContent()); } }
client模块pom.xml中添加测试依赖:
<dependency> <groupId>your.group.id</groupId> <artifactId>web</artifactId> <version>${project.version}</version> <scope>test</scope> </dependency>
二、使用Testcontainers启动独立Web服务
如果希望测试完全独立于本地代码环境,可以将web模块打包成Docker镜像,用Testcontainers在测试时启动容器化的Web服务,让client调用容器中的服务。
代码示例:
@Testcontainers public class WebClientContainerTest { // 启动web模块的Docker镜像 @Container private static final GenericContainer<?> webContainer = new GenericContainer<>("your-web-image:${project.version}") .withExposedPorts(8080); private WebClient webClient; @BeforeEach void initWebClient() { String baseUrl = String.format("http://%s:%d", webContainer.getHost(), webContainer.getMappedPort(8080)); webClient = WebClient.builder().baseUrl(baseUrl).build(); } @Test void testCallContainerizedWebService() { // 测试逻辑同前,验证容器中web服务的响应 String response = webClient.get() .uri("/api/health") .retrieve() .bodyToMono(String.class) .block(); assertEquals("UP", response); } }
三、用WireMock模拟Web服务(轻量测试)
如果仅需验证client的调用逻辑,不需要启动真实的Web服务,可以用WireMock模拟web控制器的响应,属于单元测试范畴,执行速度更快。
代码示例:
@ExtendWith(WireMockExtension.class) public class WebClientMockTest { @RegisterExtension static WireMockExtension wireMock = WireMockExtension.newInstance() .options(wireMockConfig().dynamicPort()) .build(); private WebClient webClient; @BeforeEach void initWebClient() { webClient = WebClient.builder().baseUrl(wireMock.baseUrl()).build(); } @Test void testHandleMockedWebResponse() { // 模拟web控制器的响应 wireMock.stubFor(get(urlEqualTo("/api/data")) .willReturn(aResponse() .withStatus(HttpStatus.OK.value()) .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .withBody("{\"content\":\"mocked-data\"}"))); // 调用并断言 DataResponse response = webClient.get() .uri("/api/data") .retrieve() .bodyToMono(DataResponse.class) .block(); assertEquals("mocked-data", response.getContent()); } }
方案对比
| 方案 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
| 解耦模块+测试依赖 | 日常开发、CI集成测试 | 轻量、复用现有代码、无需额外配置 | 测试依赖web模块代码,独立性稍弱 |
| Testcontainers | 生产环境模拟测试 | 完全独立、贴近生产部署 | 需要配置Docker镜像打包,测试启动较慢 |
| WireMock | 快速验证client逻辑 | 执行快、无需启动真实服务 | 仅验证client逻辑,无法覆盖web端真实逻辑 |
内容的提问来源于stack exchange,提问作者Thomas Lang
相关产品推荐
相关产品推荐

