# Using FakeHttpMessageHandler

## Introduction

HTTP calls are one of the most popular ways to do client-to-service or service-to-service communication these days. Most C# developers use `HttpClient` type to make http calls. Using `HttpClient` correctly, requires a good understanding on its internals, but for testing, this knowledge is unavoidable.

In this post, I will look into how the [Goldlight.HttpClientTestSupport](https://github.com/ohanlon/Goldlight.HttpClientTestSupport) library can be used to help writing unit tests against code with HttpClient.

## Why Testing?

Properties of a good unit tests includes *being fast*, *repeatable* and *isolated*. Code that uses HttpClient typically depends on an external service that is being invoked. This invocation makes the unit tests to break all of these properties.

1. Invoking an external service is never fast. A request needs to be serialized, sent over the network, where another service deserializes it, calculates the response which is sent back. This take several magnitudes longer compared to a method call, inherently making tests run significantly longer compared to tests without external service dependencies. In a worst case scenario the request might be timing out, which can increase the duration from a few seconds to minutes.
1. The outcome of a test with external service dependency will not be consistent. When the external service is invoked, it might be down (due to an update rolling out, or hardware issues) or might just return an incorrect result (due to a bug or error in the service). Typically dev/qa/test environments hosting these services are not providing the same level of quality and availability as production environment. Creating a test against such a dependency will make the test brittle. Some test runs might complete successfully, and some might just fail.
1. The key problem with such unit tests is that they are not *isolated*. They depend on another system, and they depend on the state of the other system. This state might change between test runs or even during the execution of the test, by other concurrent tests. We cannot test our *unit of work* independently from the rest of the system.

> Note, I am strictly considering *unit* tests in this post. Integration tests will cover individual units working together in a system. Such tests are less likely to mock a service dependency.

The suggestion today is to create HttpClient instances through [IHttpClientFactory](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests). Injecting this dependency into our *unit under test* might make you believe that the problem is solved. However, the `IHttpClientFactory`'s CreateClient() method still returns an `HttpClient`, so we cannot avoid returning an `HttpClient` typed object when preparing the mock. Using the [Typed Client](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#how-to-use-typed-clients-with-ihttpclientfactory) approach, even this first step is less evident. A developer needs to provide an actual HttpClient dependency to the unit under test. This make the business layer testable, but our typed client remains untested. 

Testing the *edge* of the system, or testing [impure](https://en.wikipedia.org/wiki/Pure_function#Impure_functions) methods is a more challenging exercise than testing pure methods. I already know that [impureim-sandwich](https://blog.ploeh.dk/2020/03/02/impureim-sandwich/) is a good architecture design for such systems, but it does not liberate us from writing tests against of impure layer.

The goal is to test the edge of our system. This might be critical code for the functionality of the application, hence we want to make sure to cover it with tests. The HTTP calls might need to set certain headers or send the content serialized to a given message and encoded into representation (such as XML or JSON). Handling the response message is just as important. Typed clients will check the status code of the response message, read the content of it and deserialize to [POCO](https://en.wikipedia.org/wiki/Plain_old_CLR_object) object, a string or to a stream of bytes. Optionally the response headers may alter the behavior of processing a response. Based on the response code a typed client can decide to log an error/warning, return *empty* (or null) result or throw an exception. 

## Testing HttpClient

The introduction should encourage the reader to write unit tests against code making HTTP calls. Mocking HttpClient is not trivial by just inspecting its implementation. The suggested approach is to create a custom message handler. This suggestion made me in the past to drop custom message handler implementations as private classes around my test codebase. Sometimes a single implementation is shared along multiple projects in the solution, but never across multiple solutions. I have never implemented a custom message handler in its entirety with all the hooks required for testing - only what I needed for the given test, following the [YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it) principal. However, I have recently come across the [Goldlight.HttpClientTestSupport](https://github.com/ohanlon/Goldlight.HttpClientTestSupport) library, which provides a feature rich implementation of a fake http message hander. Using it is fairly simple:

* add the [nuget package](https://www.nuget.org/packages/Goldlight.HttpClientTestSupport): `dotnet add package Goldlight.HttpClientTestSupport`
* after adding the required usings, create a new `FakeHttpMessageHandler` object
* customize its behavior with fluent syntax
* pass it to HttpClient's constructor
* write the rest of the test case

I will create a test against the `NugetClient` class below. This type is written using the *typed client* approach.

```csharp
public class NugetClient
{
    private readonly HttpClient _client;
    public NugetClient(HttpClient client) 
    {
         _client = client; 
    }

    public async Task<NugetResponse> GetDataAsync(string package)
    {
        var request = new HttpRequestMessage(HttpMethod.Get, $"https://api.nuget.org/v3-flatcontainer/{package}/index.json");
        request.Headers.TryAddWithoutValidation("Content-Type", "application/json");
        var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
        response.EnsureSuccessStatusCode();
        using var contentStream = await response.Content.ReadAsStreamAsync();
        var nugetResponse = await JsonSerializer.DeserializeAsync<NugetResponse>(contentStream, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
        return nugetResponse;
    }
}
```

It depends on an `HttpClient` instance. The `GetDataAsync()` method sends an HTTP request to the nuget api for getting the available version of a package. The method makes sure to receive a successful HTTP code in the response, and if so, it deserializes the response content into a `NugetResponse` typed object.

Although it is not a production grade code, it serves the purpose of this post: to write a test against it.

Let's create the simplest test, testing a happy path. Note, that covering all test cases falls out of scope of this post. For this test, I am using [xUnit.net](https://xunit.net/), and I parameterize the test with an input parameter named *version*. This parameter will be used in the fake response message to the HTTP request.

```csharp
[Theory]
[InlineData("1.1.0")]
public async Task WithSingleVersion_GetDataAsync_ReturnsDataWithVersion(string version)
{
    var messageHandler = new FakeHttpMessageHandler()
        .WithExpectedContent(new NugetResponse() { Versions = new List<string>() { version } })
        .WithStatusCode(System.Net.HttpStatusCode.OK);
    var sut = new NugetClient(new HttpClient(messageHandler));

    var response = await sut.GetDataAsync("mypackage");

    Assert.Contains(version, response.Versions);
}
```

In the happy case, I would like to make sure about two things: 'HTTP 200 OK' is returned and the *version* input string is set with the content of the response. The status code can be set with the `WithStatusCode()` method, while the content is set with the `WithExpectedContent()`. Note, that I am passing an actual `NugetResponse` object to the `WithExpectedContent()` method, but there are other overloads to take a more primitive response data too. In this case, the `NugetResponse` object gets serialized to JSON and used as the content of the response. The last step of the arrange section creates a new `HttpClient`, passing the `FakeHttpMessageHandler` object to the constructor. Finally, the system under test object is created by passing the *mocked* `HttpClient` to it. The rest of the test invokes the method under test, and asserts the results.

## Conclusion

Re-use components which makes your life easier. Implementing a custom message handler for the 12<sup>th</sup> time might be a good [Kata](https://en.wikipedia.org/wiki/Kata_(programming)), but definitely avoidable where smart people has already figured out a good implementation. Testing `HttpClient` is not trivial at first sight. So do not spend effort on designing a helper class for tests, but rather improve the critical parts of the application, and re-use everything else given.