如何测试.NET项目启动时注册的Polly重试策略?
问题根因
- Polly 针对 HttpClient 注册的策略,仅作用于
HttpClient内置的请求管道(也就是HttpClient.SendAsync方法的执行链路)。你自定义的MyHttpClient/MyFakeClient直接在封装的Post方法内抛出异常,完全绕开了 Polly 策略的作用范围,策略无法感知到异常,自然不会触发重试。 - 你当前的重试策略仅配置了
HandleTransientHttpError(),这个方法默认只处理三类场景:HttpRequestException类型异常、响应状态码为 5xx、响应状态码为 408(超时),你自定义抛出的HttpResponseException不在默认捕获范围内,就算走到策略层也不会触发重试。
调整方案
方案一:单元测试轻量验证(推荐用于策略逻辑单独验证)
直接替换自定义假客户端的实现,改用 DelegatingHandler 模拟接口异常/错误返回,这是官方推荐的 HttpClient 测试方案:
- 先实现自定义的存根消息处理器,记录调用次数、模拟返回结果:
public class StubHttpMessageHandler : DelegatingHandler { public int CallCount { get; private set; } private readonly HttpStatusCode _returnStatusCode; public StubHttpMessageHandler(HttpStatusCode returnStatusCode) { _returnStatusCode = returnStatusCode; } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { CallCount++; // 模拟错误响应,也可以根据需求抛出HttpRequestException return Task.FromResult(new HttpResponseMessage(_returnStatusCode) { RequestMessage = request }); } }
- 调整服务注册和测试逻辑:
public async Task MyTest() { const int retryCount = 3; // 替换为你实际配置的重试次数 var policy = GetRetryPolicy(); // 取你实际的重试策略实例 var stubHandler = new StubHttpMessageHandler(HttpStatusCode.InternalServerError); var services = new ServiceCollection(); services.AddHttpClient<IHttpClient, MyHttpClient>() .AddPolicyHandler(policy) .AddHttpMessageHandler(() => stubHandler); // 注入存根处理器替换真实请求逻辑 var serviceProvider = services.BuildServiceProvider(); var client = serviceProvider.GetRequiredService<IHttpClient>(); // 执行请求 _ = await client.Post(new Uri("https://someurl.com"), new StringContent("test")); // 断言:总调用次数 = 首次请求 + 重试次数 Assert.Equal(retryCount + 1, stubHandler.CallCount); }
- 如果业务逻辑确实需要处理自定义的
HttpResponseException,可以在重试策略里新增异常捕获规则:
public IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() { return HttpPolicyExtensions .HandleTransientHttpError() // 新增捕获自定义的HttpResponseException .Or<HttpResponseException>() .OrResult(message => message.StatusCode == HttpStatusCode.NotFound) .WaitAndRetryAsync(GetBackOffDelay(options), onRetry: (result, timespan, retryAttempt, context) => { var statusCode = (result.Exception as HttpResponseException)?.StatusCode ?? result.Result?.StatusCode; context.GetLogger()?.LogWarning($"Failure with status code {statusCode}. Retry attempt {retryAttempt}. Retrying in {timespan}."); }); }
方案二:集成测试验证(就是你补充提到的方案)
通过 IHostBuilder 构建完整的服务栈测试,逻辑和单元测试方案类似,只是更贴近真实运行环境:
- 注册存根
DelegatingHandler替换真实的请求发送逻辑 - 在处理器内记录调用次数,或者在响应头写入重试计数做断言
- 3次重试配置下总调用次数为4是正确逻辑,符合预期
内容的提问来源于stack exchange,提问作者sr28
相关产品推荐
相关产品推荐

