tags:

views:

369

answers:

1

Hi

I want to mockup IPrincipal so I did this

public Mock<IPrincipal> Principal { get; set; }

in my setup of my nunit

 Principal = new Mock<IPrincipal>();

So this should be all that I need in my nunit unit test but how about in my actual controller file?

Like how do I set it up?

For example I have a membership.Provider

So what I did was in my controller constructor I did

Provider = Membership.Provider;

So then in my controller I just used Provider.(whatever I need).

I am not sure how to setup the Principal thing in the same way.

+4  A: 

Are you talking about ASP.NET MVC? I guess so.

You have to create an instance of the controller and set its RequestContext. You mock the HttpContext of the RequestContext, and inside this HttpContext, you mock its User property, and set it up to your mocked IPrincipal:

var principal = new Moq.Mock<IPrincipal>();
// ... mock IPrincipal as you wish

var httpContext = new Moq.Mock<HttpContextBase>();
httpContext.Setup(x => x.User).Returns(principal.Object);
// ... mock other httpContext's properties, methods, as needed

var reqContext = new RequestContext(httpContext.Object, new RouteData());

// now create the controller:
var controller = new MyController();
controller.ControllerContext =
    new ControllerContext(reqContext, controller);

Hope this helps.

EDIT:

FYI, the User property on the Controller class comes from the HttpContext object, as you can see here (this is the getter method for the User property, obtained from Reflector -- you can download ASP.NET MVC source code as well):

public IPrincipal User
{
    get
    {
        if (this.HttpContext != null)
        {
            return this.HttpContext.User;
        }
        return null;
    }
}

If you now check the HttpContext property, you will see:

public HttpContextBase HttpContext
{
    get
    {
        if (base.ControllerContext != null)
        {
            return base.ControllerContext.HttpContext;
        }
        return null;
    }
}

So, everything until now was "read only". And we need a way to "inject" a mocked "User". So, we check that we can actually inject a ControllerContext object on the controller through a property. We verify how it is obtaining its "HttpContext" object, to know how to properly mock it up:

public virtual HttpContextBase HttpContext
{
    get
    {
        if (this._httpContext == null)
        {
            this._httpContext = (this._requestContext != null) ? this._requestContext.HttpContext : new EmptyHttpContext();
        }
        return this._httpContext;
    }
    set
    {
        this._httpContext = value;
    }
}

So, here we see that the ControllerContext object obtains it's HttpContext from a RequestContext object. So that might explain what I did above:

  1. Mock an IPrincipal with the data you want,
  2. Mock a HttpContext, and feed it with the IPrincipal,
  3. Mock a RequestContext, and feed it with the HttpContext,
  4. Create an instance of your controller and set it's ControllerContext property to the mocked RequestContext object.

After all this magic, the controller will have no idea that you are calling it without an actual connection being made through a Web Server.

So, you can continue to use your "User" property inside your controller as usual, no changes must be done.

Bruno Reis
How would the actual controller class look?
chobo2
What do you mean? It should be a "normal" controller, with actions. What exactly do you want to test?
Bruno Reis
Well I can't just leave the code in my controller like thisUser.Identity.Name since in my mockup if I am don't setup this and pass it some how into my controller(by property injection or constructor injection) it won't be mocked up. I see your pass something reContext thing into Controller-context thign and the controller but I don't even understand what that is.
chobo2
@chobo2: I'm passing a RequestContext object to the controller. It's from the RequestContext object that the controller obtains the "User" property. So, yes, you can just leave the code in your controller like that. Actually, you shouldn't make your code more ugly because of test (exception made for when you need to mock something like a Linq to SQL DataContext, something that was definetely NOT made to be easily mockable).
Bruno Reis
Hmm I can't even get the code to compile. It can't find httpContextBase. I imported System.Web.
chobo2
Naybe it was just a typo in your message, but it is actually HttpContextBase, with a capital H. Does it work? About this method to mock up, I can guarantee it works: I'm using it in a project right now. Actually I've taken part of a "home-made" tiny "controller mocking framework" I wrote for my project.
Bruno Reis
This is what I get so I am missing something.The type or namespace name 'HttpContextBase' could not be found (are you missing a using directive or an assembly reference?)
chobo2
Ok I fixed that problem now I get.'System.Web.Routing.RequestContext' does not contain a constructor that takes '1' arguments
chobo2
When you have this kind of issue, you must: make sure you have the "using" directive and that you are referencing the assembly on the project using that class. The HttpContextBase is in the System.Web namespace, and the System.Web.Abstractions.dll assembly. You might need to add a reference to System.Web.Abstractions on your test project.
Bruno Reis
About RequestContext: sorry, you must pass a RouteData object to it. I'm correcting the example right now.
Bruno Reis
Also var reqContext does not have an Object property.
chobo2
Take it away! I was not paying much attention when copying the code... sorry about those mistakes!
Bruno Reis
No Sweet it seems to work now. I am still not 100% how "RequestContext" and how "ControllerContext" works but it does work. So can I use this method for all Http stuff?
chobo2
Well, if you want to test your Routes, you still need to tweak this a little bit (you will have to put some data inside the RouteData object). However, if you want to fake a "local" connection, or a "remote" connection, or a user that has a "role", you can do all this by mocking the appropriate property on the appropriate object, among those I've show you. Maybe I will blog about this. If I do so, I will post the link here.
Bruno Reis
Just what I needed, thanks!
jrummell