30

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?

1
  • Why can't you define and use your own httpclient factory interface? Commented Jul 11, 2022 at 11:54

3 Answers 3

29

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 (similar to what the real HttpClientFactory does):

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();
        }
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

for singleton you need to put DefaultHttpClientFactory default constructor explicetely to private
this is what i meant this is not a singleton :) either you put private constructor and set CreateClient static or if you prefer like you did virtual method then no need to set static the lazy instance and manage lifetime of object DefaultHttpClientFactory then in a object container
Yop what I meant by using object container which usually does dependency injection
17

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);

Comments

10

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()

4 Comments

Come on, F# is a cheat! Comparing with C# verbosity =)
For me, this is a great answer (if you convert back to C#) as I actually want to send data as part of my integration testing. Using this method you have full control. Very Nice!
Could someone actually convert that code back to C# please?
Literally just: public class DefaultHttpClientFactory : IHttpClientFactory { public HttpClient CreateClient(string name) { return new HttpClient(); } }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.