I'm trying to create a (mostly) unified set of integation tests that can either be targetted at an in-memory API created from a WebApplicationFactory
or at a fully-deployed version of our app. Using XUnit.DependencyInjection
, I'm planning on injecting a HttpClient
into my tests that either points to the test server or the real app based on an environment variable.
So to create a client for the test server, I can just run the following in Startup.cs
:
WebApplicationFactory<Program> app = new();
HttpClient client = app.CreateClient();
This seems to work. However, I have absolutely no idea how to inject this implementation for the HttpClient
into the individual test classes.
Something like this, doesn't work (such an overload doesn't exist):
services.AddHttpClient<MyTestClass>(client);
And neither does this (the injected client has the BaseAddress
set to null for some reason):
services.AddHttpClient<InMemoryServerSelfTests>(c =>
{
c.BaseAddress = client.BaseAddress;
c.Timeout = client.Timeout;
});
My only other thought is to create a new class that wraps both clients and inject that instead but that seems messy:
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));
}
}
So really, I'm a bit stumped... Any ideas on how to accomplish this?
The problem is that the HttpClient
generated by the WebApplicationFactory
is special as the WebApplicationFactory
is hosted in memory and is not visible out of process (I think that's what I read elsewhere). What that means is that copying over the settings doesn't work.
The only way I've managed to get the WebApplicationFactory
client registered so that it is resolvable is to register an instance of IHttpClientFactory
with the container that returns clients from the WebApplicationFactory
.
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();
}
services.AddSingleton<IHttpClientFactory>(new TestClientFactory(...));
Something along those lines will work.