Модульное тестирование 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();
}
}


любая помощь ценится...

651   4  

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

    Ничего не найдено.