上面的答案对我有很大的帮助,让我找到了正确的方向。然而,我想测试是否已经将策略添加到了类型化的http客户端中。这个客户端是在应用程序启动时定义的。所以问题在于如何在类型化的客户端定义中指定的处理程序之后添加一个存根委托处理程序,并将其添加到服务集合中。
我能够利用IHttpMessageHandlerBuilderFilter.Configure并将我的存根处理程序添加为链中的最后一个处理程序。
public sealed class HttpClientInterceptionFilter : IHttpMessageHandlerBuilderFilter
{
HandlerConfig handlerconfig { get; set; }
public HttpClientInterceptionFilter(HandlerConfig calls)
{
handlerconfig = calls;
}
public Action<HttpMessageHandlerBuilder> Configure(Action<HttpMessageHandlerBuilder> next)
{
return (builder) =>
{
next(builder);
builder.AdditionalHandlers.Add(new StubDelegatingHandler(handlerconfig));
};
}
}
在你的单元测试中,将这个类注册到DI容器中:
services.AddTransient<IHttpMessageHandlerBuilderFilter>(n => new HttpClientInterceptionFilter(handlerConfig))
我需要把参数传递给存根处理程序,并从中获取数据并将其返回到我的单元测试中。我使用了这个类来完成:
public class HandlerConfig
{
public int CallCount { get; set; }
public DateTime[] CallTimes { get; set; }
public int BackOffSeconds { get; set; }
public ErrorTypeEnum ErrorType { get; set; }
}
public enum ErrorTypeEnum
{
Transient,
TooManyRequests
}
我的存根处理程序生成了短暂的和太多的请求响应:
public class StubDelegatingHandler : DelegatingHandler
{
private HandlerConfig _config;
HttpStatusCode[] TransientErrors = new HttpStatusCode[] { HttpStatusCode.RequestTimeout, HttpStatusCode.InternalServerError, HttpStatusCode.OK };
public StubDelegatingHandler(HandlerConfig config)
{
_config = config;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
_config.CallTimes[_config.CallCount] = DateTime.Now;
if (_config.ErrorType == ErrorTypeEnum.Transient)
{
var response = new HttpResponseMessage(TransientErrors[_config.CallCount]);
_config.CallCount++;
return Task.FromResult(response);
}
HttpResponseMessage response429;
if (_config.CallCount < 2)
{
response429 = new HttpResponseMessage(HttpStatusCode.TooManyRequests);
response429.Headers.Date = DateTime.UtcNow;
DateTimeOffset dateTimeOffSet = DateTimeOffset.UtcNow.Add(new TimeSpan(0, 0, 5));
long resetDateTime = dateTimeOffSet.ToUnixTimeSeconds();
response429.Headers.Add("x-rate-limit-reset", resetDateTime.ToString());
}
else
{
response429 = new HttpResponseMessage(HttpStatusCode.OK);
}
_config.CallCount++;
return Task.FromResult(response429);
}
}
最后是单元测试:
[TestMethod]
public async Task Given_A_429_Retry_Policy_Has_Been_Registered_For_A_HttpClient_When_429_Errors_Occur_Then_The_Request_Is_Retried()
{
IServiceCollection services = new ServiceCollection();
var handlerConfig = new HandlerConfig { ErrorType = ErrorTypeEnum.TooManyRequests, BackOffSeconds = 5, CallTimes = new System.DateTime[RetryCount] };
services.AddTransient<IHttpMessageHandlerBuilderFilter>(n => new HttpClientInterceptionFilter(handlerConfig));
services.ConfigureAPIClient();
HttpClient configuredClient =
services
.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>()
.CreateClient("APIClient");
var result = await configuredClient.GetAsync("https://localhost/test");
Assert.AreEqual(3, handlerConfig.CallCount, "Expected number of calls made");
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode, "Verfiy status code");
var actualWaitTime = handlerConfig.CallTimes[1] - handlerConfig.CallTimes[0];
var expectedWaitTime = handlerConfig.BackOffSeconds + 1;
Assert.AreEqual(expectedWaitTime, actualWaitTime.Seconds);
}
}