如何为WebClient的.onStatus(4xx错误)块编写测试用例以覆盖代码
如何覆盖WebClient 4xx错误处理块的单元测试
问题背景
我编写的RepoService类中,WebClient调用接口的.onStatus(HttpStatus::is4xxClientError)处理块内代码未被测试覆盖,现有Mockito单元测试仅覆盖正常返回场景,如何编写测试用例覆盖该块逻辑?
RepoService 代码
public class RepoService { @Autowired private WebClient webClient; public DIResponse createIdentity(DICreateRequest diCreateRequest) { CreateReq createReq = diCreateRequestMapper.setRequest(diCreateRequest); DIResponse diResponse = null; UriComponents uriBuilder = UriComponentsBuilder .fromUriString(url) .build(false); DIResponse apiResponse = webClient .post() .uri(uriBuilder.toUriString()) .body(Mono.just(createReq), CreateReq.class) .retrieve() .onStatus(HttpStatus::is4xxClientError, clientResponse -> clientResponse.bodyToMono(DIBadRequest.class).flatMap(errorResponse -> { LOGGER.error(CLIENT_ERROR, "POST", clientResponse.statusCode(), errorResponse); return Mono.error(new DataAccessException(ERROR_STATUS +clientResponse.statusCode()+ ERROR_DESC+ errorResponse.getDetail())); })) .bodyToMono(DIResponse.class) .block(); return apiResponse; } }
现有Mockito单元测试代码
@Mock private WebClient webClient; @Mock WebClient.RequestHeadersUriSpec requestHeadersUriSpecMock; @Mock WebClient.RequestBodyUriSpec requestBodyUriSpecMock; @Mock WebClient.RequestBodySpec requestBodySpecMock; @Mock WebClient.RequestHeadersSpec requestHeadersSpecMock; @Mock WebClient.ResponseSpec responseSpecMock; @InjectMocks private RepoService repositoryService; @Test void testCreateIdentity() throws IOException { DICreateRequest mockDICreateRequest = getMockDICreatReq(); CreateReq createReq = createDI(); DIResponse mockDIResponse = getDI(); when(diCreateRequestMapper.setRequest(mockDICreateRequest)).thenReturn(createReq); when(webClient.post()).thenReturn(requestBodyUriSpecMock); when(requestBodyUriSpecMock.uri((URI) any())).thenReturn(requestBodySpecMock); when(requestBodySpecMock.body(any())).thenReturn(requestHeadersSpecMock); when(requestHeadersSpecMock.retrieve()).thenReturn(responseSpecMock); when(responseSpecMock.onStatus(any(), any())).thenReturn(responseSpecMock); Mockito.when(responseSpecMock.bodyToMono(DIResponse.class)).thenReturn(Mono.just(mockDIResponse)); DIResponse result = repositoryService.createIdentity(mockDICreateRequest); Assert.assertEquals(mockDIResponse.getId(), result.getId()); }
解决方案
要覆盖4xx错误处理逻辑,需要模拟WebClient返回4xx状态码并触发.onStatus中的错误处理逻辑,具体步骤如下:
1. 新增Mock对象
在测试类中添加ClientResponse的Mock,用来模拟4xx响应:
@Mock private ClientResponse clientResponse;
2. 编写4xx场景测试用例
@Test void testCreateIdentity_4xxError() { // 准备测试数据 DICreateRequest mockDICreateRequest = getMockDICreatReq(); CreateReq createReq = createDI(); DIBadRequest mockErrorResponse = new DIBadRequest(); mockErrorResponse.setDetail("参数格式错误"); HttpStatus badRequestStatus = HttpStatus.BAD_REQUEST; // 映射请求转换逻辑 when(diCreateRequestMapper.setRequest(mockDICreateRequest)).thenReturn(createReq); // 模拟WebClient调用链基础流程 when(webClient.post()).thenReturn(requestBodyUriSpecMock); when(requestBodyUriSpecMock.uri((String) any())).thenReturn(requestBodySpecMock); when(requestBodySpecMock.body(eq(Mono.just(createReq)), eq(CreateReq.class))).thenReturn(requestHeadersSpecMock); when(requestHeadersSpecMock.retrieve()).thenReturn(responseSpecMock); // 关键:触发4xx错误处理逻辑 when(responseSpecMock.onStatus(eq(HttpStatus::is4xxClientError), any())).thenAnswer(invocation -> { // 获取传入的错误处理函数 Function<ClientResponse, Mono<? extends Throwable>> errorHandler = invocation.getArgument(1); // 模拟ClientResponse的状态码和错误体 when(clientResponse.statusCode()).thenReturn(badRequestStatus); when(clientResponse.bodyToMono(DIBadRequest.class)).thenReturn(Mono.just(mockErrorResponse)); // 执行错误处理函数,得到错误Mono Mono<? extends Throwable> errorMono = errorHandler.apply(clientResponse); // 让后续的bodyToMono返回错误Mono,触发block()时抛出异常 when(responseSpecMock.bodyToMono(DIResponse.class)).thenReturn(errorMono.flatMap(Mono::error)); return responseSpecMock; }); // 执行测试并捕获异常 DataAccessException exception = Assert.assertThrows(DataAccessException.class, () -> { repositoryService.createIdentity(mockDICreateRequest); }); // 验证异常信息是否符合预期 String expectedMessage = ERROR_STATUS + badRequestStatus + ERROR_DESC + mockErrorResponse.getDetail(); Assert.assertEquals(expectedMessage, exception.getMessage()); // 验证错误日志是否被正确记录 verify(LOGGER).error(eq(CLIENT_ERROR), eq("POST"), eq(badRequestStatus), eq(mockErrorResponse)); }
核心说明
- 使用
thenAnswer捕获.onStatus中的错误处理函数,手动触发执行 - 模拟
ClientResponse返回4xx状态码和对应的错误响应体 - 确保
bodyToMono(DIResponse.class)返回错误Mono,让block()抛出预期的DataAccessException - 最后验证异常信息和日志输出,确保错误处理逻辑完全执行
内容的提问来源于stack exchange,提问作者mathew thomas
相关产品推荐
相关产品推荐

