Spring Boot 如何对ExchangeFilterFunction进行单元测试?

mspsb9vt  于 2023-05-22  发布在  Spring
关注(0)|答案(1)|浏览(190)

我正在尝试创建一个全局Web客户端重试过滤器,并尝试通过Mockito对其进行单元测试。筛选器是ExchangeFilterFunction的实现。我使用的技术提出了here
我使用的是Sping Boot 3.0.6版本和Java temurin 17。

RetryStrategyFilter

@Override
    public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
        return next.exchange(request)
                .flatMap(clientResponse -> Mono.just(clientResponse)
                        .filter(response -> {
                            final HttpStatusCode code = clientResponse.statusCode();
                            boolean isRetryable = Boolean.FALSE;
                            if (code.isError()) {
                                //check if its a retryable error
                                isRetryable = Arrays.stream(DEFAULT_RETRYABLE_ERROR_CODES).anyMatch(defaultCode -> defaultCode == code.value()) ||
                                                (retryFilterConfiguration.getRetryErrorCodes() != null &&
                                                    retryFilterConfiguration.getRetryErrorCodes().stream().anyMatch(retryErrorCode -> retryErrorCode == code.value()));
                                LOGGER.warn("Request Failed.  Retrying -> url={}; status={}", request.url(), code.value());
                            }
                            return isRetryable;
                        }) // if no errors, filter it out
                        .flatMap(response -> clientResponse.createException()) // let's raise an exception if response was an error
                        .flatMap(Mono::error) // trigger onError signal
                        .thenReturn(clientResponse)
                )
                .retry(retryFilterConfiguration.getRetryCount());
    }

RetryStrategyFilterTest

@DisplayName("Retry on Default Error Code")
    @Test
    public void defaultRetryableErrorCode(){
        //ARRANGE
        ClientRequest mockRequest = Mockito.mock(ClientRequest.class);
        ExchangeFunction mockNext = Mockito.mock(ExchangeFunction.class);
        ClientResponse mockResponse = Mockito.mock(ClientResponse.class, RETURNS_DEEP_STUBS);
        int tooManyRequestsErrorCode = HttpStatus.TOO_MANY_REQUESTS.value();
        when(mockNext.exchange(mockRequest)).thenReturn(Mono.just(mockResponse));
        when(mockResponse.statusCode()).thenReturn(HttpStatusCode.valueOf(tooManyRequestsErrorCode));
        //ACT
        retryStrategyFilter.filter(mockRequest,mockNext);
        //ASSERT
        verify(mockResponse,times(0)).createException();
        verify(retryFilterConfiguration,times(1)).getRetryCount();
    }

当使用默认的可重试错误代码(429)对代码进行单元测试时,我希望它调用retry,但我得到了以下存根错误。

Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at org.mycomp.http.filter.RetryStrategyFilterTest.defaultRetryableErrorCode(RetryStrategyFilterTest.java:37)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
org.mockito.exceptions.misusing.UnnecessaryStubbingException:

这意味着Mono.just(clientResponse)filter函数没有被调用,当我调试它时,我注意到我无法单步执行它。我跟踪的一个线索是,我在测试中有这一行when(mockNext.exchange(mockRequest)).thenReturn(Mono.just(mockResponse));,在过滤器函数.flatMap(clientResponse -> Mono.just(clientResponse)中有这一行。我在这里筑巢吗?这可能是它不起作用的原因吗?

w8rqjzmb

w8rqjzmb1#

可以使用@RunWith(MockitoJUnitRunner.Silent.class)。这个Mockito JUnit Runner实现 * 忽略 * stubbing参数不匹配(MockitoJUnitRunner.StrictStubs)并且 * 不检测 * 未使用的stubbing。
”””但这不是一个解决方案!**
最好是修改你的测试。检测到不必要的存根-意味着您使用了不必要的when()存根,因此您需要删除它或使用lenient()来停止异常。

Mockito.lenient().when(mockResponse.statusCode()).thenReturn(HttpStatusCode.valueOf(tooManyRequestsErrorCode));

顺便说一下,你的代码看起来很奇怪。也许我们需要重构一些?在你的代码中,flatMap接受Mono和Publisher类型,所以我们可以很容易地返回mock response而不是Mono.just(mockResponse)。删除嵌套monos:

@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
    return next.exchange(request)
            .flatMap(clientResponse -> {
                final HttpStatusCode code = clientResponse.statusCode();
                boolean isRetryable = Boolean.FALSE;
                if (code.isError()) {
                  
                    isRetryable = Arrays.stream(DEFAULT_RETRYABLE_ERROR_CODES).anyMatch(defaultCode -> defaultCode == code.value()) ||
                            (retryFilterConfiguration.getRetryErrorCodes() != null &&
                                    retryFilterConfiguration.getRetryErrorCodes().stream().anyMatch(retryErrorCode -> retryErrorCode == code.value()));
                    LOGGER.warn("Request Failed.  Retrying -> url={}; status={}", request.url(), code.value());
                }
                if (isRetryable) {
                    return clientResponse.createException()
                            .flatMap(Mono::error);
                } else {
                    return Mono.just(clientResponse);
                }
            })
            .retry(retryFilterConfiguration.getRetryCount());
}

通过简化这些代码,所有的都应该工作。

相关问题