Unit Test and Mock HttpClientFactory in .NET Core

Mock HttpClientFactory

In this article, we will see how to Unit Test and Mock HttpClientFactory in .NET Core with proper mocking techniques in .NET Core.

As we learned in our last article of best practices that HTTTPClient object should be created using HttpClientFactory as it provides multiple benefits.

In this article, we will see another benefit of using this interface i.e easy to unit test.

Today in this article, we will cover below aspects,

HttpClientFacory can also be used in the others way like using Named HTTPClient and Typed HTTPClient

Unit Test and Mocking HttpClientFactory-Basic and Named

Today in this post we will see how to mock basic or named HttpClientFactory interface.

Below is a sample code API created using ASP.NET Core 3.1, which we shall be unit testing and mocking.

The below code demonstrates Basic and Named HTTPClient usage which we shall be unit testing and mocking using XUnit and Moq.

  
        [HttpGet]
        public async Task<IActionResult> OnGetAccount()
        {
            var uri = new Uri("https://localhost:44364/account");

            var client = _clientFactory.CreateClient();

            var response = await client.GetAsync(uri);

            if (response.IsSuccessStatusCode)
            {
                return Ok(response.Content.ReadAsStreamAsync().Result);
            }
            else
            {
                return StatusCode(500, "Something Went Wrong! Error Occured");
            }
        }

If using Named HTTPClient request object then please update the CreateClient() method with the name of the HTTP object,

var client = _clientFactory.CreateClient("PaymentClient");

Below is the Controller constructor accepting the IHttpClientFactory interface which is DI through the Constructor.

    [Route("api/[controller]")]
    [ApiController]
    public class AccountController : ControllerBase
    {
        private readonly IHttpClientFactory _clientFactory;
        public AccountController(IHttpClientFactory clientFactory)
        {
            _clientFactory = clientFactory;
        }
..
..


1. Mock IHttpClientFactory interface

So as a first step we should be mocking IHttpClientFactory and all its extension methods.

Being an interface IHttpClientFactory is very easy to mock as below,

             //Arrange
            var mockFactory = new Mock<IHttpClientFactory>();

2. Mock HTTPClient object

We need to create explicit actual HTTPClient mock objects to pass to the extension method of IHttpClientFactory i.e CreateClient().

The best way to create a mock HTTPClient instance is by mocking HttpMessageHandler. Mocking of HttpMessageHandler will also ensure the client calling actual endpoints are faked by intercepting it.

Below is the complete code,

var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
            mockHttpMessageHandler.Protected()
                .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
                .ReturnsAsync(new HttpResponseMessage
                {
                    StatusCode = HttpStatusCode.OK,
                    Content = new StringContent("{'name':thecodebuzz,'city':'USA'}"),
                });



            var client = new HttpClient(mockHttpMessageHandler.Object);
            mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(client);

So far so good.

3. Controller with mocked IHttpClientFactory

AccountController controller = new AccountController(mockFactory.Object);

All the above 3 steps ensure our Arrange steps are properly configured within Unit Testing.

Let’s Invoke the method as below,

            //Act
            var result = await controller.OnGetAccount() as ObjectResult;

4. Assert the Results

              //Assert
             Assert.NotNull(result);
             Assert.Equal(HttpStatusCode.OK,(HttpStatusCode)result.StatusCode);

Finally, we are all set with the results and our Unit test cases pass successfully.

Unit Test and Mock HttpClientFactory

Mock Typed HTTPClient

Please refer to the below post on mocking the Typed HTTPClient request object.

Using MS Test framework to Mock HttpClientFactory

You can leverage MSTes or Nunit Test framework similar way to write Unit test cases for code containing HttpClientFactory.

References:

That’s All, Happy coding !!. If you have any better suggestions, please sound off your comments below.



Please bookmark this page and share it with your friends. Please Subscribe to the blog to receive notifications on freshly published(2024) best practices and guidelines for software design and development.



6 thoughts on “Unit Test and Mock HttpClientFactory in .NET Core

    1. Hello Jayesh- Please add missing ‘using’ namespaces if any. Could you tell me what version of .NET framework you are using ?

  1. Hi,

    I get this error when trying to run above test:

    System.BadImageFormatException : Could not load file or assembly ‘Microsoft.Extensions.Http, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60’. Reference assemblies should not be loaded for execution. They can only be loaded in the Reflection-only loader context. (0x80131058)
    —-> System.BadImageFormatException : Cannot load a reference assembly for execution.

    I have tried lot of other samples and I get the same error. I am running .Net Core 3.1 / VS 2019

    1. Hi Abul- Thanks for your query. I see there could be multiple reasons for the above error.
      Are you using a Console application with HttpClientFactory? For the Console app, I have used version 3.1.1 which works fine for Microsoft.Extensions.Http.

      For other types of the app like ASP.NET Core I don’t see project references Microsoft.Extensions.Http
      Few tips:
      1. Try to use “Any CPU” in project settings.
      2. However such type error is common when trying to load a 32-bit assembly within a 64-bit environment or vice versa. Hope your .NET core 3.1 installed correctly 32 vs 64 bits.
      3. Clean any bin and obj folder in the project

      Hope this helps.

Leave a Reply

Your email address will not be published. Required fields are marked *