在Spock框架中编写Spring WebFlux集成测试时Mock WebClient并Stub数据
在Spock中Mock Spring WebFlux的WebClient并Stub外部API响应
1. 测试类基础配置
- 使用
@SpringBootTest启动Spring上下文,通过@MockBean替换应用中真实的WebClient或WebClient.Builder(取决于你应用的注入方式),避免发送真实的外部API请求。 - 如果应用通过
WebClient.Builder构建实例,就MockWebClient.Builder;若直接注入WebClient实例,直接MockWebClient即可。
2. Stub WebClient的链式调用
WebClient的调用是链式结构,需要逐步Mock每一步的返回值,最终Stub出目标响应。以下是具体示例:
示例场景:应用通过WebClient GET请求外部API
假设你的业务服务类如下:
@Service public class ExternalApiService { private final WebClient webClient; public ExternalApiService(WebClient webClient) { this.webClient = webClient; } public Mono<ExternalResponse> fetchData(String id) { return webClient.get() .uri("/external/api/{id}", id) .retrieve() .bodyToMono(ExternalResponse.class); } }
对应的Spock集成测试代码:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class ExternalApiIntegrationSpec extends Specification { @MockBean WebClient webClient @Autowired ExternalApiService externalApiService // 注入RestAssured的RequestSpecification用于发起应用API请求 @Autowired RequestSpecification requestSpecification def "测试应用API调用时,WebClient返回Stub数据"() { given: "Stub WebClient的全链路调用" def mockUriSpec = Mock(WebClient.RequestHeadersUriSpec) def mockRequestHeadersSpec = Mock(WebClient.RequestHeadersSpec) def mockResponseSpec = Mock(WebClient.ResponseSpec) def stubbedExternalResp = new ExternalResponse(id: "123", content: "mock external data") // 逐步Stub每一步的返回结果 webClient.get() >> mockUriSpec mockUriSpec.uri("/external/api/{id}", "123") >> mockRequestHeadersSpec mockRequestHeadersSpec.retrieve() >> mockResponseSpec mockResponseSpec.bodyToMono(ExternalResponse.class) >> Mono.just(stubbedExternalResp) when: "通过RequestSpecification调用应用API" def appResp = requestSpecification .get("/app/internal/data/123") .then() .statusCode(200) .extract() .as(AppResponse.class) then: "验证返回数据来自Stub的WebClient响应" appResp.content == "mock external data" } }
3. 适配POST等其他HTTP方法
如果是POST请求,只需调整起始方法的Stub逻辑,后续步骤同理:
// Stub POST请求起始 webClient.post() >> mockUriSpec // Stub URI和请求体 mockUriSpec.uri("/external/api/create") >> mockRequestHeadersSpec mockRequestHeadersSpec.body(BodyInserters.fromValue(_ as CreateRequest)) >> mockRequestHeadersSpec
4. Stub错误场景
比如模拟外部API返回404错误:
mockResponseSpec.onStatus({ it.is4xxClientError() }, _) >> Mono.error(new WebClientResponseException( 404, "Not Found", null, null, null, null ))
5. 额外注意事项
- 确保
@MockBean正确替换了应用中的WebClient实例,避免真实请求泄漏。 - Spock支持灵活的参数匹配,用
_匹配任意参数,eq()做精确匹配,可根据测试场景调整。 - 若应用通过
WebClient.Builder构建实例,需额外Stubbuild()方法:@MockBean WebClient.Builder webClientBuilder def setup() { webClientBuilder.build() >> webClient }
内容的提问来源于stack exchange,提问作者Srikanth Reddy Kallem
相关产品推荐
相关产品推荐

