Saturday 5 January 2013

Mocking HttpWebRequest using Microsoft Fakes

In my last post, I detailed the use of  RegisterPrefix method of the WebRequest class to mock calls to HttpWebRequest. I also mentioned that an alternative mechanism to isolate calls to HttpWebRequest was to use the Shims feature in Microsoft Fakes Framework. In this post, I will demonstrate how to do so.

Microsoft Fakes provides two ways of achieving isolation of "code under test" from underlying assemblies namely Stubs and Shims. The Stubs feature generates a dummy class implementing the interface to be stub'ed out allowing you to specify responses made to different method calls in your code. Shims, on the other hand, divert calls within the code of the underlying classes to the code you have written in your definition of the Shim. I wrote a post about Microsoft Fakes sometimes ago.

Coming back to the topic, below is a class that makes a call to the web service.

namespace FakesClass
{
    using System;
    using System.Net;
    /// <summary>
    /// Sample web service client class
    /// </summary>
    public class WebServiceClient
    {
        /// <summary>
        /// Calls a web service with the given URL
        /// </summary>
        /// <param name="url">The web service's URL</param>
        /// <returns>True if the services responds with an OK status code (200). False Otherwise</returns>
        public bool CallWebService(string url)
        {
            var request = CreateWebRequest(url);
            var isValid = true;
            try
            {
                var response = request.GetResponse() as HttpWebResponse;
                isValid = (HttpStatusCode.OK == response.StatusCode);
            }
            catch (Exception ex)
            {
                isValid = false;
            }
            return isValid;
        }

        /// <summary>
        /// Creates an HttpWebRequest object
        /// </summary>
        /// <param name="url">The URL to be called.</param>
        /// <returns>An HttpWebRequest.</returns>
        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;
        }
    }
}
The CallWebService method takes in the url string of the web service to be invoked and returns true or false depending upon whether the HttpResponse has a status code of 200 (HTTP OK) or not. This is a very simple example. You can get your response object to return a response string of whatever your unit test requirements is.

Now that we have a class to unit test, I created a unit test project adding a project reference to the project containing the WebServiceClient class.

Once the unit test project is created, the next step is to generate Fakes class for the HttpWebRequest, HttpWebResponse and WebRequest class. Since, these class reside in the System namespace, the way to generate fake class is to right click on the System Reference and click on the option "Add Fakes Assembly" as shown:


Please note that to date this option (and Microsoft Fakes) is only available for the Ultimate edition of Microsoft Visual Studio 2012.

The "Add Fakes Assembly" option will add the following two assemblies as reference in your unit test project
  • mscorlib.4.0.0.0.Fakes
  • System.4.0.0.0.Fakes
and also add a folder called "Fakes" with the following two files added to it
  • mscorlib.fakes
  • System.fakes




The reason that there are two set of files are added to the solution is because the System namespace is implemented in two assemblies. Since, we are faking only classes in System.Net namespace, we can safely remove the reference mscorlib.4.0.0.0.Fakes and the file mscorlib.fakes. The final references looks as follows
The .fakes file contain details of the files that would be shim'ed or stubbed in the tests. Since, we want to use shim for WebRequest, HttpWebRequest and HttpWebResponse, change the contents of the System.fakes file to the following text.

<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
    <Assembly Name="System" Version="4.0.0.0"/>
    <StubGeneration>
        <Clear/>
    </StubGeneration>
    <ShimGeneration>
        <Clear/>
        <Add FullName="System.Net.WebRequest!"/>
        <Add FullName="System.Net.HttpWebRequest!"/>
        <Add FullName="System.Net.HttpWebResponse!"/>
    </ShimGeneration>
</Fakes>
As you can see, we have cleared out all stub generation and added the three desired classes in the <ShimGenerate\> section. Now, we are all ready to use the generated shims in our unit tests.

The name convention that Microsoft Fakes uses for stubs and shims is Stub{ClassName} & Shim{ClassName} respectively so our shim'ed classes HttpWebRequest class will be called ShimHttpWebRequest class and so on.  The following unit test demonstrates using the Shims.

[TestMethod]
public void TestThatServiceReturnsAForbiddenStatuscode()
{
    using (ShimsContext.Create())
    {
        // Arrange
        var requestShim = new ShimHttpWebRequest();
        ShimWebRequest.CreateString = (uri) => requestShim.Instance;
        requestShim.GetResponse = () => { return new ShimHttpWebResponse() { StatusCodeGet = () => { return HttpStatusCode.Forbidden; } }; };
        var client = new WebServiceClient();
        var url = "http://testwebservice.mywebservice.com";
        var expectedResult = false;
        //Act
        bool actualresult = client.CallWebService(url);
        //Assert
        Assert.AreEqual(expectedResult, actualresult);
    }
}
Please note that the test makes a call to ShimContext.Create and creates objects of type ShimWebRequest and ShimWebResponse. Also, the call to ShimWebRequest returns a ShimHttpWebRequest object.

Updated 23/01/2013: Here is the link to the visual studio solution used in this post and my previous post about mocking HttpWebRequest. The solution contains two unit test projects for testing the same class, one illustrating the use of RegisterPrefix method and the other using the Shim feature of Microsoft Fakes.
 

No comments: