Writing unit tests for HttpClient using NUnit and Moq in C#
Tags: dotnet, testing, tutorial, moq
Published: 2020-09-07 16:23:09

You have a class that sends a GET request using **HttpClient** and consumes the response and performs further actions. Now that you have written this class you want to go ahead and write Unit Tests using [NUnit](https://nunit.org/) for this class so that you can make sure the right **URI** is being called with the correct **Request Headers** and **Request Method**. How do you go about this?

Let’s take a look at the demo project that I have created to understand this better. You can find this project on my Github Page [here](https://github.com/chaitanya-suvarna/NUnitTestForHttpClientDemo).

I have created a class called `EmployeeApiClientService` that has a method called `GetEmployeeAsync()` that takes an employeeid as a input parameter, sends a GET request to the employee API with the employeeid in the uri and returns the `Employee` object back.

```
public class EmployeeApiClientService
{
    private readonly HttpClient employeeHttpClient;

    public EmployeeApiClientService(HttpClient httpClient)
    {
        employeeHttpClient = httpClient;
    }

    //environment specific variables should always be set in a seperate config file or database. 
    //For the sake of this example I'm initialising them here.
    public static string testDatabase = "SloughDB";
    public static string environment = "TEST";

    public async Task<Employee> GetEmployeeAsync(int employeeId)
    {
        Employee employee = null;

        //Add headers
        employeeHttpClient.DefaultRequestHeaders.Add("Accept", "application/json");
        employeeHttpClient.DefaultRequestHeaders.Add("tracking-id", Guid.NewGuid().ToString());

        //Conditional Headers
        if (environment == "TEST")
        {
            employeeHttpClient.DefaultRequestHeaders.Add("test-db", testDatabase);
        }

        HttpResponseMessage response = await employeeHttpClient.GetAsync($"http://dummy.restapiexample.com/api/v1/employee/{employeeId}");
        if (response.IsSuccessStatusCode)
        {
            employee = JsonConvert.DeserializeObject<Employee>(await response.Content.ReadAsStringAsync());
        }
        return employee;
    }
}
```

Once you have finished writing your class, you start writing your unit tests but you are stuck. There’s **no interface for the HttpClient class** which you can use to [Moq](https://github.com/moq/moq) and you do not want to hit the actual end-pints during the unit tests. What do you do here?

There are multiple ways to tackle this problem :

**Method 1 : Write a wrapper class for HttpClient class**
This method would require you to write a wrapper class eg. HttpClientWrapper and implement all of HttpClient’s methods in your wrapper class and then use this wrapper class as a dependency instead of HttpClient in your actual class.
Then you can Mock this Wrapper class in your unit tests and verify the request details. For me, this method seemed like too much work and not a neat implementation.

So I looked around a bit and found out that the HttpClient has a constructor overload that takes a **HttpMessageHandler**.

`public HttpClient(HttpMessageHandler handler);`

And that’s how I came to the second method.

**Method 2 : Mock HttpMessageHandler and pass it to your HttpClient class**
HttpMessageHandler has one protected method `SendAsync()` which is the underlying method called by all of HttpClient’s GET/POST/PATCH/PUT Async methods. All we have to do is mock this class and setup SendAsync to accept and return our desired values as per our test cases. We can use **Moq** for this purpose.

Moq is a **Mocking Framework** used in .NET to isolate units to be tested from the underlying dependencies. We can create a Mock for HttpMessageHandler and pass it to the overloaded constructor for HttpClient.

Let’s see how we can implement this.

##**Mock HttpMessageHandler**
We will create a mock object of HttpMessageHandler using Moq and pass it to the HttpClient class constructor and pass this HttpClient object to our EmployeeApiClientService constructor in the Test **SetUp**.

```
EmployeeApiClientService employeeApiClientService;
Mock<HttpMessageHandler> httpMessageHandlerMock;

[SetUp]
public void setUp()
{
    httpMessageHandlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
    HttpClient httpClient = new HttpClient(httpMessageHandlerMock.Object);
    employeeApiClientService = new EmployeeApiClientService(httpClient);
}
```

##**Setup SendAsync method**
Moq does not allow us to directly setup `SendAsync()` method because this method is **protected** in the HttpMessageHandler class and cannot be accessed outside the class.
We can use the **Moq.Protected** api, which gives us some additional methods on the mocked object, where we can access the protected members using their names using the `.Protected()` method.
We will now **Setup** `SendAsync()` method of the mocked HttpMessageHandler object so that it returns StatusCode 200 with Employee object in json format for which we will use the `ReturnsAsync()` method and we’ll make this object verifiable by using `Verify()` method so that we can verify the number of calls to this method, request details etc. in the assertion section.

```
httpMessageHandlerMock.Protected().Setup<Task<HttpResponseMessage>>(
    "SendAsync",
    ItExpr.IsAny<HttpRequestMessage>(),
    ItExpr.IsAny<CancellationToken>()
    ).ReturnsAsync(new HttpResponseMessage()
    {
       StatusCode = HttpStatusCode.OK,
       Content = new StringContent(JsonConvert.SerializeObject(new Employee()), Encoding.UTF8, "application/json")
    }).Verifiable();
```

##**Verify the call to SendAsync**
In our assertion section of the Unit Test we’ll verify that the `SendAsync()` method is called only **once**, it’s called with a **GET** request, the request contains the expected target **uri** and the request contains all the required **headers**.

```
httpMessageHandlerMock.Protected().Verify(
    "SendAsync",
    Times.Exactly(1), 
    ItExpr.Is<HttpRequestMessage>(req =>
    req.Method == HttpMethod.Get  
    && req.RequestUri.ToString() == targetUri // veryfy the RequestUri is as expected
    && req.Headers.GetValues("Accept").FirstOrDefault() == "application/json" 
    && req.Headers.GetValues("tracking-id").FirstOrDefault() != null 
    && environment.Equals("TEST") ? 
                      req.Headers.GetValues("test-db").FirstOrDefault() == testDatabase : 
                      req.Headers.GetValues("test-db").FirstOrDefault() == null 
    ),
    ItExpr.IsAny<CancellationToken>()
    );
```

##**Complete Test Class**
The complete test class with one test case to test the Request is created correctly looks something like this.

```
[TestFixture]
class EmployeeApiClientServiceTests
{
    EmployeeApiClientService employeeApiClientService;
    Mock<HttpMessageHandler> httpMessageHandlerMock;

    //environment specific variables should always be set in a separate config file or database. 
    //For the sake of this example I'm initialising them here.
    string testDatabase = "SloughDB";
    string environment = "TEST";

    [SetUp]
    public void setUp()
    {
        httpMessageHandlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
        HttpClient httpClient = new HttpClient(httpMessageHandlerMock.Object);
        employeeApiClientService = new EmployeeApiClientService(httpClient);
    }


    [Test]
    public async Task GivenICallGetEmployeeAsyncWithValidEmployeeId_ThenTheEmployeeApiIsCalledWithCorrectRequestHeadersAsync()
    {
        //Arrange
        int employeeId = 1;
        string targetUri = $"http://dummy.restapiexample.com/api/v1/employee/{employeeId}";
        //Setup sendAsync method for HttpMessage Handler Mock
        httpMessageHandlerMock.Protected().Setup<Task<HttpResponseMessage>>(
            "SendAsync",
            ItExpr.IsAny<HttpRequestMessage>(),
            ItExpr.IsAny<CancellationToken>()
            )
            .ReturnsAsync(new HttpResponseMessage()
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(JsonConvert.SerializeObject(new Employee()), Encoding.UTF8, "application/json")
            })
            .Verifiable();

        //Act
        var employee = await employeeApiClientService.GetEmployeeAsync(employeeId);

        //Assert
        Assert.IsInstanceOf<Employee>(employee);

        httpMessageHandlerMock.Protected().Verify(
            "SendAsync",
            Times.Exactly(1), // verify number of times SendAsync is called
            ItExpr.Is<HttpRequestMessage>(req =>
            req.Method == HttpMethod.Get  // verify the HttpMethod for request is GET
            && req.RequestUri.ToString() == targetUri // veryfy the RequestUri is as expected
            && req.Headers.GetValues("Accept").FirstOrDefault() == "application/json" //Verify Accept header
            && req.Headers.GetValues("tracking-id").FirstOrDefault() != null //Verify tracking-id header is added
            && environment.Equals("TEST") ? req.Headers.GetValues("test-db").FirstOrDefault() == testDatabase : //Verify test-db header is added only for TEST environment
                                            req.Headers.GetValues("test-db").FirstOrDefault() == null
            ),
            ItExpr.IsAny<CancellationToken>()
            );
    }
}
```

Thus we have seen how we can easily write unit test cases to test **HttpClient** calls to verify various aspects of the requests and how your class processes the responses. This method can also be used for POST/PUT/PATCH requests as all these methods in HttpClient use HttpMessageHandler’s `SendAsync()` method under the hood.

I hope you found this interesting. Thanks for reading!