.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);
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.
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") .
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.
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.
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); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With