测试 Polly 重试通过 Startup.ConfigureServices() 使用 ASP.NET Core API 配置的 Polly

Test Polly retry polly configured via Startup.ConfigureServices() with ASP.NET Core API

我想了解如何测试通过 Startup.ConfigureServices() 配置的 Polly 重试 polly。

配置服务

里面配置了Polly策略

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
                 services.AddHttpClient<IHttpClientService, HttpClientService>()
                .SetWaitAndRetryPolicy1();     

     }
}

以下是 Polly 政策:

 public static class IServiceCollectionExtension
    {

    public static void SetWaitAndRetryPolicy1(this IHttpClientBuilder clientBuilder)
        {
                             clientBuilder.AddPolicyHandler((service, request) =>
                            HttpPolicyExtensions.HandleTransientHttpError()
                                .WaitAndRetryAsync(3,
                                    retryCount => TimeSpan.FromSeconds(Math.Pow(2, retryCount)),
                onRetry: (outcome, timespan, retryCount, context) =>
                                {
                                    service.GetService<ILog>().Error("Delaying for {delay}ms, then making retry {retry}.",
                                        timespan.TotalMilliseconds, retryCount);
                                }
                            )

                        );        
        }
    }

下面是我试过的:

集成测试

测试中配置了Polly策略。

  public class RetryPolicyTests : IClassFixture<WebApplicationFactory<Startup>>
    {
        private readonly WebApplicationFactory<Startup> _factory;

        public RetryPolicyTests(WebApplicationFactory<Startup> factory)
        {
            _factory = factory;
        }

        [Theory]
        [InlineData("http://localhost:1234/api/v1/car/")]
        public async Task Test3(string url)
        {
            // Arrange
            var client = _factory.WithWebHostBuilder(whb =>
                {
                    whb.ConfigureServices((bc, sc) =>
                    {
                        sc.AddOptions();
                        sc.AddHttpClient("test")
                          .SetWaitAndRetryPolicy1();         //Test the Polly policy             
                        sc.BuildServiceProvider();
                    });
                })
                .CreateClient();  //cannot get a named or typed HttpClient

            // Act           
            var body = "{}";
            using (var content = new StringContent(body, Encoding.UTF8, "application/json"))
            {

                var response = await client.PostAsync(url, content);



            }

            //Assert: somewhy assert it
        }
    }
}

问题

我无法检索已使用 Polly polly 配置的 HttpClient。因为 WebApplicationFactory.CreateClient() 没有 returns 命名或类型 HttpClient:

的重载

有什么想法吗?

有没有更好的测试方法?

ASPS.NET核心API2.2

要最少修改您发布的代码以获得在 HttpClientFactory 上配置的命名或类型 HttpClient,构建 IServiceProvider,获取 IHttpClientFactory,然后从 [= 获取配置的客户端14=]。

var configuredClient = sc.BuildServiceProvider()
    .GetRequiredService<IHttpClientFactory>()
    .CreateClient("test");

许多人认为像这样使用 IServiceProvider 是生产代码中的服务定位器反模式;也许在测试中可以将要单元测试的特定项目从默认应用程序配置中拉出。但是,还有更短的测试方法来获取在 HttpClientFactory 上配置的示例 HttpClient,而不使用完整的 WebApplicationFactory(请参阅答案的最后一部分)。


对于完整的端到端集成测试,测试您的应用如何使用配置的策略,使用 WebApplicationFactory 来测试您应用的某些端点,例如 http://localhost:1234/api/v1/car/:

您可以 - 在集成测试中 - 使用像 Mountebank for .NET or HttpClientInterception 这样的工具来消除配置的 HttpClient 进行的调用,以便那些调用 return 您期望的错误处理的政策。

您可以使用 WebHostBuilder.ConfigureServices(...) 的功能来修改您的应用程序的正常启动,以便轻松断言一些东西来证明策略已被调用。例如,您可以配置 mock/fake ILog 实现,并断言 onRetry 委托中的 ILog.Error(...) 调用发生。


对于最短的自包含单元测试来测试在 HttpClientFactory 上的给定 HttpClient 配置上配置的 Polly 策略,您可以使用如下代码模式。这仅使用 IHttpClientFactory 和标准的 Microsoft DI 基础结构; ASP.NET.

没有虚拟主机
public class HttpClientFactory_Polly_Policy_Test
{
    [Fact]
    public async Task Given_a_retry_policy_configured_on_a_named_client_When_call_via_the_named_client_Then_the_policy_is_used()
    {
        // Given / Arrange 
        IServiceCollection services = new ServiceCollection();

        bool retryCalled = false;

        HttpStatusCode codeHandledByPolicy = HttpStatusCode.InternalServerError;

        const string TestClient = "TestClient";
        services.AddHttpClient(TestClient)
            .AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
                .RetryAsync(3, onRetry: (_, __) => retryCalled = true))
            .AddHttpMessageHandler(() => new StubDelegatingHandler(codeHandledByPolicy));

        HttpClient configuredClient =
            services
                .BuildServiceProvider()
                .GetRequiredService<IHttpClientFactory>()
                .CreateClient(TestClient);

        // When / Act
        var result = await configuredClient.GetAsync("https://www.doesnotmatterwhatthisis.com/");

        // Then / Assert
        Assert.Equal(codeHandledByPolicy, result.StatusCode);
        Assert.True(retryCalled);
    }

}

public class StubDelegatingHandler : DelegatingHandler
{
    private readonly HttpStatusCode stubHttpStatusCode;
    public StubDelegatingHandler(HttpStatusCode stubHttpStatusCode) => this.stubHttpStatusCode = stubHttpStatusCode;
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => Task.FromResult(new HttpResponseMessage(stubHttpStatusCode));
}

如果将策略声明提取到方法中(如您发布的代码中的 SetWaitAndRetryPolicy1()),上述方法提供了一种更注重单元测试的方法来测试它们。