我创建了一个自定义库,它自动为特定服务设置 Polly 策略,这些服务依赖于HttpClient
.
这是使用IServiceCollection
扩展方法和类型化客户端方法完成的。一个简化的例子:
public static IHttpClientBuilder SetUpFooServiceHttpClient(this IServiceCollection services)
{
return services
.AddHttpClient<FooService>()
.AddPolicyHandler(GetRetryPolicy());
}
public class FooService
{
private readonly HttpClient _client;
public FooService(HttpClient httpClient)
{
_client = httpClient;
}
public void DoJob()
{
var test = _client.GetAsync("http://example.com");
}
}
请注意,我的真实代码使用了泛型类型和选项生成器,但为了简单起见,我省略了该部分。我的测试的目的是确认我的选项生成器正确地应用了我希望它应用的策略。为了此处的示例,我们假设它是我要测试的硬编码重试策略。
我现在想测试这个库是否正确地将 Polly 策略注册到我注入的HttpClient
依赖项中。
注意
有很多答案可以在网上和 StackOverflow 上找到,其中建议是HttpClient
自己构建,即:new HttpClient(new MyMockedHandler());
,但这违背了我需要测试实际IHttpClientFactory
是否使用请求的策略构建 httpclients 的目的。
为此,我想使用由real生成的real 进行测试,但我希望模拟它的处理程序,这样我就可以避免发出实际的 Web 请求并人为地导致错误的响应。HttpClient
IHttpClientFactory
我AddHttpMessageHandler()
用来注入一个模拟处理程序,但工厂似乎忽略了这一点。
这是我的测试夹具:
public class BrokenDelegatingHandler : DelegatingHandler
{
public int SendAsyncCount = 0;
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
SendAsyncCount++;
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.InternalServerError));
}
}
private BrokenDelegatingHandler _brokenHandler = new BrokenDelegatingHandler();
private FooService GetService()
{
var services = new ServiceCollection();
services.AddTransient<BrokenDelegatingHandler>();
var httpClientBuilder = services.SetUpFooServiceHttpClient();
httpClientBuilder.AddHttpMessageHandler(() => _brokenHandler);
services.AddSingleton<FooService>();
return services
.BuildServiceProvider()
.GetRequiredService<FooService>();
}
这是我的测试:
[Fact]
public void Retries_client_connection()
{
int retryCount = 3;
var service = GetService();
_brokenHandler.SendAsyncCount.Should().Be(0); // PASS
var result = service.DoJob();
_brokenHandler.SendAsyncCount.Should().Be(retryCount); // FAIL: expected 3 but got 0
}
当我调试测试时,处理程序的断点永远不会被命中,并且响应返回为 200(因为它实际上连接到 URL,而不是命中模拟处理程序)。
为什么 http 客户端工厂忽略了我的模拟处理程序?
请注意,我还将接受任何允许我以另一种有效方式测试策略的答案。
我知道我可以只使用一个损坏的 URL 字符串,但我需要在我的测试中测试特定的 http 响应。