Blog

Mocking a ControllerContext with authenticated user with Moq for ASP.NET MVC (3)

14-03-2011 by Sergi Papaseit

Whenever you are writing unit tests or BDD-style specs for your ASP.NET MVC controllers (you are, right?) there will come a moment when you actually need to simulate that a user is authenticated to your site.

Even if you’re familiar with mocking, letting the controller think someone has logged in to your site might not be straight forward. Fear no more.

 

Mocking

I’ll be using Moq as my mocking framework. I’ve tried RhinoMocks in the pas but I find it personally to verbose, but what I’ll show you here should be very easily translated to RhinoMocks or any other ol’ mocking framework out there.

 

Failing test

So let’s say the controller looks like this:
 
public class AdminController : Controller
{
    [Authorize]
    public ActionResult ViewProfile()
    {
        // pass the authorized user name to the view
        var username = User.Identity.Name;

        return View(username);
    }
}

If we want to test the ViewProfile action, the unit test will fail:

[Subject("AdminController")]
public class When_the_profile_page_is_requested
{
    static AdminController controller;
    static string username;

    Establish context = () =>
    {
        controller = new AdminController();
        username = "Sergi";
    }

    Because of = () => result = controller.ViewProfile();

    It should_know_the_username = () =>
        result.Model<string>().ShouldEqual("Sergi");
}

I’m using MSpec with James Broome’s Machine.Specifications.MVC extensions for the specs (tests) here. If you’re not familiar with MSpec, don’t panic, just know that the Establish, Because and It correspond to Arrange, Act and Assert of traditional TDD.

But this spec will obviously fail because we’ve marked our action with the Authorize attribute and even if we hadn’t we’re trying to get the logged in username through the controller’s User property, which will be null when no user has logged in.

 

Getting it to pass

So how do we set our controller up?

[Subject("AdminController")]
public class When_the_profile_page_is_requested
{
    static AdminController controller;

    Establish context = () =>
    {
        controller = new AdminController();

        var controllerContext = new Mock<ControllerContext>();

        // Just fake the name of the user we want to
        // "authenticate"
        controllerContext.SetupGet(x => 
            x.HttpContext.User.Identity.Name).Returns("Sergi");

        // And tell the controllerContext that, sure,
        // we've logged in allright...
        controllerContext.SetupGet(x => 
            x.HttpContext.User.Identity.IsAuthenticated)
            .Returns(true);

        controllerContext.SetupGet(x => 
            x.HttpContext.Request.IsAuthenticated)
            .Returns(true);

        // now that we've set up the controllerContext
        // pass it to the controller
        controller.ControllerContext = controllerContext.Object;
        
        // As a bonus, we initialize the Url helper
        // property of the controller as well
        var context = 
            new Mock<HttpContextBase>(MockBehavior.Strict);

        controller.Url = 
            new UrlHelper(new RequestContext(context.Object, 
            new RouteData()), new RouteCollection());
    }

    Because of = () => result = controller.ViewProfile();

    It should_know_the_username = () =>
        result.Model<string>().ShouldEqual("Sergi");
}

A controller exposes the current HttpContext through the ControllerContext property. Here we tell Moq to set up only certain properties of ControllerContext; only the ones we’re going to use.

That’s what the SetupGet method does.
For instance on line 13 we’re saying: “when someone calls ControllerContext.HttpContext.User.Identity.Name, return the value we’re specifying,  “Sergi” in this case”.

The User property on the Controller class actually exposes this same ControllerContext.HttpContext.User object.

When all is set up we need to pass the ControllerContext to the Controller; that’s what happens on line 28. Why are we actually calling controllerContext.Object? Well, if you look where we initialize the controllerContext variable we really create a Moq.Mock<T> object. Mock<T> exposes the real object T through the Object property, which is exactly what out controller needs.

 

I hope this helps. Any questions? Let me know!

Leave a comment

2 comment(s)

gravatar
Ken Pespisa 2011-04-22 20:02

Thank you! I've been trying to wrap my mind around this very concept, and your post just answered all my questions.

gravatar
DavidS 2011-04-25 21:52

Hey Sergi,
A bit off topic but you should definitely check out FakeItEasy which is even simpler to use than Moq.

I even posted a question on SO http://stackoverflow.com/questions/4335624/how-do-i-fake-a-user-log-in-for-unit-testing-purposes-using-fakeiteasy-within-asp to do what you're currently doing.

Even though documentation is a bit thin, the creator of FakeItEasy is a top guy and he'll answer any question very quickly.

Leave a comment