You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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逻辑确保流能正常完成。具体修改步骤如下:

  1. 订阅返回的Mono流
    原测试仅调用方法但未订阅返回的Mono,导致整条流(包括doFinally)都不会执行。测试环境中可使用block()同步订阅,或subscribe()异步订阅:

    // 调用方法并订阅流,触发完整执行链路
    class.SomeMethod(requestObject).block();
    
  2. 修正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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 19:23:28