Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I unit test a controller action that uses ther Controller.User variable?

I have a controller action that automatically redirects to a new page if the user is already logged in (User.Identity.IsAuthenticated). What is the best way to write a unit test for this scenario to ensure that the redirect takes places?

like image 772
Jedidja Avatar asked Nov 23 '09 14:11

Jedidja


People also ask

How will you unit test a controller?

Unit testing controllers. Set up unit tests of controller actions to focus on the controller's behavior. A controller unit test avoids scenarios such as filters, routing, and model binding. Tests that cover the interactions among components that collectively respond to a request are handled by integration tests.

Should you write unit tests for controllers?

Controller logic can be tested using automated integration tests, separate and distinct from unit tests for individual components. -1: A unit test for a controller could be pointless.

What is mock in unit testing?

Mocking is used in unit tests to replace the return value of a class method or function. This may seem counterintuitive since unit tests are supposed to test the class method or function, but we are replacing all those processing and setting a predefined output.


1 Answers

I've been using the following Mocks with Moq to allow setting up various conditions in my unit tests. First, the HttpContextBase mock:

    public static Mock<HttpContextBase> GetHttpContextMock(bool isLoggedIn)
    {
        var context = new Mock<HttpContextBase>();
        var request = new Mock<HttpRequestBase>();
        var response = new Mock<HttpResponseBase>();
        var session = new Mock<HttpSessionStateBase>();
        var server = new Mock<HttpServerUtilityBase>();
        var principal = AuthenticationAndAuthorization.GetPrincipleMock(isLoggedIn);

        context.SetupGet(c => c.Request).Returns(request.Object);
        context.SetupGet(c => c.Response).Returns(response.Object);
        context.SetupGet(c => c.Session).Returns(session.Object);
        context.SetupGet(c => c.Server).Returns(server.Object);
        context.SetupGet(c => c.User).Returns(principal.Object);

        return context;
    }

Every property that might provide a useful Mock is set up in here. That way, if I need to add something like a referrer, I can just use:

Mock.Get(controller.Request).Setup(s => s.UrlReferrer).Returns(new Uri("http://blah.com/");

The "GetPrincipleMock" method is what sets up the user. It looks like this:

    public static Mock<IPrincipal> GetPrincipleMock(bool isLoggedIn)
    {
        var mock = new Mock<IPrincipal>();

        mock.SetupGet(i => i.Identity).Returns(GetIdentityMock(isLoggedIn).Object);
        mock.Setup(i => i.IsInRole(It.IsAny<string>())).Returns(false);

        return mock;
    }

    public static Mock<IIdentity> GetIdentityMock(bool isLoggedIn)
    {
        var mock = new Mock<IIdentity>();

        mock.SetupGet(i => i.AuthenticationType).Returns(isLoggedIn ? "Mock Identity" : null);
        mock.SetupGet(i => i.IsAuthenticated).Returns(isLoggedIn);
        mock.SetupGet(i => i.Name).Returns(isLoggedIn ? "testuser" : null);

        return mock;
    }

Now, my controller setups in the tests look like this:

var controller = new ProductController();
var httpContext = GetHttpContextMock(true); //logged in, set to false to not be logged in

ControllerContext controllerContext = new ControllerContext(httpContext.Object, new RouteData(), controller);
controller.ControllerContext = controllerContext;

It's a little bit of verbose setup, but once you have everything in place, testing a variety of conditions becomes a lot easier.

like image 123
mkedobbs Avatar answered Nov 11 '22 15:11

mkedobbs