Unit Testing with HttpClient

I've been moving some code away from depending on RestSharp and moving to the new System.Net.Http.HttpClient in .Net 4.5 to improve security (and that's another story). One nice thing about RestSharp is that the RestClient, RestRequest and RestResponse objects are implementations of interfaces: IRestClient, IRestRequest and IRestResponse.

This is good news for unit testing since it means that it makes it relatively easy to implement these as fakes or stubs in test code. The disadvantage is that implementing IRestClient from scratch smells somewhat wastefully verbose since a stub implementation runs to approximately 200 lines of C#.

Moving to HttpClient I was initially concerned that I wouldn't be able to find a seam to exploit for unit testing. Then I noticed one of the constructors for HttpClient takes an HttpMessageHandler object, and delegates all actual network requests to it's SendAsync method.

This means that the only thing I need to be able to unit test HttpClient code is a fake implementation of HttpMessageHandler!

The code below uses a fake implementation with a settable HttpResponseMessage property so that you can simulate different responses without needing anything on the other end of the wire.


// using the default handler sends requests to 
// the interwebs...
HttpClient httpClient = new HttpClient();
var response = httpClient.GetAsync(url).Result;

// using fake handler keeps messages in your tests.
public class FakeHttpMessageHandler : HttpMessageHandler
{
	public FakeHttpMessageHandler()
	{
		this.Response = new HttpResponseMessage
		{
			StatusCode = System.Net.HttpStatusCode.OK
		};
	}

	public HttpResponseMessage Response { get; set; }

	protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
	{
		return Task<HttpResponseMessage>.Factory.StartNew(() =>
		{
			return this.Response;
		});
	}
}

// simulate a 404 response
var handler = new FakeHttpMessageHandler();
handler.Response.StatusCode = System.Net.HttpStatusCode.NotFound;
handler.Response.ReasonPhrase = "Sorry old bean, this url is missing";

HttpClient httpClient = new HttpClient(handler);
var response = httpClient.GetAsync(url).Result;

So that's 200 lines of code against 20. Win!