Chandra Sivaraman
Software Engineering Notes

Unit Testing Part 3

Unit Testing Photo by Joshua J. Cotten on Unsplash

Mocking dependencies in Unit Tests

So, in part 1, we had an overview of unit tests and mocking and in part 2, we saw a couple of simple tests. In this post, we’ll look at some examples of mocking dependencies. Consider the C# code below:

using System.Net.Http;
using Newtonsoft.Json.Linq;

namespace UnitTestSamples
{
    public class ActivityGenerator
    {
        public string GetActivity()
        {
            using (var webClient = new HttpClient())
            {
                var uri = "https://www.boredapi.com/api/activity";
                var responseJson = webClient.GetStringAsync(uri).Result;
                if (!string.IsNullOrEmpty(responseJson))
                {
                    dynamic jObj = JObject.Parse(responseJson);
                    var activity = jObj.activity?.ToString();
                    return activity;
                }
            }

            return null;
        }
    }
}

The class ActivityGenerator has a single public method GetActivity that calls an external API to get a random activity (“Pull a harmless prank on one of your friends”, “Create a compost pile” etc.) to add to our already overflowing todo lists. The JSON response looks like:

{
    "activity": "Make a new friend",
    "type": "social",
    "participants": 1,
    "price": 0,
    "link": "",
    "key": "1000000",
    "accessibility": 0
}

The GetActivity method returns the activity property from this JSON.

To write a unit test for this method, we would create a test project, add a test class and add a test method, like this:

using Xunit;

namespace UnitTestSamples.Tests
{
    public class ActivityGeneratorTests
    {
        [Fact]
        public void GetActivity_ValidResponse_ReturnsActivity()
        {
            // Arrange
            var activityGenerator = new ActivityGenerator();

            // Act
            var activity = activityGenerator.GetActivity();

            // Assert
            Assert.True(!string.IsNullOrEmpty(activity));
        }
    }
}

As mentioned in part 2, we use the ASR and AAA patterns used for naming the test method and structuring the test code.

If you run this test, it will return success. However, it would have made a live call to the API. This is not what we want. What if the API is temporarily down, or is still under development, or we want to test for a failure condition? Calling the live API doesn’t help us in any of these scenarios. To add insult to injury, if we have lots of these kind of tests, we’ll be spending significant time waiting for tests to finish.

We can do better by using mocks (See part 1). We’ll need to add a Nuget reference to Moq, a popular mocking framework. To mock or simulate the web API call, we’ll need to “inject” the HttpClient instance into the ActivityGenerator instead of having it created inside the class. Moq works by creating alternative implementations of interfaces. The built-in HttpClient class doesn’t have an interface we can mock. So, we’ll wrap it in an ApiClient class and pass its interface into ActivityGenerator.

ActivityGenerator.cs:

using Newtonsoft.Json.Linq;

namespace UnitTestSamples
{
    public class ActivityGenerator
    {
        private readonly IApiClient _apiClient;

        public ActivityGenerator(IApiClient apiClient)
        {
            _apiClient = apiClient;
        }
        public string GetActivity()
        {
            var uri = "https://www.boredapi.com/api/activity";
            var response = _apiClient.GetAsync(uri).Result;
            var responseJson = response.Content.ReadAsStringAsync().Result;
            if (!string.IsNullOrEmpty(responseJson))
            {
                dynamic jObj = JObject.Parse(responseJson);
                var activity = jObj.activity?.ToString();
                return activity;
            }

            return null;
        }
    }
}

ApiClient.cs:

using System.Net.Http;
using System.Threading.Tasks;

namespace UnitTestSamples
{
    public class ApiClient : IApiClient
    {
        readonly HttpClient _httpClient;

        public ApiClient()
        {
            _httpClient = new HttpClient();
        }

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

IApiClient.cs:

using System.Net.Http;
using System.Threading.Tasks;

namespace UnitTestSamples
{
    public interface IApiClient
    {
        Task<HttpResponseMessage> GetAsync(string url);
    }
}

Now, we can rewrite the test to inject the mock:

using System.Net;
using System.Net.Http;
using Moq;
using Xunit;

namespace UnitTestSamples.Tests
{
    public class ActivityGeneratorTests
    {
        [Fact]
        public void GetActivity_ValidResponse_ReturnsActivity()
        {
            // Arrange

            // This is the json response we want the API to return
            var responseJson = @"{
                ""activity"": ""Make a new friend"",
                ""type"": ""social"",
                ""participants"": 1,
                ""price"": 0,
                ""link"": """",
                ""key"": ""1000000"",
                ""accessibility"": 0
            }";

            // This is the HTTP response message class we want the API to
            // return
            var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK);
            httpResponseMessage.Content = new StringContent(responseJson);
            
            // Here, we setup a mock ApiClient instance and program it to 
            // return our canned HTTP response above.
            var apiClient = Mock.Of<IApiClient>();
            Mock.Get(apiClient)
                .Setup(m => m.GetAsync(It.IsAny<string>()))
                .ReturnsAsync(httpResponseMessage);
            
            // We instantiate the SUT and pass in the mock dependency
            var activityGenerator = new ActivityGenerator(apiClient);

            // Act
            var activity = activityGenerator.GetActivity();

            // Assert
            Assert.True(!string.IsNullOrEmpty(activity));
        }
    }
}

The Mock static class exposes helper methods to create a mock object (Mock.Of<InterfaceToMock>), get a reference to the mock setup class (Mock.Get), setup a method on the interface to mock (Mock.Setup) to return a canned object (Mock.ReturnsAsync). The It static class lets us specify expectations on input parameters. It.IsAny<string>() means any string value is OK.

Now, what if we want to test a scenario where the API call throws an exception? We setup the API to throw a WebException, and test the type of the return to be AggregateException (since this is async code, the underlying exception would be wrapped in an AggregateException), and the type of the base exception to be WebException:

[Fact]
public void GetActivity_APIThrowsException_RaisesException()
{
    // Arrange
    // Here, we setup a mock ApiClient instance and program it to 
    // throw a WebException.
    var apiClient = Mock.Of<IApiClient>();
    Mock.Get(apiClient)
        .Setup(m => m.GetAsync(It.IsAny<string>()))
        .ThrowsAsync(
            new WebException("network error", WebExceptionStatus.ConnectFailure));

    // We instantiate the SUT and pass in the mock dependency
    var activityGenerator = new ActivityGenerator(apiClient);

    // Act
    var ex = Assert.Throws<AggregateException>(()=> activityGenerator.GetActivity());

    // Assert
    Assert.IsType<WebException>(ex.GetBaseException());
}

What if we wanted to test that we handle a 500 error from the API gracefully? We could do it like this:

[Fact]
public void GetActivity_500Response_ReturnsNull()
{
    // Arrange

    // This is the HTTP response message class we want the API to
    // return
    var response = new HttpResponseMessage(HttpStatusCode.InternalServerError);

    // Here, we setup a mock ApiClient instance and program it to 
    // return our canned HTTP response above.
    var apiClient = Mock.Of<IApiClient>();
    Mock.Get(apiClient)
        .Setup(m => m.GetAsync(It.IsAny<string>()))
        .ReturnsAsync(response);

    // We instantiate the SUT and pass in the mock dependency
    var activityGenerator = new ActivityGenerator(apiClient);

    // Act
    var activity = activityGenerator.GetActivity();

    // Assert
    Assert.True(string.IsNullOrEmpty(activity));
}

This is just a small flavor of how mocking frameworks can make our lives easier when it comes to writing unit tests.

Code

https://github.com/cs31415/unit-test-samples

Takeaways

Continue to part 4.