从 WebApplicationFactory 注入 HttpClient

Per*_*nce 4 c# asp.net dependency-injection dotnet-httpclient

WebApplicationFactory我正在尝试创建一组(大部分)统一的集成测试,这些测试可以针对从我们的应用程序的完全部署版本创建的内存中 API 。使用XUnit.DependencyInjection,我计划将 a 注入HttpClient到我的测试中,该测试要么指向测试服务器,要么基于环境变量指向真实的应用程序。

因此,要为测试服务器创建客户端,我可以在以下位置运行以下命令Startup.cs

WebApplicationFactory<Program> app = new();
HttpClient client = app.CreateClient();
Run Code Online (Sandbox Code Playgroud)

这似乎有效。但是,我完全不知道如何将此实现注入到HttpClient各个测试类中。

像这样的东西不起作用(这样的重载不存在):

services.AddHttpClient<MyTestClass>(client);
Run Code Online (Sandbox Code Playgroud)

这也不是(注入的客户端BaseAddress由于某种原因设置为 null):

services.AddHttpClient<InMemoryServerSelfTests>(c =>
                                    {
                                        c.BaseAddress           = client.BaseAddress;
                                        c.Timeout               = client.Timeout;
                                    });
Run Code Online (Sandbox Code Playgroud)

我唯一的另一个想法是创建一个新类来包装两个客户端并注入它,但这看起来很混乱:

public class TestClientWrapper
{
    public readonly HttpClient Client;
    public TestClientWrapper(InMemoryTestServer server)
    {
        Client = server.CreateClient();
    }

    public TestClientWrapper(HttpClient client)
    {
        Client = client;
    }
}

// In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    string targetEndpoint = Environment.GetEnvironmentVariable("targetEndpoint"); // Make this configurable
    bool   isLocal        = string.IsNullOrEmpty(targetEndpoint);
    
    if (isLocal)
    {
        InMemoryTestServer app = new();
        services.AddSingleton(new TestClientWrapper(app));
    }
    else
    {
        HttpClient client = new();
        services.AddSingleton(new TestClientWrapper(client));
    }
}
Run Code Online (Sandbox Code Playgroud)

所以真的,我有点难住了......关于如何实现这一目标有什么想法吗?

bri*_*ler 6

问题是HttpClient生成的WebApplicationFactory是特殊的,因为WebApplicationFactory托管在内存中并且在进程外不可见(我认为这是我在其他地方读到的)。这意味着复制设置不起作用。

我设法WebApplicationFactory注册客户端以使其可解析的唯一方法是向IHttpClientFactoryWebApplicationFactory.

public class TestHttpClientFactory<TStartup> : IHttpClientFactory 
    where TStartup : class
{
    private readonly WebApplicationFactory<TStartup> _appFactory;

    public TestHttpClientFactory(WebApplicationFactory<TStartup> appFactory) => _appFactory = appFactory;

    public HttpClient CreateClient(string name) => _appFactory.CreateClient();
}
Run Code Online (Sandbox Code Playgroud)
services.AddSingleton<IHttpClientFactory>(new TestClientFactory(...));
Run Code Online (Sandbox Code Playgroud)

沿着这些思路的一些东西将会起作用。