Mockito:WebClient响应式方法调用

z9ju0rcb  于 9个月前  发布在  其他
关注(0)|答案(1)|浏览(128)

我在为我的collect方法编写单元测试时遇到了困难。具体地说,在collectEndpointCount方法中调用getBearerToken()之后发生的方法的Jacoco测试覆盖率没有得到识别。StepVerifier能够验证它是否已完成,但其中的值都为null。
下面是我的collect的快速分解。它接受一个timeRange,并使用collectEndpointCount方法为每个端点启动2个异步操作。此方法使用extractAccessTokengetBearerToken检索Apigee访问令牌以验证API请求
使用构造的URL和获得的访问令牌从每个端点的外部API获取使用计数数据。然后通过processEndpointResponse方法处理响应以提取使用计数。使用www.example.com操作合并所有2个终点的收集计数Mono.zip。

ResultsCollectorImpl.java

public ResultCollectorImpl(WebClient webClient) {
    this.webClient = webClient;
}

@Override
public Mono<ResultStats> collect(String timeRange) {
    Mono<Long> results1 = collectEndpointCount(timeRange, "url1");
    Mono<Long> results2 = collectEndpointCount(timeRange, "url2");

    return Mono.zip(results1, results2)
        .map(tuple -> new ResultStats(tuple.getT1(), tuple.getT2()));
}

private Mono<String> getBearerToken() {
    return webClient.post()
        .uri("https://login.apigee.com/oauth/token")
        .header(HttpHeaders.AUTHORIZATION, "Basic ZWRnZWNsaTplZGdlY2xpc2VjcmV0")
        .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
        .bodyValue("grant_type=refresh_token&refresh_token=my_refresh_token")
        .retrieve()
        .bodyToMono(String.class)
        .flatMap(this::extractAccessToken);
}

private Mono<String> extractAccessToken(String responseBody) {
    try {
        JsonNode accessTokenNode = new ObjectMapper().readTree(responseBody).get("access_token");
        if (accessTokenNode != null && accessTokenNode.isTextual()) {
            return Mono.just(accessTokenNode.asText());
        }
        return Mono.empty();
    } catch (IOException e) {
        return Mono.error(e);
    }
}

private Mono<Long> collectEndpointCount(String timeRange, String url) {
    return getBearerToken()
        .flatMap(bearerToken -> webClient.get()
            .uri(url)
            .header(HttpHeaders.AUTHORIZATION, "Bearer " + bearerToken)
            .retrieve()
            .bodyToMono(String.class)
            .flatMap(responseBody -> Mono.fromCallable(() -> processEndpointResponse(responseBody))))
        .onErrorMap(ex -> ex instanceof WebClientResponseException || ex instanceof IOException,
            BillProcessorExceptionCollect::new);
}

private long processEndpointResponse(@NonNull String responseBody) throws IOException {
    try (JsonParser jsonParser = new JsonFactory().createParser(responseBody)) {
        while (jsonParser.nextToken() != null) {
            if (jsonParser.getCurrentToken() == JsonToken.FIELD_NAME
                && "values".equals(jsonParser.getCurrentName())) {
                jsonParser.nextToken();
                while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
                    if (jsonParser.getCurrentToken() == JsonToken.VALUE_STRING) {
                        return (long) Double.parseDouble(jsonParser.getText());
                    }
                }
            }
        }
    }
    return 0;
}

ResultsCollectorImplTest.java

@Mock
private WebClient webClient;
@Mock
private ApigeeConfig apigeeConfig;
@InjectMocks
private ResultStatsCollectorImpl resultStatsCollector;
@Mock
private RequestBodyUriSpec requestBodyUriSpec;
@Mock
private RequestHeadersUriSpec requestHeadersUriSpec;
@Mock
private RequestHeadersSpec requestHeadersSpec;
@Mock
private ResponseSpec responseSpec;

@BeforeEach
void setUp() {
    MockitoAnnotations.openMocks(this);
    ResultStatsCollector = new ResultStatsCollectorImpl(webClient, apigeeConfig);
}

@Test
void testCollectWithValidData() {
    String timeRange = "08/01/2023 00:00~08/31/2023 23:59";

    Mono<String> accessToken = Mono.just("{\"access_token\":\"bearer_token\"");
    when(apigeeConfig.getRefreshToken()).thenReturn("mockRefreshToken");
    when(webClient.post()).thenReturn(requestBodyUriSpec);
    when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodyUriSpec);
    when(requestBodyUriSpec.header(anyString(), anyString())).thenReturn(requestBodyUriSpec);
    when(requestBodyUriSpec.bodyValue(anyString())).thenReturn(requestHeadersSpec);
    when(requestBodyUriSpec.retrieve()).thenReturn(responseSpec);
    when(responseSpec.bodyToMono(String.class)).thenReturn(accessToken);
    
    String mockApiResponse = "{\"from\":\"2023-08-01T00:00:00+08:00\",\" count1\":100,\"count2\":100}";
    when(webClient.get()).thenReturn(requestHeadersUriSpec);
    when(requestHeadersUriSpec.uri(anyString())).thenReturn(requestHeadersUriSpec);
    when(requestHeadersUriSpec.header(HttpHeaders.AUTHORIZATION, "mockAccessToken")).thenReturn(requestHeadersSpec);
    when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
    when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(mockApiResponse));

    Mono<ResultStats> usageStats = resultStatsCollector.collect(timeRange);

    StepVerifier.create(usageStats).verifyComplete();
}

soat7uwm

soat7uwm1#

当我的方法调用webClient时,每个方法都生成了不同的responseSpec。添加另一个mock ResponseSpec就可以了。

@Mock
private ResponseSpec responseSpecStats;

...
when(requestHeadersUriSpec.retrieve()).thenReturn(responseSpecStats);
when(responseSpecStats.bodyToMono(String.class)).thenReturn((endpointResponse));

相关问题