Mockito:如何覆盖WebClient调用中Mono的doFinally代码块
问题描述
现有一段WebClient调用代码,其中的doFinally块在JUnit测试中未被执行,导致Sonar代码覆盖率未覆盖该部分:
WebClient调用代码
webclient .post() .uri(url) .headers(headersConsumer -> getHeaders()) .bodyValue(requestObject) .exchangeToMono( response -> { if(response.statusCode().isError()) { log.error(new StringMapMessage() .with("info", "error") .with("responseTime", Duration.between(start, Instant.now()).toMillis()) .with("responseStatus", response.statusCode())); } return response.bodyToMono(String.class);}) .doOnError(error -> log.error("Encountered webclient exception while Publishing to Reporting API endpoint. ErrorMessage: {}", error.getMessage())) .retryWhen(Retry.backoff(3, Duration.ofMillis(1000)).filter(throwable -> throwable instanceof TimeoutException)) .doFinally(response -> log.info(new StringMapMessage() .with("info", "completed ") .with("responseTime", Duration.between(start, Instant.now()).toMillis())));
当前JUnit测试代码
@Test public void someMethod_Success() { Mono<String> strMono = Mono.just("Single Item Only"); when(webClient.post()).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodySpec); when(requestBodySpec.headers(any())).thenReturn(requestBodySpec); when(requestBodySpec.bodyValue(any())).thenReturn(requestHeadersSpec); ClientResponse clientResponse = ClientResponse .create(HttpStatus.OK) .header("Content-Type", "application/json"). body(strMono.toString()).build(); doAnswer(new Answer() { public Object answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); ((Function<ClientResponse, ? extends Mono>) args[0]).apply(clientResponse); return Mono.just(strMono); } }).when(requestHeadersSpec).exchangeToMono(any(Function.class)); RequestObject requestObject = RequestObject .builder().build(); class.SomeMethod(requestObject ); }
请问如何修改测试代码,让doFinally块被执行,从而覆盖这部分代码?
解决方案
要触发doFinally块执行,核心是激活Reactor冷流的订阅逻辑(Mono只有被订阅时才会执行整条流水线),同时修正Mock逻辑确保流能正常完成。具体修改步骤如下:
订阅返回的Mono流
原测试仅调用方法但未订阅返回的Mono,导致整条流(包括doFinally)都不会执行。测试环境中可使用block()同步订阅,或subscribe()异步订阅:// 调用方法并订阅流,触发完整执行链路 class.SomeMethod(requestObject).block();修正
exchangeToMono的Mock逻辑
原Mock中返回的Mono.just(strMono)与实际逻辑不符,应返回传入的响应处理函数执行后的结果;同时构建ClientResponse时需传入正确的Mono类型body:// 修正ClientResponse的body参数 ClientResponse clientResponse = ClientResponse .create(HttpStatus.OK) .header("Content-Type", "application/json") .body(strMono) // 直接传入Mono<String>而非字符串 .build(); // 修正exchangeToMono的Mock逻辑 doAnswer(invocation -> { Function<ClientResponse, ? extends Mono<?>> responseHandler = invocation.getArgument(0); return responseHandler.apply(clientResponse); }).when(requestHeadersSpec).exchangeToMono(any(Function.class));
修改后的完整测试代码
@Test public void someMethod_Success() { Mono<String> strMono = Mono.just("Single Item Only"); when(webClient.post()).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodySpec); when(requestBodySpec.headers(any())).thenReturn(requestBodySpec); when(requestBodySpec.bodyValue(any())).thenReturn(requestHeadersSpec); // 构建正确的ClientResponse ClientResponse clientResponse = ClientResponse .create(HttpStatus.OK) .header("Content-Type", "application/json") .body(strMono) .build(); // 修正exchangeToMono的Mock行为 doAnswer(invocation -> { Function<ClientResponse, ? extends Mono<?>> responseHandler = invocation.getArgument(0); return responseHandler.apply(clientResponse); }).when(requestHeadersSpec).exchangeToMono(any(Function.class)); RequestObject requestObject = RequestObject.builder().build(); // 订阅流触发完整执行 class.SomeMethod(requestObject).block(); }
内容的提问来源于stack exchange,提问作者Sunny Gupta
相关产品推荐
相关产品推荐

