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?
2
Answers
The problem is that the
HttpClient
generated by theWebApplicationFactory
is special as theWebApplicationFactory
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 ofIHttpClientFactory
with the container that returns clients from theWebApplicationFactory
.Something along those lines will work.
I created a custom
HttpMessageHandlerBuilder
to solve this issue.Then in the startup code register the custom
TestServerHttpMessageHandlerBuilder
BEFORE callingAddHttpClient
.AddHttpClient
checks if aHttpMessageHandlerBuilder
was already registered and uses it instead of the default implementation.Now ALL
HttpClient
s created using theHttpClientFactory
will use the TestServer.