Hadi Hariri's Blog

Mocking UserAgent property in ASP.NET MVC with Moq

Tuesday, 31 March 2009 08:54 by hadi

A question popped up today about someone having trouble mocking the UserAgent property of a Controller. There's enough information on the Internet with solutions to this problem, so I'm going to describe how to figure it out if you don't have an Internet connection.

Let's assume we need to test that our Index action in our HomeController displays the UserAgent information:

   1: public ActionResult Index() 
   2: {
   3:     // Don't use ViewData at home kids! This is just a demo
   4:     ViewData["UserAgent"] = Request.UserAgent;
   5:  
   6:     return View();
   7: }

 

Our test would look something like:

   1: [TestMethod]
   2: public void Index() 
   3: {
   4:  
   5:     HomeController controller = new HomeController();
   6:  
   7:     ViewResult result = controller.Index() as ViewResult;
   8:  
   9:     ViewDataDictionary viewData = result.ViewData;
  10:     Assert.AreEqual("Mozilla/5.0 ....", viewData["UserAgent"]);
  11: }

 

If we run this, it will fail with an exception, reason being that Request is null in our test. This makes sense because we're not coming in from a browser, we're just calling a method on a class. Therefore, what we need to do is somehow inject a fake Request in there with some value assigned to the UserAgent. The problem is how....

 

Reflector is your friend 

Now, ASP.NET MVC comes with source code that you can download from Codeplex. Having the source code makes it easy to figure out how things work. But we're assuming that you don't have an Internet connection remember? And you forgot to download the source code. So let's fire up Reflector and see what it shows us.

We open up Reflector and load System.Web.Mvc, and locate the Controller class. Since we're trying to access the Request property of the Controller, that's the first thing we want to look up:

image

 

Hmm, it looks like the Request property is really coming from the HttpContext property. Clicking on that gives us:

 

image

 

We see that the HttpContext is being obtained from the ControllerContext. So let's continue to drill down to ControllerContext:

 

image

 

Aha! Looks like we've reached the end. ControllerContext is a read-write property. This means that if we provide a fake ControllerContext in our test, one that contains a fake HttpContext which in turn contains a fake Request with our UserAgent, we're all set.

Working our way backwards

I'm going to use Moq as my mocking framework, but you can use any other framework, such as RhinoMocks.  Instead of trying to declare a whole bunch of mocks first, in a true TDD fashion let's work our way backwards. First thing we need is a mock ControllerContext. In Moq, the actual mocked object is accessed via the Object property (I'm using screenshots so you can better contemplate the process):

image

As we can see, we've added a controllerContext mock object and so the next step is to create this object:

image

Now, our controllerContext needs the HttpContext property set:

image

The lines boxed in red are what we've added new. Now that we have our mocked ControllerContext and our HttpContext, the only thing left is to setup the UserAgent property of our Request. But to do that, we need another mock, the HttpRequest object:

image

We therefore create the new mock object and setup the UserAgent property:

image

Our complete test now looks like this:

image

And running it should give us a successful pass!

Refactoring

Now that we've understood exactly what's required, let's see if we can clean up the test a bit:

image

See what's happened? We've removed the mock objects for both the HttpRequest and the HttpContext. The reason we can do this is because of how Moq works, creating proxies for reference types. Therefore, HttpContext is actually not null, and nor is Request even though we haven't specifically created a mock for them. This allows us to easily access the UserAgent property and set it to the value required. Saves quite a bit of code

 

Summary

Mocking can sometimes seem complicated, not knowing what you're meant to do to get something to work. But all you need to do is take a step back, analyze what is it you're trying to mock and work your way up. Having tools like Reflector make this easier!

Tags:   , ,
Categories:  
Actions:   E-mail | del.icio.us | Permalink | Comments (1) | Comment RSSRSS comment feed

Comments

Comments are closed