如何Mock WebClient的onStatus方法以覆盖测试代码分支?
如何Mock WebClient的onStatus()分支以覆盖4xx错误处理代码
要覆盖WebClient中onStatus()处理4xx错误的代码分支,你需要模拟一个4xx状态的响应,并确保onStatus()的谓词条件被触发,执行对应的错误逻辑。以下是具体的修改步骤和完整测试用例:
问题根源
你的原有测试用例模拟了正常成功的响应(Mono.just(...)),不会触发onStatus(HttpStatus::is4xxClientError, ...)的分支判断。需要构造一个4xx状态的响应场景,让该分支的代码被执行。
修改后的测试用例
@Test void test4xxClientErrorHandling() throws URISyntaxException, InterruptedException, ExecutionException { // GIVEN DtoReq requestDTO = new DtoReq(); String expectedErrorMessage = "4xx Client Error"; // Mock WebClient的各阶段Spec WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class); WebClient.RequestHeadersSpec<?> headerSpec = mock(WebClient.RequestHeadersSpec.class); WebClient.RequestBodySpec bodySpec = mock(WebClient.RequestBodySpec.class); WebClient.ResponseSpec responseSpec = mock(WebClient.ResponseSpec.class); // 构建WebClient调用链的Mock given(webClient.patch()).willReturn(requestBodyUriSpec); given(requestBodyUriSpec.uri(anyString())).willReturn(headerSpec); given(headerSpec.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)).willReturn(bodySpec); given(bodySpec.bodyValue(any(DtoReq.class))).willReturn(bodySpec); given(bodySpec.retrieve()).willReturn(responseSpec); // Mock onStatus方法:验证4xx谓词匹配,并触发错误处理逻辑 given(responseSpec.onStatus( argMatches(predicate -> predicate.test(HttpStatus.BAD_REQUEST)), any(Function.class) )).willAnswer(invocation -> { // 获取传入的错误处理函数 Function<ClientResponse, Mono<? extends Throwable>> errorHandler = invocation.getArgument(1); // 构造一个4xx状态的ClientResponse ClientResponse mockClientResponse = mock(ClientResponse.class); given(mockClientResponse.statusCode()).willReturn(HttpStatus.BAD_REQUEST); // 执行错误处理函数(覆盖onStatus内的代码分支) errorHandler.apply(mockClientResponse); return responseSpec; }); // Mock bodyToMono返回4xx异常,触发整个错误链路 given(responseSpec.bodyToMono(DVAUpdateStatusResponse.class)).willReturn(Mono.error( WebClientResponseException.create(400, "Bad Request", HttpHeaders.EMPTY, null, null, null) )); // WHEN & THEN // 验证最终抛出的异常(由onStatus+onErrorResume包装后的RuntimeException) RuntimeException thrown = assertThrows(RuntimeException.class, () -> integrationService.toBeTested(requestDTO).get()); assertTrue(thrown.getMessage().contains("error:")); }
关键说明
- 模拟4xx响应状态:通过
WebClientResponseException构造400错误的异常,让bodyToMono返回错误信号,触发onStatus的分支判断。 - 触发onStatus内的逻辑:在
willAnswer中手动调用传入的错误处理函数,确保onStatus内的Mono.error(new RuntimeException(errorMessage))代码被执行,覆盖该分支。 - 链式调用保持:确保每个WebClient的Spec方法(如
onStatus)都返回对应的Spec对象,维持调用链的完整性。 - 验证异常:通过
assertThrows验证最终抛出的异常,确保错误链路执行正常。
额外优化建议
- 如果需要验证日志输出(比如
log.info("success", res)或错误日志),可以使用@MockBean注入Logger并验证调用。 - 可以使用
ArgumentMatchers的eq(HttpStatus::is4xxClientError)替代argMatches,但需要注意方法引用的匹配(部分Mockito版本可能需要调整)。
内容的提问来源于stack exchange,提问作者Divya Singh
相关产品推荐
相关产品推荐

