Mocking HttpClient in unit tests
Mocking HttpClient
in .NET isn't difficult. But if you haven't done it before, it's not obvious how to approach it, since the class has no interface or virtual methods. What you should mock, is the protected abstract SendAsync
method of the HttpMessageHandler
class, which can be passed to HttpClient
as a constructor parameter.
If you're using Moq, you can mock it like this (notice the call to Protected()
to allow setting up a mock for a protected method):
var handlerMock = new Mock<HttpMessageHandler>();
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = JsonContent.Create(new[] { 1, 2, 3 }),
});
var httpClient = new HttpClient(handlerMock.Object);
Depending on the HttpClient
consumption pattern you're using in your project, you have two options from here on:
When using typed clients, you can simply inject the
HttpClient
instance into your service:var hackerNews = new HackerNewsTypedClient(httpClient);
For basic usage and named clients, you need to create a mock
IHttpClientFactory
and inject that into the service:var httpClientFactoryMock = new Mock<IHttpClientFactory>(); httpClientFactoryMock .Setup(x => x.CreateClient(It.IsAny<string>())) .Returns(httpClient); var hackerNews = new HackerNewsBasicUsage(httpClientFactoryMock.Object);
You can make your job easier by using Moq.Contrib.HttpClient. This NuGet package adds a collection of extension methods that make mocking the HttpMessageHandler
in Moq much less verbose:
var handlerMock = new Mock<HttpMessageHandler>();
handlerMock
.SetupRequest(
HttpMethod.Get,
"https://hacker-news.firebaseio.com/v0/topstories.json")
.ReturnsJsonResponse(new[] { 1, 2, 3 });
var httpClient = handlerMock.CreateClient();
If you need an IHttpClientFactory
instead of the HttpClient
, just replace the last line of code:
var httpClientFactory = handlerMock.CreateClientFactory();
If you're not using Moq, you should take a look at RichardSzalay.MockHttp NuGet package instead. It provides a mock HttpMessageHandler
implementation that doesn't depend on any mocking libraries:
var handlerMock = new MockHttpMessageHandler();
handlerMock
.When(
HttpMethod.Get,
"https://hacker-news.firebaseio.com/v0/topstories.json")
.Respond("application/json", "[1, 2, 3]");
var httpClient = handlerMock.ToHttpClient();
Unfortunately, there is no helper method to create an IHttpClientFactory
instance, so you'll need to take care of this part yourself, if you need it.
You can find a sample project with full source code in my GitHub repository. Each commit contains one of the approaches described in this post.
Mocking HttpClient
seems a difficult or even an impossible job at first glance. Fortunately, that's not the case. You just need to mock the underlying HttpMessageHandler
instead of HttpClient
directly. In this post, I described three different approaches you can take to do that.