为集成测试创建默认的 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的主要内容,如果未能解决你的问题,请参考以下文章

无法为 Django 的重置密码流程创建集成测试

如何使用 @Build 为 grails 集成测试创建多种类型的测试数据

Kafka 测试容器未运行

Spring boot、ElasticSearch 和 TestContainers 集成测试。拒绝连接

如何通过排除集成测试来运行 sbt 程序集

如何为 MEAN 应用程序编写集成测试