Home > OS >  Nunit 3: Test a Controller that uses IHttpClientFactory as Constructor Parameter
Nunit 3: Test a Controller that uses IHttpClientFactory as Constructor Parameter

Time:10-16

I am using a .NET 6 Web API controller with IHttpClientFactory to perform external API calls. As such, I have the following constructor:

public MyController(IHttpClientFactory httpClientFactory)
{
  _httpClientFactory = httpClientFactory;
}

This works because in my Program.cs I add an HTTP Client to my builder.Services.

In my tests, how do I instantiate/set up the httpClientFactory for the controller because I need it to instantiate my controller: var controller = new MyController(httpClientFactory); generates an error since there isn't any settings added.

I ran into a similar issue with configurations from appsettings.json and resolved with ConfigurationBuilder but there doesn't seem to be a similar one for IHttpClientFactory.

If you need any more information, please let me know. Thanks!

CodePudding user response:

I tried as below:

[TestFixture]
    public class IndexActionTests
    {
        private HomeController controller;

        [SetUp]
        public void Setup()
        {
            var services = new ServiceCollection();
            services.AddHttpClient();
            var provider = services.BuildServiceProvider();
            var httpclientfactory = provider.GetService<IHttpClientFactory>();
            controller = new HomeController(httpclientfactory);
        }

        [Test]
        public void Test1()
        {
            var result = controller.Index();
            Assert.AreEqual(typeof(ViewResult),result.GetType());
        }
    }

Result:

enter image description here

CodePudding user response:

In order to be able to use a properly mocked IHttpClientFactory in your unit test you need to do the following steps:

Setup a DelegatingHandler mock

var mockHandler = new Mock<DelegatingHandler>();
mockHandler.Protected()
    .Setup<Task<HttpResponseMessage>>("SendAsync", It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())
    .ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK))
    .Verifiable();
mockHandler.As<IDisposable>().Setup(s => s.Dispose());

This sample mock will always return with 200 OK status code and without a response body

  • Tailor the setup for your needs

Create an HttpClient

var httpClient = new HttpClient(mockHandler.Object);

It creates an HttpClient instance and pass the above handler to it

Setup an IHttpClientFactory mock

var mockFactory = new Mock<IHttpClientFactory>(MockBehavior.Strict);
mockFactory
  .Setup(factory => factory.CreateClient())
  .Returns(httpClient)
  .Verifiable();

It setups an IHttpClientFactory mock to return the above HttpClient for the CreateClient method call

  • If you use the IHttpClientFactory to create a named client then change the Setup to this .Setup(factory => factory.CreateClient(It.IsAny<string>()))

Use the mock objects for verification

mockFactory.Verify(factory => factory.CreateClient(), Times.Once); 
mockHandler.Protected()
   .Verify("SendAsync", Times.Once(), It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>());
  • Related