使用 Polly 对 HttpClient 进行单元测试

大卫·莫利纽克斯

我希望对HttpClient具有 a 的 a进行单元测试Polly RetryPolicy,我正在努力研究如何控制HTTP响应。

HttpMessageHandler在客户端上使用了 a ,然后覆盖了发送异步,这很好用,但是当我添加 Polly 重试策略时,我必须使用IServiceCollection和无法HttpMessageHandler为客户端创建一个HTTP 客户端实例我曾尝试使用 ,.AddHttpMessageHandler()但这会阻止轮询重试策略并且它只触发一次。

这就是我在测试中设置 HTTP 客户端的方式

IServiceCollection services = new ServiceCollection();

const string TestClient = "TestClient";
 
services.AddHttpClient(name: TestClient)
         .AddHttpMessageHandler()
         .SetHandlerLifetime(TimeSpan.FromMinutes(5))
         .AddPolicyHandler(KYA_GroupService.ProductMessage.ProductMessageHandler.GetRetryPolicy());

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

public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
            .HandleTransientHttpError()
            .WaitAndRetryAsync(6,
                retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetryAsync: OnRetryAsync);
}

private async static Task OnRetryAsync(DelegateResult<HttpResponseMessage> outcome, TimeSpan timespan, int retryCount, Context context)
{
    //Log result
}

这将在我调用时触发请求,_httpClient.SendAsync(httpRequestMessage)但它实际上创建了一个对地址的 Http 调用,我需要以某种方式拦截它并返回受控响应。

我想测试该策略是否用于在请求失败时重试请求并在完成响应时完成。

我的主要限制是我不能在 MSTest 上使用 Moq。

西蒙-皮尔森

您不希望将HttpClient真正的 HTTP 请求作为单元测试的一部分发出 - 那将是集成测试。为避免提出真正的请求,您需要提供自定义HttpMessageHandler. 您已在帖子中规定您不想使用模拟框架,因此HttpMessageHandler您可以提供一个stub而不是模拟

由于评论对 Polly 的 GitHub 页面上的一个问题产生了重大影响,我调整了您的示例以调用 stubbed HttpMessageHandler,它在第一次调用时抛出 500,然后在后续请求中返回 200。

该测试断言重试处理程序已被调用,并且当执行步骤越过HttpClient.SendAsync对结果响应的调用时,状态代码为 200:

public class HttpClient_Polly_Test
{
    const string TestClient = "TestClient";
    private bool _isRetryCalled;

    [Fact]
    public async Task Given_A_Retry_Policy_Has_Been_Registered_For_A_HttpClient_When_The_HttpRequest_Fails_Then_The_Request_Is_Retried()
    {
        // Arrange 
        IServiceCollection services = new ServiceCollection();
        _isRetryCalled = false;

        services.AddHttpClient(TestClient)
            .AddPolicyHandler(GetRetryPolicy())
            .AddHttpMessageHandler(() => new StubDelegatingHandler());

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

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

        // Assert
        Assert.True(_isRetryCalled);
        Assert.Equal(HttpStatusCode.OK, result.StatusCode);
    }

    public IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions.HandleTransientHttpError()
            .WaitAndRetryAsync(
                6,
                retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetryAsync: OnRetryAsync);
    }

    private async Task OnRetryAsync(DelegateResult<HttpResponseMessage> outcome, TimeSpan timespan, int retryCount, Context context)
    {
        //Log result
        _isRetryCalled = true;
    }
}

public class StubDelegatingHandler : DelegatingHandler
{
    private int _count = 0;

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (_count == 0)
        {
            _count++;
            return Task.FromResult(new HttpResponseMessage(HttpStatusCode.InternalServerError));
        }

        return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章