How to Mock the New Httpclientfactory in .Net Core 2.1 Using Moq

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

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);
}

Mock HttpClientFactory to create a mocked HttpClient Using Moq Framework

I suggest to look into the following blog post from Gingster Ale. This example of the author shows you how you can mock calls to HttpClient:

// ARRANGE
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
// Setup the PROTECTED method to mock
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
// prepare the expected response of the mocked http call
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':1,'value':'1'}]"),
})
.Verifiable();

// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
BaseAddress = new Uri("http://test.com/"),
};

var subjectUnderTest = new MyTestClass(httpClient);

// ACT
var result = await subjectUnderTest
.GetSomethingRemoteAsync('api/test/whatever');

// ASSERT
result.Should().NotBeNull(); // this is fluent assertions here...
result.Id.Should().Be(1);

// also check the 'http' call was like we expected it
var expectedUri = new Uri("http://test.com/api/test/whatever");

handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get // we expected a GET request
&& req.RequestUri == expectedUri // to this uri
),
ItExpr.IsAny<CancellationToken>()
);

Also, if you have the freedom to use something else than HttpClient, I recommend to take a look into Flurl.

Mocking HttpClient in unit tests

Your interface exposes the concrete HttpClient class, therefore any classes that use this interface are tied to it, this means that it cannot be mocked.

HttpClient does not inherit from any interface so you will have to write your own. I suggest a decorator-like pattern:

public interface IHttpHandler
{
HttpResponseMessage Get(string url);
HttpResponseMessage Post(string url, HttpContent content);
Task<HttpResponseMessage> GetAsync(string url);
Task<HttpResponseMessage> PostAsync(string url, HttpContent content);
}

And your class will look like this:

public class HttpClientHandler : IHttpHandler
{
private HttpClient _client = new HttpClient();

public HttpResponseMessage Get(string url)
{
return GetAsync(url).Result;
}

public HttpResponseMessage Post(string url, HttpContent content)
{
return PostAsync(url, content).Result;
}

public async Task<HttpResponseMessage> GetAsync(string url)
{
return await _client.GetAsync(url);
}

public async Task<HttpResponseMessage> PostAsync(string url, HttpContent content)
{
return await _client.PostAsync(url, content);
}
}

The point in all of this is that HttpClientHandler creates its own HttpClient, you could then of course create multiple classes that implement IHttpHandler in different ways.

The main issue with this approach is that you are effectively writing a class that just calls methods in another class, however you could create a class that inherits from HttpClient (See Nkosi's example, it's a much better approach than mine). Life would be much easier if HttpClient had an interface that you could mock, unfortunately it does not.

This example is not the golden ticket however. IHttpHandler still relies on HttpResponseMessage, which belongs to System.Net.Http namespace, therefore if you do need other implementations other than HttpClient, you will have to perform some kind of mapping to convert their responses into HttpResponseMessage objects. This of course is only a problem if you need to use multiple implementations of IHttpHandler but it doesn't look like you do so it's not the end of the world, but it's something to think about.

Anyway, you can simply mock IHttpHandler without having to worry about the concrete HttpClient class as it has been abstracted away.

I recommend testing the non-async methods, as these still call the asynchronous methods but without the hassle of having to worry about unit testing asynchronous methods, see here

Mock HttpClient using Moq

That particular overload method is not virtual so is unable to be overridden by Moq.

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);

Which is why it throws NotSupportedException

The virtual method you are looking for is this method

public virtual Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);

However mocking HttpClient is not as simple as it seems with its internal message handler.

I suggest using a concrete client with a custom message handler stub that will allow for more flexibility when faking the request.

Here is an example of a delegating handler stub.

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);
}
}

Note the default constructor is doing basically what you were trying to mock before. It also allows for more custom scenarios with a delegate for the request.

With the stub, the test can be refactored to something like

public async Task _SendRequestAsync_Test() {
//Arrange
var handlerStub = new DelegatingHandlerStub();
var client = new HttpClient(handlerStub);
var sut = new ClassA(client);
var obj = new SomeObject() {
//Populate
};

//Act
var response = await sut.SendRequest(obj);

//Assert
Assert.IsNotNull(response);
Assert.IsTrue(response.IsSuccessStatusCode);
}

Mocking HttpMessageHandler with moq - How do I get the contents of the request?

To take advantage of the async delegate use the Returns method instead

public class TestHttpClientFactory : IHttpClientFactory {
public HttpClient CreateClient(string name) {
var messageHandlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);

messageHandlerMock.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.Returns(async (HttpRequestMessage request, CancellationToken token) => {

string requestMessageContent = await request.Content.ReadAsStringAsync();

HttpResponseMessage response = new HttpResponseMessage();

//...decide what to put in the response after looking at the contents of the request

return response;
})
.Verifiable();

var httpClient = new HttpClient(messageHandlerMock.Object);
return httpClient;
}
}

Or consider creating your own handler that exposes a delegate to handle the desired behavior.

For example

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);
}
}

And used in the factory like this

public class TestHttpClientFactory : IHttpClientFactory {
public HttpClient CreateClient(string name) {
var messageHandlerMock = new DelegatingHandlerStub(async (HttpRequestMessage request, CancellationToken token) => {

string requestMessageContent = await request.Content.ReadAsStringAsync();

HttpResponseMessage response = new HttpResponseMessage();

//...decide what to put in the response after looking at the contents of the request

return response;
});

var httpClient = new HttpClient(messageHandlerMock);
return httpClient;
}
}

Mocking HttpMessageHandler with moq - How do I get the contents of the request?

To take advantage of the async delegate use the Returns method instead

public class TestHttpClientFactory : IHttpClientFactory {
public HttpClient CreateClient(string name) {
var messageHandlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);

messageHandlerMock.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.Returns(async (HttpRequestMessage request, CancellationToken token) => {

string requestMessageContent = await request.Content.ReadAsStringAsync();

HttpResponseMessage response = new HttpResponseMessage();

//...decide what to put in the response after looking at the contents of the request

return response;
})
.Verifiable();

var httpClient = new HttpClient(messageHandlerMock.Object);
return httpClient;
}
}

Or consider creating your own handler that exposes a delegate to handle the desired behavior.

For example

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);
}
}

And used in the factory like this

public class TestHttpClientFactory : IHttpClientFactory {
public HttpClient CreateClient(string name) {
var messageHandlerMock = new DelegatingHandlerStub(async (HttpRequestMessage request, CancellationToken token) => {

string requestMessageContent = await request.Content.ReadAsStringAsync();

HttpResponseMessage response = new HttpResponseMessage();

//...decide what to put in the response after looking at the contents of the request

return response;
});

var httpClient = new HttpClient(messageHandlerMock);
return httpClient;
}
}


Related Topics



Leave a reply



Submit