Testing the ASP.Net MVC Module Authorize an attribute to verify redirection to the login page

This will probably be the case when you just need another pair of eyes. I must have missed something, but I can’t understand why this item cannot be checked. I basically try to prevent unauthorized users from accessing the view by marking the controller with the [Authorize] attribute, and I'm trying to verify this using the following code:

[Fact] public void ShouldRedirectToLoginForUnauthenticatedUsers() { var mockControllerContext = new Mock<ControllerContext>() { DefaultValue = DefaultValue.Mock }; var controller = new MyAdminController() {ControllerContext = mockControllerContext.Object}; mockControllerContext.Setup(c => c.HttpContext.Request.IsAuthenticated).Returns(false); var result = controller.Index(); Assert.IsAssignableFrom<RedirectResult>(result); } 

The RedirectResult I'm looking for is some indication that the user is being redirected to the login form, but instead the ViewResult always returns, and when debugging, I see that the Index () method has successfully hit, although the user is not authenticated.

Am I doing something wrong? Testing at the wrong level? Should I better test at route level for this kind of thing?

I know that the [Authorize] attribute works because when I expand the page, the login screen really imposes itself on me, but how can I check this in the test?

The controller and index method is very simple so that I can check the behavior. I have included them for completeness:

 [Authorize] public class MyAdminController : Controller { public ActionResult Index() { return View(); } } 

Any help appreciated ...

+56
asp.net-mvc
Mar 21 '09 at 11:52
source share
4 answers

You are testing at the wrong level. The [Authorize] attribute ensures that the routing mechanism never calls this method for an unauthorized user - RedirectResult will actually come from the route, not from your controller method.

The good news is they already check coverage for this (as part of the source code of the MVC environment), so I would say that you do not need to worry about that; just make sure your controller method does the right thing when it is called, and trust the framework not to call it in the wrong circumstances.

EDIT: if you want to check for an attribute in unit tests, you will need to use reflection to check controller methods as follows. This example will check for the Authorize attribute in the POST ChangePassword method in the "New ASP.NET MVC 2 Project" demo installed with MVC2.

 [TestFixture] public class AccountControllerTests { [Test] public void Verify_ChangePassword_Method_Is_Decorated_With_Authorize_Attribute() { var controller = new AccountController(); var type = controller.GetType(); var methodInfo = type.GetMethod("ChangePassword", new Type[] { typeof(ChangePasswordModel) }); var attributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true); Assert.IsTrue(attributes.Any(), "No AuthorizeAttribute found on ChangePassword(ChangePasswordModel model) method"); } } 
+87
Mar 22 '09 at 10:52
source share

It's good that you can test at the wrong level, but its test makes sense. I mean, if I mark a method with the authorize attribute (Roles = "Superhero"), I don't need a test if I tagged it. What I (I think) want is to verify that the unauthorized user does not have access and that the authorized user is doing.

For an unauthorized user, run the following tests:

 // Arrange var user = SetupUser(isAuthenticated, roles); var controller = SetupController(user); // Act SomeHelper.Invoke(controller => controller.MyAction()); // Assert Assert.AreEqual(401, controller.ControllerContext.HttpContext.Response.StatusCode, "Status Code"); 

Well, it's not easy, and it took me 10 hours, but here it is. I hope someone can take advantage of this or convince me to go to another profession. :) (BTW - I use rhino mock)

 [Test] public void AuthenticatedNotIsUserRole_Should_RedirectToLogin() { // Arrange var mocks = new MockRepository(); var controller = new FriendsController(); var httpContext = FakeHttpContext(mocks, true); controller.ControllerContext = new ControllerContext { Controller = controller, RequestContext = new RequestContext(httpContext, new RouteData()) }; httpContext.User.Expect(u => u.IsInRole("User")).Return(false); mocks.ReplayAll(); // Act var result = controller.ActionInvoker.InvokeAction(controller.ControllerContext, "Index"); var statusCode = httpContext.Response.StatusCode; // Assert Assert.IsTrue(result, "Invoker Result"); Assert.AreEqual(401, statusCode, "Status Code"); mocks.VerifyAll(); } 

Although this is not very useful without this helper function:

 public static HttpContextBase FakeHttpContext(MockRepository mocks, bool isAuthenticated) { var context = mocks.StrictMock<HttpContextBase>(); var request = mocks.StrictMock<HttpRequestBase>(); var response = mocks.StrictMock<HttpResponseBase>(); var session = mocks.StrictMock<HttpSessionStateBase>(); var server = mocks.StrictMock<HttpServerUtilityBase>(); var cachePolicy = mocks.Stub<HttpCachePolicyBase>(); var user = mocks.StrictMock<IPrincipal>(); var identity = mocks.StrictMock<IIdentity>(); var itemDictionary = new Dictionary<object, object>(); identity.Expect(id => id.IsAuthenticated).Return(isAuthenticated); user.Expect(u => u.Identity).Return(identity).Repeat.Any(); context.Expect(c => c.User).PropertyBehavior(); context.User = user; context.Expect(ctx => ctx.Items).Return(itemDictionary).Repeat.Any(); context.Expect(ctx => ctx.Request).Return(request).Repeat.Any(); context.Expect(ctx => ctx.Response).Return(response).Repeat.Any(); context.Expect(ctx => ctx.Session).Return(session).Repeat.Any(); context.Expect(ctx => ctx.Server).Return(server).Repeat.Any(); response.Expect(r => r.Cache).Return(cachePolicy).Repeat.Any(); response.Expect(r => r.StatusCode).PropertyBehavior(); return context; } 

Thus, you will receive confirmation that users who do not have a role do not have access. I tried to write a test to confirm the opposite, but after two hours of work in mvc plumbing, I will leave it for hand testers. (I instructed when I got to the VirtualPathProviderViewEngine.WTF class? I don’t want anything to do VirtualPath or Provider or ViewEngine altogether out of three!)

I am curious why this is so difficult in the supposedly “verifiable” structure.

+20
Mar 10 2018-11-11T00:
source share

Why not just use reflection to look for the [Authorize] attribute in the controller class and / or the action method you are testing? Assuming the framework made sure the attribute was honored, that would be the easiest thing to do.

+3
Apr 12 '09 at 11:08
source share

I disagree with Dylan's answer because “user must be logged in” does not mean that “controller method is annotated with AuthorizeAttribute”

so that the "user should be logged in" when you call the action method, the ASP.NET MVC framework does something like this (just hold on, in the end it will get easier)

 let $filters = All associated filter attributes which implement IAuthorizationFilter let $invoker = instance of type ControllerActionInvoker let $ctrlCtx = instance or mock of type ControllerContext let $actionDesc = instance or mock of type ActionDescriptor let $authzCtx = $invoker.InvokeAuthorizationFilters($ctrlCtx, $filters, $actionDesc); then controller action is authorized when $authzCtx.Result is not null 

It is hard to implement this pseudo script in working C # code. Probably Xania.AspNet.Simulator makes it very easy to set up such a test and perform just this step under the cover. here is an example.

first install the package from nuget (version 1.4.0-beta4 at the time of writing)

PM> install-package Xania.AspNet.Simulator -Pre

Then your test method might look like this (assuming NUnit and FluentAssertions are installed):

 [Test] public void AnonymousUserIsNotAuthorized() { // arrange var action = new ProfileController().Action(c => c.Index()); // act var result = action.GetAuthorizationResult(); // assert result.Should().NotBeNull(); } [Test] public void LoggedInUserIsAuthorized() { // arrange var action = new ProfileController().Action(c => c.Index()) // simulate authenticated user .Authenticate("user1", new []{"role1"}); // act var result = action.GetAuthorizationResult(); // assert result.Should().BeNull(); } 
+1
Sep 19 '15 at 21:08
source share



All Articles