为集成测试创建默认的 HttpClientFactory
Posted
技术标签:
【中文标题】为集成测试创建默认的 HttpClientFactory【英文标题】:Create default HttpClientFactory for integration test 【发布时间】:2018-09-30 09:03:55 【问题描述】:我有一个打字客户端,我想注册为单身人士:
public class SomeHttpClient
private readonly IHttpClientFactory _clientFactory;
public SomeHttpClient(IHttpClientFactory clientFactory)
_clientFactory = clientFactory;
public Task MakeSomeCallAsync(...)
var client = _clientFactory.Create();
// Do more stuff
类型化的客户端必须是单例的,因为它有一些断路器逻辑,如果 API 限制客户端的速率,它会中断电路。必须在整个应用程序中断开电路,而不仅仅是当前的请求管道(否则客户端将继续访问已经受到其他实例速率限制的 API)。这已经通过使用封装了该功能的类型化客户端实现(虽然不是 Polly)。
现在因为类型化的客户端将是一个单例,我们不能注入单个 HttpClient
,因为这会带来我们之前在单个长寿 HttpClient
中遇到的所有问题。显而易见的解决方案是注入一个HttpClientFactory
,现在可以在每次键入客户端需要一个HttpClient
实例时使用它,而不必担心生命周期管理等,因为现在已经推迟到HttpClientFactory
。
我现在的问题是如何为简单的功能集成测试创建默认的HttpClientFactory
,我想在其中实例化一个类型化的客户端,看看我是否可以点击 API 并从自动化测试中获得成功的响应?
我不想围绕它设置一个 ASP.NET Core TestServer 和一个完整的应用程序,因为这对于我现在想做的那种功能测试来说太过分了。
没有可用于注入 HttpClientFactory
的公共构造函数?
【问题讨论】:
【参考方案1】:@dustinmoris' answer,但在 C# 中
public sealed class DefaultHttpClientFactory : IHttpClientFactory
public HttpClient CreateClient(string name) => new HttpClient();
或者更好的是,让客户端单例:
public sealed class DefaultHttpClientFactory : IHttpClientFactory
private static readonly Lazy<HttpClient> _httpClientLazy =
new Lazy<HttpClient>(() => new HttpClient());
// NOTE: This will always return the same HttpClient instance.
public HttpClient CreateClient(string name) => _httpClientLazy.Value;
【讨论】:
对于单例,您需要将 DefaultHttpClientFactory 默认构造函数明确地设置为私有 @StevenJ 不,您需要能够实例化DefaultHttpClientFactory
才能使用CreateClient
。此外,_httpClientLazy
是静态的,这就是使 HttpClient
实例成为单例的原因。
这就是我的意思,这不是一个单例 :) 要么你放置私有构造函数并设置 CreateClient 静态,或者如果你喜欢你做虚拟方法,那么不需要设置静态惰性实例和管理生命周期对象 DefaultHttpClientFactory 然后在对象容器中
@StevenJ 恐怕你弄错了。只有返回值应该是单例的,不是工厂。工厂和CreateClient
方法是通过IHttpClientFactory
提供的,您不能在接口上调用静态方法,因此如果您想在 DI 上下文中使用它,它必须是可实例化的。
是的,我的意思是使用通常进行依赖注入的对象容器【参考方案2】:
我现在的问题是如何为一个简单的功能集成测试创建一个默认的 HttpClientFactory,我想在其中实例化一个类型化的客户端,看看我是否可以点击 API 并从自动化测试中获得成功的响应?
您的测试可以采用您用于在StartUp
中配置 HttpClientFactory 的确切代码(这是使用 Polly 策略的示例,但它可以是 HttpClientFactory 上的任何配置)。
IServiceCollection services = new ServiceCollection(); // [1]
services.AddHttpClient(TestClient)
.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
.RetryAsync(3); // [2]
第 [2] 行可能是您的 StartUp
类中的确切代码。您可以将其从正常的 ConfigureServices(...)
方法中分解为单独的 static
方法,您的测试也可以调用该方法 - 因此您的测试完全测试了 StartUp
的配置。
然后在测试中:
IHttpClientFactory factory = services
.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>();
var sut = new SomeHttpClient(factory);
【讨论】:
【参考方案3】:我只是自己创建了一个用于测试(我认为可能已经有一种方法可以为我做到这一点):
type DefaultHttpClientFactory() =
interface IHttpClientFactory with
member __.CreateClient (name) =
new HttpClient()
【讨论】:
来吧,F#是个骗子!与 C# 冗长比较 =) 对我来说,这是一个很好的答案(如果您转换回 C#),因为我实际上想在集成测试中发送数据。使用这种方法,您可以完全控制。非常好!以上是关于为集成测试创建默认的 HttpClientFactory的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 @Build 为 grails 集成测试创建多种类型的测试数据