Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock the new HttpClientFactory in .NET Core 2.1 using Moq

.NET Core 2.1 comes with this new factory called HttpClientFactory, but I can't figure out how to mock it to unit test some methods that include REST service calls.

The factory is being injected using .NET Core IoC container, and what the method does is create a new client from the factory:

var client = _httpClientFactory.CreateClient(); 

And then using the client to get data from a REST service:

var result = await client.GetStringAsync(url); 
like image 332
Mauricio Atanache Avatar asked Jan 17 '19 00:01

Mauricio Atanache


People also ask

What can be mocked with Moq?

You can use Moq to create mock objects that simulate or mimic a real object. Moq can be used to mock both classes and interfaces.

How do you mock HttpClient factory?

Configuring the IHttpClientFactory instance CreateClient("ext_service")). Returns(httpClient); var service = new MyExternalService(mockHttpClientFactory. Object); So, we create the Mock of IHttpClientFactory and define the instance of HttpClient that will be returned when calling CreateClient("ext_service") .

How do I create an instance of HttpClientFactory?

Basic HttpClientFactory usage A basic HttpClientFactory can be instanced via Dependency Injection. First we will need to add the following code to the Startup class within the ConfigureServices method: // File: Startup. cs public class Startup { // Code deleted for brevity.

How do you mock HttpMessageHandler?

Mocking HttpMessageHandler is simple as there is only one, protected SendAsync() method . Importing the Moq. Protected namespace allows us to mock protected methods with the Protected() method. We mock a call to the SendAsync() function and provide a response to return with the ReturnAsync() method.


1 Answers

The HttpClientFactory is derived from IHttpClientFactory Interface So it is just a matter of creating a mock of the interface

var mockFactory = new Mock<IHttpClientFactory>(); 

Depending on what you need the client for, you would then need to setup the mock to return a HttpClient for the test.

This however requires an actual HttpClient.

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

The factory can then be injected into the dependent system under test when exercising the test.

If you do not want the client calling actual endpoints then you will need to create a fake delegate handler to intercept the requests.

Example of the handler stub used to fake the requests

public class DelegatingHandlerStub : DelegatingHandler {     private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;     public DelegatingHandlerStub() {         _handlerFunc = (request, cancellationToken) => Task.FromResult(request.CreateResponse(HttpStatusCode.OK));     }      public DelegatingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc) {         _handlerFunc = handlerFunc;     }      protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {         return _handlerFunc(request, cancellationToken);     } } 

Taken from an answer I gave here

Reference Mock HttpClient using Moq

Suppose you have a controller

[Route("api/[controller]")] public class ValuesController : Controller {     private readonly IHttpClientFactory _httpClientFactory;      public ValuesController(IHttpClientFactory httpClientFactory) {         _httpClientFactory = httpClientFactory;     }      [HttpGet]     public async Task<IActionResult> Get() {         var client = _httpClientFactory.CreateClient();         var url = "http://example.com";         var result = await client.GetStringAsync(url);         return Ok(result);     } } 

and wanted to test the Get() action.

public async Task Should_Return_Ok() {     //Arrange     var expected = "Hello World";     var mockFactory = new Mock<IHttpClientFactory>();     var configuration = new HttpConfiguration();     var clientHandlerStub = new DelegatingHandlerStub((request, cancellationToken) => {         request.SetConfiguration(configuration);         var response = request.CreateResponse(HttpStatusCode.OK, expected);         return Task.FromResult(response);     });     var client = new HttpClient(clientHandlerStub);          mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(client);          IHttpClientFactory factory = mockFactory.Object;          var controller = new ValuesController(factory);          //Act     var result = await controller.Get();          //Assert     result.Should().NotBeNull();          var okResult = result as OkObjectResult;          var actual = (string) okResult.Value;          actual.Should().Be(expected); } 
like image 181
Nkosi Avatar answered Sep 18 '22 02:09

Nkosi