Проверка опроса повторных попыток Polly, настроенного с помощью Startup.ConfigureServices () с API ASP.NET Core

Я хочу узнать, как можно протестировать Polly retry polly, настроенный с помощью Startup.ConfigureServices ().

ConfigureServices

В нем настраивается политика 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
        }
    }
}

проблема в том, что

Я не могу получить HttpClient, который был настроен с помощью опроса Polly. Поскольку WebApplicationFactory.CreateClient() не имеет перегрузок, возвращающих именованный или типизированный HttpClient:

Любая идея?

Есть ли лучший способ проверить это?

ASPS.NET Core API 2.2


person Pingpong    schedule 03.01.2019    source источник


Ответы (1)


Чтобы минимально изменить опубликованный код для получения именованного или набранного HttpClient, настроенного на HttpClientFactory, создайте IServiceProvider, получите IHttpClientFactory, а затем получите настроенного клиента из IHttpClientFactory.

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

Многие люди считают использование такого IServiceProvider анти-паттерном локатора служб в производственном коде; возможно, здесь, в тесте, нормально вытащить конкретный элемент, который вы хотите протестировать, из конфигурации приложения по умолчанию. Однако есть и более короткие способы тестирования образца HttpClient, настроенного на HttpClientFactory, без использования полного WebApplicationFactory (см. Последнюю часть ответа).


Для полного теста сквозной интеграции, тестирования того, как ваше приложение использует настроенную политику, с использованием WebApplicationFactory для проверки некоторых конечных точек вашего приложения, например http://localhost:1234/api/v1/car/:

Вы можете - в рамках интеграционного теста - использовать такой инструмент, как Mountebank for .NET или HttpClientInterception, чтобы заглушить вызовы, которые делает настроенный HttpClient, чтобы эти вызовы возвращали ошибки, которые вы ожидаете от политики. .

Вы можете использовать возможность WebHostBuilder.ConfigureServices(...) для изменения нормального запуска вашего приложения, чтобы упростить утверждение чего-либо, чтобы доказать, что политика была вызвана. Например, вы можете настроить фиктивную / поддельную ILog реализацию и утверждать, что ILog.Error(...) вызов в вашем onRetry делегате имеет место.


Для кратчайшего возможного автономного модульного теста для проверки политики Polly, настроенной для данной HttpClient конфигурации на HttpClientFactory, вы можете использовать шаблон кода, как показано ниже. При этом используется только 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() в опубликованном вами коде), подход, подобный описанному выше, обеспечивает более ориентированный на модульный тест способ их тестирования.

person mountain traveller    schedule 04.01.2019
comment
Это сокращенная версия более длинного сообщения (в частности, более подробная информация о подходе сквозной интеграции), по адресу; github.com/App-vNext/Polly/issues/555 - person mountain traveller; 05.01.2019