Reputation: 727
I'm calling an external API and would like my API to be unit testable. And to do that, i'm trying to wrap HttpClient. I only need one method for now. Here is my interface.
public interface IHttpClient
{
Task<string> GetStringAsync(string url);
}
And this is how I implemented it.
public class HttpClientWrapper : IHttpClient { private readonly HttpClient _httpClient;
public HttpClientWrapper()
{
// I could also inject this but I think this will be fine as is.
_httpClient = new HttpClient(new HttpClientHandler(), false);
}
public async Task<string> GetStringAsync(string url)
{
//validate url here
return await _httpClient.GetStringAsync(url);
}
}
Doubts I have? is this the right way to do it? Will setting the bool parameter result in resource leaking here? I read a couple of conflicting ideas about whether HttpClient has to be disposed on every call or not. I took, the not disposing side but am not really quite certain though.
If there is a way to use HttpClient without having a wrapper but make the API testable, that will be great too. But so far, i failed to get that working.
Thanks, CleanKoder
Upvotes: 8
Views: 4314
Reputation: 3589
While it could still be nice to create an interface for the client, the HttpClient
class is actually designed with testability in mind! When instantiating your HttpClient
you can inject a custom HttpMessageHandler
. By overriding Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
in this class, you can interrupt all requests before they are actually written to the socket, inspecting them and returning whatever you see fit.
Here is an example of such a test double I wrote in a project, feel free to modify it to suit your needs:
public class FakeHttpMessageHandler : HttpMessageHandler
{
public HttpRequestMessage LastRequest;
public string LastRequestString = string.Empty;
public string ResponseContent = string.Empty;
public HttpStatusCode ResponseStatusCode = HttpStatusCode.OK;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request.Content != null)
{
LastRequestString = await request.Content.ReadAsStringAsync();
}
LastRequest = request;
return await Task.FromResult(new HttpResponseMessage
{
StatusCode = ResponseStatusCode,
Content = new StringContent(ResponseContent)
});
}
}
You could also use some isolation framework like NSubstitute if you think that's more appropriate for your project.
Upvotes: 6