首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >单元测试ASP.Net MVC Authorize属性以验证重定向到登录页面

单元测试ASP.Net MVC Authorize属性以验证重定向到登录页面
EN

Stack Overflow用户
提问于 2009-03-21 11:52:39
回答 3查看 36.2K关注 0票数 70

这可能只是一种需要另一双眼睛的情况。我一定是漏掉了什么,但是我不明白为什么这种东西不能被测试。我基本上是想确保未经身份验证的用户无法访问视图,方法是将控制器标记为Authorize属性,并尝试使用以下代码对此进行测试:

代码语言:javascript
复制
[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);
}

我正在寻找的索引是某种类型的指示,表明用户正被重定向到登录表单,但是总是返回一个ViewResult,并且在调试时我可以看到,即使用户没有经过身份验证,也成功地命中了RedirectResult ()方法。

我做错了什么吗?在错误的级别进行测试?我是不是应该在路由级别测试这类东西?

我知道Authorize属性是有效的,因为当我打开页面时,登录屏幕确实是强加给我的--但是我如何在测试中验证这一点呢?

控制器和索引方法非常简单,这样我就可以验证行为。为了完整起见,我将它们包括在内:

代码语言:javascript
复制
[Authorize]
public class MyAdminController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

感谢任何帮助..。

EN

回答 3

Stack Overflow用户

发布于 2011-03-10 11:48:36

你可能在错误的级别上进行测试,但是这个测试是有意义的。我的意思是,如果我用authorize(Roles=“超级英雄”)属性标记一个方法,如果我标记了它,我真的不需要测试。我想要的是测试一个未经授权的用户是否没有访问权限,而一个授权用户是否有访问权限。

对于未经授权的用户,执行如下测试:

代码语言:javascript
复制
// 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个小时,但这就是它。我希望有人能从中受益,或者说服我从事另一种职业。:) (BTW -我使用的是rhino mock)

代码语言:javascript
复制
[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();
}

不过,如果没有这个辅助函数,这就不是很有用了:

代码语言:javascript
复制
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管道之后,我将把它留给手动测试人员。(当我到达VirtualPathProviderViewEngine课程时,我放弃了。见鬼?我不想要任何东西去做VirtualPath、Provider或ViewEngine,而是三者的结合!)

我很好奇为什么这在一个所谓的“可测试”框架中是如此困难。

票数 27
EN

Stack Overflow用户

发布于 2009-04-12 11:08:11

为什么不使用反射来查找控制器类和/或正在测试的操作方法上的[Authorize]属性呢?假设框架确实确保了该属性得到遵守,这将是最容易做的事情。

票数 4
EN

Stack Overflow用户

发布于 2021-02-24 04:31:57

对于.NET框架,我们使用这个类来验证每个MVC和API Controller都有AuthorizeAttribute,并且每个API Controller都应该有一个RoutePrefixAttribute

代码语言:javascript
复制
[TestFixture]
public class TestControllerHasAuthorizeRole
{
    private static IEnumerable<Type> GetChildTypes<T>()
    {
        var types = typeof(Startup).Assembly.GetTypes();
        return types.Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
    }

    [Test]
    public void MvcControllersShouldHaveAuthrorizeAttribute()
    {
        var controllers = GetChildTypes<Controller>();
        foreach (var controller in controllers)
        {
            var authorizeAttribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Mvc.AuthorizeAttribute), true) as System.Web.Mvc.AuthorizeAttribute;
            Assert.IsNotNull(authorizeAttribute, $"MVC-controller {controller.FullName} does not implement AuthorizeAttribute");
        }
    }

    [Test]
    public void ApiControllersShouldHaveAuthorizeAttribute()
    {
        var controllers = GetChildTypes<ApiController>();
        foreach (var controller in controllers)
        {
            var attribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Http.AuthorizeAttribute), true) as System.Web.Http.AuthorizeAttribute;
            Assert.IsNotNull(attribute, $"API-controller {controller.FullName} does not implement AuthorizeAttribute");
        }
    }

    [Test]
    public void ApiControllersShouldHaveRoutePrefixAttribute()
    {
        var controllers = GetChildTypes<ApiController>();
        foreach (var controller in controllers)
        {
            var attribute = Attribute.GetCustomAttribute(controller, typeof(System.Web.Http.RoutePrefixAttribute), true) as System.Web.Http.RoutePrefixAttribute;
            Assert.IsNotNull(attribute, $"API-controller {controller.FullName} does not implement RoutePrefixAttribute");
            Assert.IsTrue(attribute.Prefix.StartsWith("api/", StringComparison.OrdinalIgnoreCase), $"API-controller {controller.FullName} does not have a route prefix that starts with api/");
        }
    }
}

在.NET核心和.NET 5<中,这要容易一些。在这里,MVC Controller继承自Controller,而后者又继承自ControllerBase。Api控制器直接继承自ControllerBase,因此我们可以使用单一的方法测试MVC和API控制器:

代码语言:javascript
复制
public class AuthorizeAttributeTest
{
    private static IEnumerable<Type> GetChildTypes<T>()
    {
        var types = typeof(Startup).Assembly.GetTypes();
        return types.Where(t => t.IsSubclassOf(typeof(T)) && !t.IsAbstract);
    }

    [Fact]
    public void ApiAndMVCControllersShouldHaveAuthorizeAttribute()
    {
        var controllers = GetChildTypes<ControllerBase>();
        foreach (var controller in controllers)
        {
            var attribute = Attribute.GetCustomAttribute(controller, typeof(Microsoft.AspNetCore.Authorization.AuthorizeAttribute), true) as Microsoft.AspNetCore.Authorization.AuthorizeAttribute;
            Assert.NotNull(attribute);
        }
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/669175

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档