为什么 .NetCore HttpClient 在我的单元测试中的第二次重试中被处理?

Adv*_*emy 0 .net c# xunit nsubstitute

我想在我的 UT 中测试我的 httpclient post 重试功能,这里我模拟了 HttpFactory:

第一次和第二次,HttpFactory 总是返回 HttpStatusCode.InternalServerError

public class MyServiceClient
{
    private readonly IHttpClientFactory _clientFactory;

    public MyServiceClient(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public async Task<string> GetResponse(string test= "te")
    {
        using var client = _clientFactory.CreateClient("MyClient");
        var content = new StringContent("{}", Encoding.UTF8, "application/json");
        var response = await client.PostAsync("http://www.contoso.com/",content);
        if (!response.IsSuccessStatusCode)
        {
            throw new ApplicationException("Application Error!");
        }
        var result = await response.Content.ReadAsStringAsync();
        return result;
    }

    public async Task<string> PollyExecute()
    {
        try
        {
            var policy = Policy
                .Handle<Exception>()
                .WaitAndRetryAsync(3,
                    count => TimeSpan.FromSeconds(2),
                    (ex, timeSpan,retrycount, context) =>
                    {
                        Console.WriteLine(ex);
                    });

            var response = await policy.ExecuteAsync(()=>GetResponse());

            return response;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw ;
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

然后我使用我的策略来运行客户端 postasync 方法,我第一次重试没有问题,我得到了例外的 500 内部服务器错误。

public class HttpClientTest
{
    [Fact]
    public async Task PoliceTest()
    {
        var messageHandler = new StubHttpMessageHandler(HttpStatusCode.InternalServerError, "Error!!!!");

        var httpClient = new HttpClient(messageHandler)
        {
            BaseAddress = new Uri("http://mockuri")
        };

        var factory = Substitute.For<IHttpClientFactory>();
        factory.CreateClient(Arg.Any<string>()).Returns(httpClient, httpClient);

        var client = new MyServiceClient(factory);

        var result = await client.PollyExecute();

    }
}

public sealed class StubHttpMessageHandler : HttpMessageHandler
{
    public string _response;
    public HttpStatusCode _statusCode;

    public StubHttpMessageHandler(HttpStatusCode statusCode, string response)
    {
        _statusCode = statusCode;
        _response = response;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        return Task.FromResult(Send(request));
    }

    private HttpResponseMessage Send(HttpRequestMessage request)
    {
        return new HttpResponseMessage
        {
            Content = new StringContent(_response),
            StatusCode = _statusCode,
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,在第二次重试中,运行 postasync 方法时,

它抛出一个异常,说 httpclient 已被处理。 在此处输入图片说明 在此处输入图片说明

为什么?有朋友知道原因吗?提前致谢!

Pet*_*etr 6

我认为您只是IHttpClientFactory在测试设置中错误地嘲笑。根据设计,工厂返回新客户端的默认实现(Microsoft docs):

每次调用 CreateClient(String) 都保证返回一个新的 HttpClient 实例。调用者可以无限期地缓存返回的 HttpClient 实例或将其使用包含在 using 块中以在需要时处置它。

在示例中,您提供的工厂返回与httpClient第一次使用后处理的相同。模拟工厂一次又一次地返回相同的已处理对象,从而导致已处理错误。只需修改您的示例以返回客户端的不同实例(请原谅重复的代码)即可避免已处理的错误:

...
        var httpClient = new HttpClient(messageHandler)
        {
            BaseAddress = new Uri("http://mockuri")
        };

        var httpClient1 = new HttpClient(messageHandler)
        {
            BaseAddress = new Uri("http://mockuri")
        };

        var httpClient2 = new HttpClient(messageHandler)
        {
            BaseAddress = new Uri("http://mockuri")
        };

        var httpClient3 = new HttpClient(messageHandler)
        {
            BaseAddress = new Uri("http://mockuri")
        };

        var factory = Substitute.For<IHttpClientFactory>();
    
        factory
            .CreateClient(Arg.Any<string>())
            .Returns(httpClient, httpClient1, httpClient2, httpClient3);
...
Run Code Online (Sandbox Code Playgroud)

  • 当对象被释放时,它不会自动变为 `null`,它只是释放底层资源(或者它假设释放它们,因为它取决于实现者。 (3认同)