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?
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With