ASP.NET MVC控制器方法的单元测试安全性

时间:2014-12-27 19:08:24

标签: c# asp.net asp.net-mvc unit-testing asp.net-mvc-5

是否有任何建议的方法来单独测试一个MVC控制器方法的安全性(在ASP.NET MVC 5中)?例如我有管理员用户和普通用户,我希望进行单元测试,以确保只有管理员才能访问某些页面。 e.g:

    /// <summary>
    /// Initial page for creating a new <see cref="Widget"/>.
    /// </summary>
    /// <returns>
    /// An <see cref="ActionResult"/> used to indicate the view to be
    /// rendered. Guaranteed not to be null or empty.
    /// </returns>
    [HttpGet]
    [Authorize(Roles = "ADMIN")]
    public ActionResult Create()
    {
        return this.View(new CreateWidgetModel());
    }

......单元测试应该是这样的:

using (var userStore = new MyUserStore())
{
    using (var userManager = new MyUserManager(userStore))
    {
        if (await userManager.HasAdministratorUserAsync())
        {
            await userStore.DeleteAsync(await userManager.FindByNameAsync(MyUserManager.AdministratorUserName));
        }

        ApplicationUser adminUser = await userManager.CreateAdministratorUserAsync();

        ClaimsIdentity claimsIdentity = await userManager.CreateIdentityAsync(adminUser, "Forms");

        this.MockHttpContext.SetupGet(x => x.User).Returns(new GenericPrincipal(claimsIdentity, adminUser.Roles.ToArray()));

        List<MethodInfo> methodInfos = this.Sut.GetType().GetMethods().Where(m => m.Name == "Create" && typeof(ActionResult).IsAssignableFrom(m.ReturnType)).ToList();

        Assert.AreEqual(2, methodInfos.Count, "Unexpected number of create methods");

        foreach (var method in methodInfos)
        {
            Assert.IsTrue(method.IsDefined(typeof(ClaimsAuthorizeAttribute)), "No ClaimsAuthorize attribute was placed on the method");

            ClaimsAuthorizeAttribute attribute = method.GetCustomAttribute<ClaimsAuthorizeAttribute>();

            Assert.AreEqual(1, attribute.Claims.Length, "Unexpected number of claims");

            AuthorizationContext authorizationContext = new AuthorizationContext(this.Sut.ControllerContext, new ReflectedActionDescriptor(method, "Create", new ReflectedControllerDescriptor(this.Sut.GetType())));

            attribute.OnAuthorization(authorizationContext);

            Assert.IsNotNull(authorizationContext.Result, "The authorization context result must not be null");
        }
    }
}

我现在遇到的问题是attribute.OnAuthorization失败,出现以下空引用异常:

System.NullReferenceException: Object reference not set to an instance of an object.
    at  System.Web.Mvc.OutputCacheAttribute.GetChildActionFilterFinishCallback(ControllerContext controllerContext)
    at System.Web.Mvc.OutputCacheAttribute.IsChildActionCacheActive(ControllerContext controllerContext)
    at System.Web.Mvc.AuthorizeAttribute.OnAuthorization(AuthorizationContext filterContext)

编辑:看到this post后,我现在得到了一个不同的空引用异常:

System.NullReferenceException: Object reference not set to an instance of an object.
    at System.Web.Mvc.AuthorizeAttribute.OnAuthorization(AuthorizationContext filterContext)
    at MyProject.Support.Security.ClaimsAuthorizeAttribute.OnAuthorization(AuthorizationContext filterContext) in ClaimsAuthorizeAttribute.cs: line 106

编辑:看到OnAuthorization here的源代码后,将其添加到我的测试中解决了我的问题:

        this.MockHttpResponse.Setup(x => x.Cache).Returns(new Mock<HttpCachePolicyBase>().Object);

2 个答案:

答案 0 :(得分:0)

您可以使用反射:

using System.Reflection;
var isAdmin = typeof(Controller).GetMethods().First(x => x.Name == "Create").GetCustomAttribute<Authorize>(false).Roles == "ADMIN";

基本思路是从方法中获取属性并检查角色。

答案 1 :(得分:0)

最简单的方法是使用Xania.AspNet.Simulator 1.2.3-beta(或更高版本)然后您可以编写如下测试:

using NUnit.Framework;
using Xania.AspNet.Simulator;

    [TestCase("ADMIN", true)]
    [TestCase("CUSTOMER", false)]
    public void AdminRoleAuthorizationTest(string roleName, bool isAuthorized)
    {
        // arrange
        var action = new AdminController().Action(c => c.Index()).Authenticate("user1", new[] {roleName});
        // assert
        Assert.AreEqual(isAuthorized, action.Authorize() == null);
    }

有关更多示例,请参阅github上的测试项目