如何验证ExchangeFilterFunction中的重试已耗尽?
问题:如何程序化验证WebClient重试过滤器的重试次数?
我实现了一个作为WebClient全局重试机制的ExchangeFilterFunction,过滤器代码如下:
@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; } ).flatMap(response -> clientResponse.createException()) .flatMap(Mono::error) .thenReturn(clientResponse) ).retry(retryFilterConfiguration.getRetryCount()); }
我编写了单元测试来验证重试已耗尽,测试能确认错误发生,但无法通过代码验证重试次数,仅能通过日志观察重试行为,测试代码如下:
@DisplayName("Retry on Default Error Code") @Test public void defaultRetryableErrorCode(){ //ARRANGE int retryCount = 2; int tooManyRequestsErrorCode = HttpStatus.TOO_MANY_REQUESTS.value(); ClientRequest mockRequest = mock(ClientRequest.class); ExchangeFunction mockNext = mock(ExchangeFunction.class); ClientResponse mockResponse = mock(ClientResponse.class); when(mockNext.exchange(mockRequest)).thenReturn(Mono.just(mockResponse)); when(mockResponse.statusCode()).thenReturn(HttpStatusCode.valueOf(tooManyRequestsErrorCode)) .thenReturn(HttpStatusCode.valueOf(tooManyRequestsErrorCode)) .thenReturn(HttpStatusCode.valueOf(tooManyRequestsErrorCode)); when(retryFilterConfiguration.getRetryCount()).thenReturn(retryCount); //ACT Mono<ClientResponse> clientResponseMono = retryStrategyFilter.filter(mockRequest,mockNext); //ASSERT StepVerifier.create(clientResponseMono) .verifyError(); }
请问如何通过程序化方式验证重试已耗尽?
解决方案
可以通过以下几种直接的方式程序化验证重试次数:
1. 用Mockito验证方法调用次数
ExchangeFunction.exchange()是每次重试都会触发的方法,直接验证它的调用次数即可。修改测试的断言部分:
// 原有的错误验证 StepVerifier.create(clientResponseMono) .verifyError(); // 验证exchange方法总调用次数:初始请求1次 + 重试次数retryCount verify(mockNext, times(retryCount + 1)).exchange(mockRequest);
注意:retry(n)会在失败后重试n次,所以总调用次数是1 + n,和你之前mock的3次错误返回完全匹配。
2. 自定义计数器追踪调用次数
如果不想依赖Mockito,可自己实现带计数功能的ExchangeFunction:
// 在ARRANGE阶段创建计数器和自定义ExchangeFunction AtomicInteger callCount = new AtomicInteger(0); ExchangeFunction countingExchangeFunction = request -> { callCount.incrementAndGet(); return Mono.just(mockResponse); }; // 替换原有的mockNext逻辑,无需再mock exchange方法 // when(mockNext.exchange(mockRequest)).thenReturn(Mono.just(mockResponse)); // ACT阶段使用自定义的ExchangeFunction Mono<ClientResponse> clientResponseMono = retryStrategyFilter.filter(mockRequest, countingExchangeFunction); // ASSERT阶段验证计数器值 StepVerifier.create(clientResponseMono) .verifyError(); assertEquals(retryCount + 1, callCount.get());
注意事项
- 确保mock的错误返回次数和总调用次数一致,否则会抛出
NoSuchElementException; - 如果重试逻辑包含延迟(比如用
retryWhen配合退避策略),需要在StepVerifier中添加thenAwait()等待延迟完成。
内容的提问来源于stack exchange,提问作者Juan Bustamante
相关产品推荐
相关产品推荐

