Mocking HttpClient in unit tests

December 22nd 2023 Moq Unit Testing .NET

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.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License