Testable HttpClient for C#

23 Jul
2011

Unit testing is a method to test and determine that each single unit of testable source code behaves exactly as expected. Unit tests isolates the unit that is under test from the remainder of all source code. Only that particular single unit should be executed when unit test invokes a unit to test and makes assertions to determine the test results.

Your test is not a unit test if your source code invokes another unit when you run a unit test against to a one single unit. It’s integration testing.

You have to write testable code if you want to apply Test Driven Development (TDD) or want to write unit tests at least. If you apply TDD practice “red, green, refactor”, basically if you write your test first and implementation to pass that test you will be forced to write testable code.

The way to write unit tests are using stub and mock objects. Read Martin Fowler’s Mocks Aren’t Stubs article for more information and differences between mock and stub objects.

After this short introduction to unit testing I want to share a code sample.

I wanted to write tests against a single unit which uses web request objects from .NET BCL in a side project of mine. I ended up with a wrapper around HttpWebRequest because HttpWebRequest type was not testable by itself. And in a unit test you can’t invoke another unit so you need to mock it up. Otherwise HttpWebRequest will make an actual Http request, which is not that I want to do. I want to test my unit to determine that this unit can make an valid Http request under given state, or not.

My solution is to create a HttpClient type which wraps .NET BCL HttpWebRequest type.

First let’s create a simple DTO type to return Http response.

using System.Net;
namespace elchi.common
{
    public class HttpClientResponse
    {
        private readonly HttpStatusCode httpStatusCode;
        private readonly string responseBody;

        public HttpClientResponse() { }
        public HttpClientResponse(HttpStatusCode httpStatusCode, string responseBody)
        {
            this.httpStatusCode = httpStatusCode;
            this.responseBody = responseBody;
        }

        public virtual string ResponseBody
        {
            get { return responseBody; }
        }
        public virtual HttpStatusCode StatusCode
        {
            get { return httpStatusCode; }
        }
    }
}

I want to create an interface for my wrapper HttpClient to easily mock new HttpClient type in my unit tests and inject it to the units under test.

namespace elchi.common
{
    public interface IHttpClient
    {
        HttpClientResponse Get();
        HttpClientResponse Put(string requestData);
        HttpClientResponse Post(string requestData);
        string ContentType { get; set; }
        string Method { get; set; }
    }
}

And here is HttpClient implementation. No magic here, it’s just an wrapper class which uses WebRequest and HttpWebRequest.

using System.IO;
using System.Net;
using System.Text;

namespace elchi.common
{
    public class HttpClient : IHttpClient
    {
        private const string GET = "GET";
        private const string POST = "POST";
        private const string PUT = "PUT";
        private const string DELETE = "DELETE";
        private readonly HttpWebRequest httpWebRequest;

        public HttpClient(string url)
            : this((HttpWebRequest)WebRequest.Create(url)) { }

        public HttpClient(HttpWebRequest httpWebRequest)
        {
            this.httpWebRequest = httpWebRequest;
            ContentType = "application/x-www-form-urlencoded";
        }

        #region IHttpClient Members
        public string ContentType { get; set; }
        public HttpClientResponse Get()
        {
            return MakeRequest(GET, null);
        }
        public HttpClientResponse Post(string requestData)
        {
            return MakeRequest(POST, requestData);
        }
        public HttpClientResponse Put(string requestData)
        {
            return MakeRequest(PUT, requestData);
        }
        public string Method
        {
            get { return httpWebRequest.Method; }
            set { httpWebRequest.Method = value; }
        }
        #endregion

        private HttpClientResponse MakeRequest(string method, string requestData)
        {
            httpWebRequest.Method = method;
            httpWebRequest.ContentType = ContentType;
            httpWebRequest.ContentLength = 0;

            SetRequestData(requestData);

            var response = httpWebRequest.GetResponse();
            var responseStream = response.GetResponseStream();
            var responseStreamReader = new StreamReader(responseStream);
            string responseFromServer = responseStreamReader.ReadToEnd();

            responseStreamReader.Close();
            responseStream.Close();
            response.Close();

            return new HttpClientResponse(((HttpWebResponse)response).StatusCode, responseFromServer);
        }

        private void SetRequestData(string requestData)
        {
            if (string.IsNullOrEmpty(requestData)) return;

            var requestDataBuffer = Encoding.Default.GetBytes(requestData);
            httpWebRequest.ContentLength = requestDataBuffer.Length;
            httpWebRequest.GetRequestStream()
                .Write(requestDataBuffer, 0, requestDataBuffer.Length);
        }
    }
}

I will not use HttpWebRequest type directly, I will just use HttpClient in my project . This blog post doesn’t aim to teach how to write testable code but you have to write a testable code to inject HttpClient type as it is needed. Basic rule don’t use new keyword, inject it, use an IoC container like Windsor.

Let’s see an test code that mocks HttpClient to invoke just a single unit that is under test.

using System;
using elchi.common;
using elchi.sub;
using Moq;
using NUnit.Framework;

namespace elchi.tests.sub
{
    [TestFixture]
    public class SubscriberClientTest
    {
        private Mock<IHttpClient> mockHttpClient;
        private Mock<ISubscriptionRepository> mockSubscriptionRepository;
		
        [SetUp]
        public void Setup()
        {
            mockHttpClient = new Mock<IHttpClient>(MockBehavior.Strict);
            mockSubscriptionRepository = new Mock<ISubscriptionRepository>();
        }
		
        [Test]
        public void Subscribe_ShouldSuccessfullySubscribe()
        {
            var subscriptionRequest = new SubscriptionRequest("callbackUri", "topic", "12345", new TimeSpan(1, 0, 0, 0), "secret", "verifyToken");
            var actualRequestBody = "";
            mockHttpClient.Setup(x => x.Post(It.IsAny<string>()))
                          .Callback(delegate(string requestData) { actualRequestBody = requestData; });
                          .Returns(new HttpClientResponse(HttpStatusCode.OK, "response body from mock"));

            var subscriberClient = new SubscriberClient(mockHttpClient.Object, mockSubscriptionRepository.Object);
            subscriberClient.MakeRequest(subscriptionRequest);

            Assert.AreEqual(subscriptionRequest.BuildRequestParameterString(), actualRequestBody);
        }
    }
}

A snippet from SubscriberClient:

using elchi.common;

namespace elchi.sub
{
    public class SubscriberClient
    {
        private readonly IHttpClient httpClient;
        private readonly ISubscriptionRepository subscriptionRepository;

        public SubscriberClient(string hubUrl) : this(new HttpClient(hubUrl), new SubscriptionRepository()) { }

        public SubscriberClient(IHttpClient httpClient, ISubscriptionRepository subscriptionRepository)
        {
            this.httpClient = httpClient;
            this.subscriptionRepository = subscriptionRepository;
        }
 
        public void MakeRequest(SubscriptionRequest subscriptionRequest)
        {
            var requestData = subscriptionRequest.BuildRequestParameterString();
            httpClient.Post(requestData);
            subscriptionRepository.Add(subscriptionRequest);
        }

    }
}

In the above sample mock objects (for IHttpClient and ISubscriptionRepository) get injected to SubscriberClient by constructor. I used moq mocking library for C#. There is a mock setup line in Subscribe_ShouldSuccessfullySubscribe unit test, for more information read documentation in moq site. It basically gets the parameter that is passed to the Post method of IHttpClient mock objects and keeps it in actualRequestBody variable and makes an assertion to determine that correct request body passed to the HttpClient object because this is the responsibility of the single unit that is under test.



Comment Form

top