I have a typed client which I want to register as a singleton:
public class SomeHttpClient
{
private readonly IHttpClientFactory _clientFactory;
public SomeHttpClient(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public Task MakeSomeCallAsync(...)
{
var client = _clientFactory.Create();
// Do more stuff
}
}
The typed client must be a singleton, because it has some circuit breaker logic which breaks the circuit if the API is rate limiting the client. The circuit must be broken across the entire application and obviously not just the current request pipeline (otherwise the client is going to keep hitting the API which is already rate limiting from other instances). This is already implemented by using a typed client which encapsulates that functionality (not Polly though).
Now because the typed client is going to be a singleton we can't inject a single HttpClient
because that would bring all the problems we had before with a single long living HttpClient
. The obvious solution is to inject a HttpClientFactory
which now can be used to issue a HttpClient
instance every time the typed client needs one without having to worry about lifetime management etc, since that has been deferred to the HttpClientFactory
now.
My question now is how can I create a default HttpClientFactory
for a simple functional integration test where I want to instantiate a typed client and see if I can hit the API and get a successful response from an automated test?
I don't want to set up an ASP.NET Core TestServer and a whole application around it because that's an overkill for the sort of functional testing I want to do as this point.
There is no public constructor which I can use for injecting a HttpClientFactory
?
Here's an implementation that will return a new HttpClient
instance for each call (just as the interface contract specifies), but re-uses the same HttpMessageHandler
:
public sealed class DefaultHttpClientFactory : IHttpClientFactory, IDisposable
{
private readonly Lazy<HttpMessageHandler> _handlerLazy = new (() => new HttpClientHandler());
public HttpClient CreateClient(string name) => new (_handlerLazy.Value, disposeHandler: false);
public void Dispose()
{
if (_handlerLazy.IsValueCreated)
{
_handlerLazy.Value.Dispose();
}
}
}
My question now is how can I create a default HttpClientFactory for a simple functional integration test where I want to instantiate a typed client and see if I can hit the API and get a successful response from an automated test?
Your test can take the exact code that you use to configure the HttpClientFactory in StartUp
(this is an example using a Polly policy, but it could be any configuration on HttpClientFactory).
IServiceCollection services = new ServiceCollection(); // [1]
services.AddHttpClient(TestClient)
.AddPolicyHandler(HttpPolicyExtensions.HandleTransientHttpError()
.RetryAsync(3); // [2]
Line [2] could be the exact code from your StartUp
class. You could factor it out of the normal ConfigureServices(...)
method to a separate static
method which your test can also call - so your test tests exactly what StartUp
configures.
Then in the test just:
IHttpClientFactory factory = services
.BuildServiceProvider()
.GetRequiredService<IHttpClientFactory>();
var sut = new SomeHttpClient(factory);
I just create one myself for testing (I thought there's perhaps already a method that could do that for me):
type DefaultHttpClientFactory() =
interface IHttpClientFactory with
member __.CreateClient (name) =
new HttpClient()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With