Thursday 28 February 2013

Mocking HttpContext using Fakes framework

It is a common problem when unit testing a MVC controller action which uses HttpContext in some capacity. By default the HttpContext.Current returns null during the execution of test case as the execution takes place outside a webserver and hence results in the expected "Object reference not set to an instance of an object" exception.

So how do we avoid it? Well, simply mock it. If you are using .NET 4.5, then you can use the newly introduced Fakes framework by Microsoft. Since System.Web is a framework assembly, we need to create "Shim" behavior for methods or properties that we expect to be used in the controller's action.

For example - assume that "Index" action of HomeController is implemented like following:

        public ActionResult Index()
        {
            ViewBag.Message = System.Web.HttpContext.Current.Request.Url;
            return View();
        }
 
You will need to add Fakes for System.Web assembly which you can do by simply "Right-Click"ing on the System.Web assembly in references of the unit test project and selecting "Add Fakes Assembly" option. Once the fakes implementation is added, the unit test of the action can be written like following:
 
        [TestMethod]
        public void Index()
        {
            using (ShimsContext.Create())
            {
                System.Web.Fakes.ShimHttpContext.AllInstances.RequestGet =
                (e) =>
                {
                    return new System.Web.Fakes.ShimHttpRequest()
                    {
                        UrlGet = () =>
                        {
                            return new Uri(http://localhost/);
                        }
                    };
                };
 
                System.Web.Fakes.ShimHttpContext.CurrentGet =
                    () =>
                    {
                        return new System.Web.Fakes.ShimHttpContext();
                    };
 
                HomeController controller = new HomeController();
                ViewResult result = controller.Index() as ViewResult;
                Assert.AreEqual("http://localhost/", result.ViewBag.Message.ToString());
            }
        }
 
Like they say - Fake it till you make it :)

2 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete