I worked with HttpClient today and needed to write some unit tests. Here is how I did it.
First off, we need to define an HttpClientFactory interface like this:
public interface IHttpClientFactory
{
HttpClient CreateHttpClient();
}
Your API code must then not directly create an http client, but pass in a IHttpClientFactory instance, allowing for tailored functionality such as mocking / unit testing.
A default implementation could be:
public class DefaultHttpClientFactory : IHttpClientFactory
{
public HttpClient CreateHttpClient()
{
return new HttpClient();
}
}
And a mock implementation using Moq then looks like this:
using Moq;
using Moq.Protected;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace SomeAcme.SomeApi.Test
{
public class MockHttpClientFactory : IHttpClientFactory
{
private HttpResponseMessage _httpContentMessage;
public HttpClient CreateHttpClient()
{
return new HttpClient(CreateMockedHttpMessageHandler());
}
private HttpMessageHandler CreateMockedHttpMessageHandler()
{
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(SetupHttpResponseMessage())
.Verifiable();
handlerMock
.Protected()
// Setup the PROTECTED method to mock
.Setup(
"Dispose",
ItExpr.IsAny<bool>()
)
// prepare the expected response of the mocked http call
.Verifiable();
return handlerMock.Object;
}
/// <summary>
/// Set up the desired http response of unit tests
/// </summary>
/// <param name="statusCode"></param>
/// <param name="responseJson"></param>
public void SetHttpResponseMessage(HttpStatusCode statusCode, string responseJson)
{
_httpContentMessage = new HttpResponseMessage
{
StatusCode = statusCode,
Content = new StringContent(responseJson)
};
}
private HttpResponseMessage SetupHttpResponseMessage()
{
if (_httpContentMessage != null)
return _httpContentMessage;
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':1,'value':'1'}]")
}; //return default dummy implementation for easy green testing of HttpClient..
}
}
}
The key is to implement a mocked HttpMessageHandler which is then injected into the real HttpClient class. And also expose a method SetupHttpResponseMessage() allowing unit tests to specify desired http status code and json payload in the response. This is a very basic approach of unit testing HttpClient, so you can get code coverage of your HttpClient and related code in the application layer where your use it.
Make sure you now inject the MockHttpClientFactory into your code and initialize it in your testse setting the SetupHttpResponseMessage. Tests should go a green again despite you actually use the HttpClient (with a mocked HttpMessageHandler) !