Thursday, 3 January 2013

Mocking HttpWebRequest

In one of my recent assignments, I had a requirement to call an external web service. As I wanted to isolate calls to the web service from the functionality that handles the response, I was looking to find ways of mocking calls to the web service. My first thoughts were to create an "IHttpRequest" interface and to encapsulate all HttpWebRequest through this interface.

However, since the call to create an HttpRequest requires casting a WebRequest into an HttpWebRequest, using interface to mock calls to HttpRequest wasn't particular. Following is a typical code block of creating HttpRequest (Note the typecast to HttpWebRequest)


        private static HttpWebRequest CreateWebRequest(string url)
        {
            var request = WebRequest.Create(url) as HttpWebRequest;
            request.ContentType = "text/xml;charset=\"utf-8\"";            
            request.Method = "GET";
            request.Timeout = 1000;
            request.Credentials = CredentialCache.DefaultNetworkCredentials;
            return request;
        }

Looking closely at the WebRequest class, I realised that the class is implementing the Factory Method pattern. To create your own factory class, all that is needed to be done is to create a class that implements the IWebRequestCreate interface and register this class to be used for the Urls in your web request calls. 

My testing requirements were to return different HttpStatusCodes, so my factory class looked as follows. Please note that I am using Moq for mocking.


    /// <summary>
    /// A custom implementation of IWebRequestCreate for Web Requests.
    /// </summary>
    /// <summary>A web request creator for unit testing.</summary>
    public class CustomWebRequestCreate : IWebRequestCreate
    {
        /// <summary>
        /// The web request.
        /// </summary>
        private static WebRequest nextRequest;

        /// <summary>
        /// Internally held lock object for multi-threading support.
        /// </summary>
        private static object lockObject = new object();

        /// <summary>
        /// Gets or sets the next request object.
        /// </summary>
        public static WebRequest NextRequest
        {
            get
            {
                return nextRequest;
            }

            set
            {
                lock (lockObject)
                {
                    nextRequest = value;
                }
            }
        }

        /// <summary>
        /// Creates the new instance of the CustomWebRequest.
        /// </summary>
        /// <param name="uri">The given Uri</param>
        /// <returns>An instantiated web request object requesting from the given Uri.</returns>
        public WebRequest Create(Uri uri)
        {
            return nextRequest;
        }

        /// <summary>
        /// Creates a Mock Http Web request
        /// </summary>
        /// <returns>The mocked HttpRequest object</returns>
        public static HttpWebRequest CreateMockHttpWebRequestWithGivenResponseCode(HttpStatusCode httpStatusCode)
        {
            var response = new Mock<HttpWebResponse>(MockBehavior.Loose);
            response.Setup(c => c.StatusCode).Returns(httpStatusCode);

            var request = new Mock<HttpWebRequest>();
            request.Setup(s => s.GetResponse()).Returns(response.Object);
            NextRequest = request.Object;
            return request.Object;
        }
    }

Having got this class, the setup code in my unit tests would look as follows

WebRequest.RegisterPrefix("http://testService.mywebservice.com", new CustomWebRequestCreate());

CustomWebRequestCreate.CreateMockHttpWebRequestWithGivenResponseCode(HttpStatusCode.OK);

Or

WebRequest.RegisterPrefix("http://testService.mywebservice.com", new CustomWebRequestCreate());            CustomWebRequestCreate.CreateMockHttpWebRequestWithGivenResponseCode(HttpStatusCode.Forbidden);

Just makes creating unit tests so much easier for a variety of response codes. You can extend the CustomWebRequest to mock your responses as you like.


Microsoft Fakes and .Net 4.5. 


The WebRequest class is extended in .Net 4.5 to include new CreateHttp(Uri) and CreateHttp(String) methods, each of which returns an HttpWebRequest object. So there is no need to have a cast. Also, the "Shims" functionality in Microsoft Fakes allows to describe the behaviour of .Net classes. So, HttpWebRequest.GetResponse() method can be "Shim'ed" out to return a response of your choice. This follow up post describes how to use fakes to isolate calls to HttpWebRequests.

2 comments:

Matt D said...

Great Stuff! Very similar to http://blog.salamandersoft.co.uk/index.php/2009/10/how-to-mock-httpwebrequest-when-unit-testing/ but I really like the elegance of using Moq in this instance. Also this solution worked for my scenario (writing unit tests, needed a way to mock a response that gets cast to an HttpWebResponse).

masmer said...

Very well explained especially the Mocking part.Thanks