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

Polly重试策略无法捕获NSubstitute模拟抛出的HttpRequestException异常

问题分析

你的Polly重试策略在_tokenInfrastructure.GetTokenAsync()抛出HttpRequestException时未触发重试,但在_timezoneInfrastructure.ProcessRequest()返回非空字符串时正常工作,说明策略的重试逻辑本身是有效的,问题出在异常捕获环节。

可能的原因及解决方案

1. 确认策略类型匹配

确保_retryPolicy被明确声明为AsyncPolicy<string>类型,因为它处理的是返回string的异步操作:

// 明确声明泛型策略类型
AsyncPolicy<string> _retryPolicy;

// 初始化策略
var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromMilliseconds(RetryDelay), RetryCount);
_retryPolicy = Policy
    .Handle<HttpRequestException>()
    .Or<CustomException>()
    .OrResult<string>(response => !string.IsNullOrEmpty(response))
    .WaitAndRetryAsync(delay);

若策略被错误声明为非泛型的AsyncPolicy,可能导致异步异常捕获失效。

2. 修正异常模拟方式

在测试中使用NSubstitute专门的异步异常模拟方法ThrowsAsync,替代手动创建Task.FromException,避免潜在的模拟逻辑问题:

// 替换原有的异常模拟代码
_tokenInfrastructure.GetTokenAsync().ThrowsAsync(new HttpRequestException());

3. 排查隐藏的异常捕获

检查RetryProcessRequest方法,确保没有try-catch块吃掉HttpRequestException,让异常能正常传递到Polly策略:

private async Task<string> RetryProcessRequest()
{
    // 无额外异常捕获逻辑,确保异常向上传递
    var token = await _tokenInfrastructure.GetTokenAsync();
    return await _timezoneInfrastructure.ProcessRequest(token);
}

4. 添加重试日志验证

在策略中添加OnRetryAsync回调,直接查看Polly是否触发了重试:

_retryPolicy = Policy
    .Handle<HttpRequestException>()
    .Or<CustomException>()
    .OrResult<string>(response => !string.IsNullOrEmpty(response))
    .WaitAndRetryAsync(
        delay,
        (exception, timeSpan, retryCount, context) =>
        {
            Console.WriteLine($"触发第{retryCount}次重试,原因:{exception?.Message ?? "非空返回结果"}");
            return Task.CompletedTask;
        });

运行测试时查看控制台输出,确认异常场景下是否有重试日志生成。

5. 升级Polly版本

你使用的Polly 7.2.3版本较旧,存在部分异步异常处理的已知问题。建议升级到7.x系列的最新稳定版本(如7.2.4及以上),修复潜在的框架bug。

调整后的测试代码
[Fact]
public async Task ProcessRequest_Throws()
{
    string errors = _fixture.Create<string>();
    var token = _fixture.Create<string>();
    var expectedReceivedCalls = 4;

    // 使用ThrowsAsync模拟异步异常
    _tokenInfrastructure.GetTokenAsync().ThrowsAsync(new HttpRequestException());
    _timezoneInfrastructure.ProcessRequest(token).Returns(errors);

    await _timezoneOrchestration.Awaiting(o => o.ProcessRequest()).Should()
        .ThrowAsync<HttpRequestException>();

    await _tokenInfrastructure.Received(expectedReceivedCalls).GetTokenAsync();
    await _timezoneInfrastructure.Received(expectedReceivedCalls).ProcessRequest(Arg.Any<string>());
}

内容的提问来源于stack exchange,提问作者Maruf

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 03:55:32