Mocking an HttpClient using an HttpClientHandler
Recently, I wrote a .NET client for Open Exchange Rates
, a service to get exchange rates for more than 100 currencies. You can find it on GitHub. The code is pretty simple, it simply calls the API of Open Exchange Rates using an HttpClient
class and deserializes json using Json.NET
. I don't want to show you this code in this post, but how you can add some tests to this kind of code. Yes, you must test your code 😃
To be able to test the code, you need to simulate the server response. You can create an interface and use a mock library to replace the HttpClient
. But I prefer keeping the HttpClient
in the code because it's the main part of the code. So, let's use another way. The HttpClient
has a concept of handlers. Handlers can modify the behavior of the HttpClient. For instance, you can add headers, change the body of the request, or change the result of the request. Here's a schema from the documentation:
HttpClient and HttpClientHandler explanation
Let's create the handler to enable mocking the server response. An handler only has one method: Task<HttpResponseMessage> SendAsync(HttpRequestMessage, CancellationToken)
. To easily mock the class, we need to add another method with 2 arguments, the verb and the url HttpResponseMessage SendAsync(HttpMethod method, string url)
. This method is more convenient to mock. Here's the class:
public abstract class MockHandler : HttpClientHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(SendAsync(request.Method, request.RequestUri.PathAndQuery));
}
public abstract HttpResponseMessage SendAsync(HttpMethod method, string url);
}
Now you can easily create handlers with a mocking framework such as FakeItEasy.
[TestMethod]
public async Task Main()
{
// Create an handler and give the response of the *server*
var handler = A.Fake<MockHandler>(opt => opt.CallsBaseMethods());
A.CallTo(() => handler.SendAsync(HttpMethod.Get, "https://example.com/ping"))
.ReturnsLazily(() => Success("pong"));
// Initialize the client with the handler
using (var client = new HttpClient(handler))
{
// Call the url, the response should be "pong"
var response = await client.GetStringAsync("https://example.com/ping");
Assert.AreEqual("pong", response);
}
}
private static HttpResponseMessage Success(string content)
{
var response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
response.Content = new StringContent(content);
return response;
}
It's very easy to test a class that has an HttpClient
using a custom HttpClientHandler
. This way you don't need to change your code to enable testing it. Handlers also allow lots of nice scenarios, such as retrying requests, adding custom headers, caching, etc.
Do you have a question or a suggestion about this post? Contact me!