Модульное тестирование ASP.Net атрибут авторизации MVC для проверки перенаправления на страницу входа
вероятно, это будет случай, когда просто нужна еще одна пара глаз. Должно быть, я что-то упускаю, но я не могу понять, почему такого рода вещи не могут быть проверены на. Я в основном пытаюсь убедиться, что неавторизованные пользователи не могут получить доступ к представлению, пометив контроллер атрибутом [Authorize], и я пытаюсь проверить это, используя следующий код:
[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);
}
RedirectResult, который я ищу, - это своего рода указание на то, что пользователь перенаправляется в форму входа, но вместо этого всегда возвращается ViewResult, и при отладке я вижу, что метод Index() успешно поражен, даже если пользователь не аутентифицирован.
Я делаю что-то неправильно? Тестирование на неправильном уровне? Должен ли я скорее тестировать на уровне маршрута для такого рода вещей?
Я знаю, что атрибут [Authorize] работает, потому что, когда я разворачиваю страницу, экран входа в систему действительно навязывается мне - но как я могу проверьте это в тесте?
метод контроллера и индекса очень прост, так что я могу проверить поведение. Я включил их для полноты картины:
[Authorize]
public class MyAdminController : Controller
{
public ActionResult Index()
{
return View();
}
}
любая помощь ценится...
4 ответов:
вы тестируете на неправильном уровне. Атрибут [Authorize] гарантирует, что маршрут двигатель никогда не будет вызывать этот метод для несанкционированного пользователя - RedirectResult на самом деле будет исходить от маршрута, а не от вашего метода контроллера.
хорошая новость - для этого уже есть тестовое покрытие (как часть исходного кода MVC framework), поэтому я бы сказал, что вам не нужно беспокоиться об этом; просто убедитесь, что ваш метод контроллера делает все правильно , когда он называется, и доверяйте структуре, чтобы не называть его в неправильных обстоятельствах.
изменить: если вы хотите проверить наличие атрибута в модульных тестах, вам нужно будет использовать отражение для проверки методов контроллера следующим образом. В этом примере будет проверено наличие атрибута Authorize в методе ChangePassword POST в 'New ASP.NET MVC 2 Project' демо, который установлен с 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"); } }
Ну, вы можете тестировать на неправильном уровне, но это тест, который имеет смысл. Я имею в виду, если я отмечаю метод с атрибутом authorize(Roles="Superhero"), мне действительно не нужен тест, если я его пометил. То, что я (думаю, что я) хочу, чтобы проверить, что несанкционированный пользователь не имеет доступа и что авторизованный пользователь делает.
для неавторизованного пользователя такой тест:
// 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");Ну, это не просто, и это заняло у меня 10 часов, но вот оно. Я надеюсь, что кто-то может извлеките из этого пользу или убедите меня перейти в другую профессию. :) (Кстати-я использую 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(); }хотя, это не очень полезно без этой вспомогательной функции:
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; }Так что вы получите подтверждение, что пользователи не в роли не имеют доступа. Я попытался написать тест, чтобы подтвердить обратное, но после еще двух часов копания в MVC plumbing я оставлю его ручным тестерам. (Я бросил, когда добрался до класса VirtualPathProviderViewEngine. WTF? Я не хочу ничего делать с VirtualPath или провайдером или ViewEngine много союза трех!)
мне любопытно, почему это так трудно в якобы "тестирования" рамках.
почему бы просто не использовать отражение, чтобы искать
[Authorize]атрибут класса контроллера и / или метода действия, который вы тестируете? Предполагая, что структура действительно гарантирует, что атрибут соблюдается, это было бы проще всего сделать.
Я не согласен с ответом Дилана, потому что "пользователь должен войти в систему" не означает, что "метод контроллера аннотируется атрибутом AuthorizeAttribute"
чтобы убедиться, что "пользователь должен войти в систему" при вызове метода действия, ASP.NET MVC framework делает что-то вроде этого (просто подождите, в конце концов это станет проще)
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трудно реализовать этот псевдо скрипт в рабочем коде C#. Скорее всего,зания.сеть САШ.Симулятор делает его очень простой в настройке тест, как это и выполняет именно эти шаги под крышкой. вот вам пример.
сначала установите пакет из nuget (версия 1.4.0-beta4 на момент написания статьи)
PM > install-package Xania.сеть САШ.Симулятор-Пре
тогда ваш метод тестирования может выглядеть так (при условии, что установлены NUnit и FluentAssertions):
[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(); }
Comments