C# 带有Polly的单元测试HttpClient
我想对一个C# 带有Polly的单元测试HttpClient,c#,unit-testing,httpclient,polly,C#,Unit Testing,Httpclient,Polly,我想对一个HttpClient进行单元测试,它有一个PollyRetryPolicy,我想知道如何控制HTTP响应 我在客户端上使用了一个HttpMessageHandler,然后覆盖了Send Async,这非常有效,但是当我添加Polly重试策略时,我必须使用IServiceCollection创建一个HTTP客户端实例,并且无法为客户端创建HttpMessageHandler。我尝试过使用.AddHttpMessageHandler(),但这会阻止轮询重试策略,并且只触发一次 这就是我在测
HttpClient
进行单元测试,它有一个Polly
RetryPolicy
,我想知道如何控制HTTP
响应
我在客户端上使用了一个HttpMessageHandler
,然后覆盖了Send Async,这非常有效,但是当我添加Polly重试策略时,我必须使用IServiceCollection
创建一个HTTP客户端实例,并且无法为客户端创建HttpMessageHandler
。我尝试过使用.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
}
IServiceCollection服务=newServiceCollection();
常量字符串TestClient=“TestClient”;
services.AddHttpClient(名称:TestClient)
.AddHttpMessageHandler()
.SetHandlerLifetime(时间跨度从分钟(5))
.AddPolicyHandler(KYA_GroupService.ProductMessage.ProductMessageHandler.GetRetryPolicy());
HttpClient配置的客户端=
服务
.BuildServiceProvider()
.GetRequiredService()
.CreateClient(TestClient);
公共静态IAsyncPolicy GetRetryPolicy()
{
返回HttpPolicyExtensions
.HandleTransientHttpError()
.WaitandertryAsync(6,
retrytry=>TimeSpan.FromSeconds(Math.Pow(2,retrytry)),
onRetryAsync:onRetryAsync);
}
私有异步静态任务OnRetryAsync(DelegateResult结果、TimeSpan TimeSpan、int retryCount、上下文)
{
//日志结果
}
然后,当我调用\u httpClient.sendaync(httpRequestMessage)
时,这将触发请求,但它实际上创建了一个对地址的Http调用,我需要截取该请求并返回一个受控响应
我想测试该策略是否用于在请求失败时重试请求,并在请求是完整响应时完成
我的主要限制是不能在MSTest上使用Moq。您不希望您的
HttpClient
作为单元测试的一部分发出真正的HTTP请求-这将是一个集成测试。为了避免发出真正的请求,您需要提供一个定制的HttpMessageHandler
。您已在帖子中规定不希望使用模拟框架,因此,您可以提供一个模拟框架,而不是模拟HttpMessageHandler
由于Polly的GitHub页面上对一个问题的评论影响很大,我调整了您的示例以调用stubbedHttpMessageHandler
,它在第一次调用时抛出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));
}
}
公共类HttpClient\u Polly\u测试
{
常量字符串TestClient=“TestClient”;
私人住宅被重新调用;
[事实]
给定的公共异步任务\u重试\u策略\u已注册\u HttpClient\u当\u HttpRequest\u失败时\u请求\u重试()
{
//安排
IServiceCollection服务=新的ServiceCollection();
_isRetryCalled=false;
services.AddHttpClient(TestClient)
.AddPolicyHandler(GetRetryPolicy())
.AddHttpMessageHandler(()=>new StubDelegatingHandler());
HttpClient配置的客户端=
服务
.BuildServiceProvider()
.GetRequiredService()
.CreateClient(TestClient);
//表演
var result=await configuredClient.GetAsync(“https://www.stackoverflow.com");
//断言
Assert.True(_isRetryCalled);
Assert.Equal(HttpStatusCode.OK,result.StatusCode);
}
公共IAsyncPolicy GetRetryPolicy()
{
返回HttpPolicyExtensions.HandletTransientHttpError()
.WaitAndRetryAsync(
6.
retrytry=>TimeSpan.FromSeconds(Math.Pow(2,retrytry)),
onRetryAsync:onRetryAsync);
}
私有异步任务OnRetryAsync(DelegateResult结果、TimeSpan TimeSpan、int retryCount、上下文)
{
//日志结果
_isRetryCalled=true;
}
}
公共类StubDelegatingHandler:DelegatingHandler
{
私有整数_计数=0;
受保护的覆盖任务SendAsync(HttpRequestMessage请求,
取消令牌(取消令牌)
{
如果(_count==0)
{
_计数++;
返回Task.FromResult(新的HttpResponseMessage(HttpStatusCode.InternalServerError));
}
返回Task.FromResult(新的HttpResponseMessage(HttpStatusCode.OK));
}
}
您不希望您的HttpClient
作为单元测试的一部分发出真正的HTTP请求,这将是一个集成测试。为了避免发出真正的请求,您需要提供一个定制的HttpMessageHandler
。您已在帖子中规定不希望使用模拟框架,因此,您可以提供一个模拟框架,而不是模拟HttpMessageHandler
由于Polly的GitHub页面上对一个问题的评论影响很大,我调整了您的示例以调用stubbedHttpMessageHandler
,它在第一次调用时抛出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));
}
}
公共类HttpClient\u Polly\u测试
{
常量字符串TestClient=“TestClient”;
私人住宅被重新调用;
[事实]
给定的公共异步任务\u重试\u策略\u已注册\u HttpClient\u当\u HttpRequest\u失败时\u请求\u重试()
{
//安排
IServiceCollection服务=新的ServiceCollection();