重试 Polly 单元测试(xUnit 和 Moq)

3
我有一个 .net core 的 Web API(见下面的代码)。我正在使用 Polly 重试策略(见下面的策略)。我想对端点(getProducts)进行单元测试并测试 Polly 重试。
我找到了这些示例,但不清楚如何对端点和重试策略进行单元测试?
services
  .AddHttpClient<IProductService, ProductService>()
  .AddPolicyHandler(GetRetryPolicy(3, 2)); 

static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy(int retryCount, int breakDuration)
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
        .WaitAndRetryAsync(retryCount, retryAttempt => TimeSpan.FromSeconds(Math.Pow(breakDuration,
            retryAttempt)));
}

.Net Core API:
public interface IProductService
{
    Task<IEnumerable<ProductResponse>> GetProducts(string productType);
}

public class ProductService: IProductService
{ 
    private readonly HttpClient _httpClient;
    
    public ProductService(HttpClient httpClient)
    {
         _httpClient = httpClient;
    }
    
    public async Task<IEnumerable<ProductResponse>> GetProducts(string productType)
    {
         var response = await _httpClient.GetAsync("uri");
         ...
    }
}

你在控制器中哪里使用了 productService? - Maruf
你能用 Given When Then 结构描述一下你的测试用例吗?因为 单元测试端点和重试策略 并不能说明你的测试意图。 - Peter Csala
给定=调用GetProducts时,如果返回错误5XX错误,则重试3次 - itaustralia
绕过方法是在启动期间向 DI 容器中添加一个策略实例,并将其注入到您的服务中。然后,您可以使用 retryPolicy.Policy.ExecuteAsync(async () => await client.GetAsync("uri")) 来执行操作。您可以模拟 HTTP 端以按顺序响应 HTTP 状态码,并捕获请求正文以测试它发送的次数是否符合预期。 - mishal153
1个回答

3
悲哀的事实是,你无法真正对重试+端点逻辑进行单元测试,以下是我的理由:
  1. 通过 DI(AddPolicyHandler)将重试注册在 HttpClient 的顶部。当您进行单元测试时,您不是依赖于 DI 而是依赖于各个组件。
    1.1 因此,集成测试可能更合适。我已经详细说明了如何通过 WireMock.Net 进行测试:1, 2。基本思路是创建一个本地 http 服务器(用于模拟下游系统),并预定义响应序列。
  2. 在定义了最大重试次数和时间惩罚的重试策略之后,您无法轻松地检索它们。因此,从单元测试的角度来看,确保策略已正确定义(例如,延迟以“秒”而非“分钟”为单位)非常困难。我已经为此创建了一个 github 问题,但不幸的是,V8 的开发陷入了僵局。

回到你的测试用例。使用给定-当-那么结构表达测试场景的正确方式应该写成:
  • 假设一个有故障的下游服务,它返回5XX响应
  • 我调用GetProducts时
  • 那么它会执行4次(1次初始尝试+ 3次重试尝试)
这不是一个单元测试,更像是组件/集成测试。为什么?因为即使你可以创建一个HttpClient mock,但在这种情况下,就没有重试策略了。
有一种解决方法:你可以通过PolicyHttpMessageHandler手动装饰底层处理程序来实现策略。但这在单元测试中是不好的想法,因为你基本上在测试安排代码,而不是你的生产代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接