Twitter LinkedIn Github

JetBrains

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:

1

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

 

2

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

3

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):

4

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

5

Now, our controllerContext needs the HttpContext property set:

6

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:

7

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

8

Our complete test now looks like this:

9

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:

10

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!