如何为返回Mono的Spring Webflux服务类编写组件测试
问题根因
你的submit方法存在核心的Reactor流操作逻辑错误:
调用bodyToMono(Void.class)得到的Mono<Void>是只会发射完成信号、不会发射任何数据元素的特殊Publisher。而map操作符仅会对上游发射的数据元素做转换,上游没有元素下发时map里的生成响应逻辑永远不会执行,最终返回的Mono直接完成,调用block()自然得到null。
修复业务代码
把map换成then操作符即可,then会等待上游Mono<Void>完成后,再切换到你指定的下游Publisher:
private Mono<SubmissionResponse> submit(Submission mySubmission) { return webClient .post() .uri("/submit") .body(Mono.just(mySubmission), Submission.class) .retrieve() .onStatus(status -> status.equals(HttpStatus.NOT_FOUND), resp -> Mono.just(new SubmissionNotFoundException("No Submission found", HttpStatus.NOT_FOUND.name()))) .bodyToMono(Void.class) // 替换map为then,等待上游完成后生成响应 .then(Mono.fromSupplier(() -> myResponseGenerator.generateResponse(mySubmission))); }
如果下游接口实际会返回响应体,也可以根据返回值类型调整bodyToMono的参数类型,不要用Void,这样map也能正常触发。
正确的组件测试编写
1. 配置正确的WireMock桩
要匹配WebClient的调用方法、路径、请求体,返回你预期的状态码:
// WireMock 桩配置示例 stubFor(post(urlEqualTo("/submit")) .withRequestBody(equalToJson(/* mySubmission序列化后的JSON串 */)) .willReturn(aResponse() .withStatus(201) ) );
2. 推荐用StepVerifier做响应式测试
相比手动block,StepVerifier更符合响应式测试规范,能更方便的断言流的行为:
// 导入reactor.test.StepVerifier StepVerifier.create(submissionService.submit(mySubmission)) // 断言会发出一个非空响应元素 .expectNextMatches(response -> { // 这里添加你对response字段的校验逻辑 return response != null; }) // 断言流正常完成 .verifyComplete();
修复业务代码后,你原本用block的写法也能拿到非null的response对象。
额外注意点
- 确保测试上下文里WebClient的baseUrl配置为WireMock的监听地址,端口不要填错
- 如果有超时、重试逻辑,测试时可以适当调整超时时间避免流提前中断
- 确保
myResponseGenerator是被Spring正确注入的实例,避免生成响应时抛出空指针被流吞掉
内容的提问来源于stack exchange,提问作者Spartacus
相关产品推荐
相关产品推荐

