0

我已成功设置模拟以测试 asp.net 表单授权,但我看到角色成员身份和授权属性出现一些意外行为。具体来说,当如下所示调用 ChangePassword 方法时,我希望我会得到一个未经授权的访问重定向到登录屏幕,但是我可以一直通过 ChangePassword 方法并成功接收更改密码。谁能帮助指导我做错了什么?

我已经测试过,在 ChangePassword 方法中调用 IsUserInRole 方法确实可以按预期工作,并且我可以在这种情况下重定向到登录屏幕,但是在我的所有方法中测试这种情况似乎很麻烦。提前致谢。我也尝试过不将用户分配给角色(而不是使用模拟返回 false),但结果是一样的,更改密码成功。

    [TestMethod]
    public void TestProfile()
    {
        string testUserName = "userName", password = "password1", newPassword = "newPassword1";
        var prov = new Mock<IMembershipProvider>();
        prov.Setup(v => v.ValidateUser(testUserName, password)).Returns(true);
        var user = new Mock<MembershipUser>();
        var frmAuth = new Mock<IFormsAuthentication>();
        user.Setup(v => v.ChangePassword(password, newPassword)).Returns(true);
        prov.Setup(v => v.GetUser(testUserName, true)).Returns(user.Object);
        AccountController ctrl = new AccountController(prov.Object, frmAuth.Object);
        var ctrlCtx = new Mock<ControllerContext>();
        ctrlCtx.SetupGet(x => x.HttpContext.User.Identity.Name).Returns(testUserName);
        ctrlCtx.SetupGet(x => x.HttpContext.User.Identity.IsAuthenticated).Returns(true);
        //with this line I would expect to see a redirect to unauhorized
        ctrlCtx.Setup(x => x.HttpContext.User.IsInRole("RoleToTest")).Returns(false);
        ctrl.ControllerContext = ctrlCtx.Object;
        ctrl.Url = Moq.Mock.Of<IUrlHelper>(x => x.IsLocalUrl(It.IsAny<string>()) == true);

        ChangePasswordModel changePass = new ChangePasswordModel() { NewPassword = newPassword, OldPassword = password, ConfirmPassword = password };
        var result = ctrl.ChangePassword(changePass) as ViewResult;
        string expectedViewName = "Logon";
        Assert.AreEqual(result.ViewName, expectedViewName, true /* ignoreCase */,
            string.Format("The expected view '{0}' was not returned. Did change password succeed?", expectedViewName));

    }


    [Authorize(Roles="RoleToTest")]
    [HttpPost]
    public ActionResult ChangePassword(ChangePasswordModel model)
    {
        if (ModelState.IsValid)
        {

            // ChangePassword will throw an exception rather
            // than return false in certain failure scenarios.
            bool changePasswordSucceeded;
            try
            {
                MembershipUser currentUser = membershipProvider.GetUser(User.Identity.Name, true /* userIsOnline */);
                changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
            }
            catch (Exception e)
            {
                Elmah.ErrorSignal.FromCurrentContext().Raise(e);
                changePasswordSucceeded = false;
            }

            if (changePasswordSucceeded)
            {
                return View("ChangePasswordSuccess");
            }
            else
            {
                ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
            }
        }

        // If we got this far, something failed, redisplay form
        return View("ChangePassword", model);
    }
4

1 回答 1

2

具体来说,当如下所示调用 ChangePassword 方法时,我希望我会得到一个未经授权的访问重定向到登录屏幕,但是我可以一直通过 ChangePassword 方法并成功接收更改密码。

你的期望是错误的。诸如此类的属性[Authorize]只是在编译时烘焙到程序集中的元数据。如果没有什么可以解释它们,那么在运行时什么都不会发生。

问题是该[Authorize]属性由 ASP.NET MVC 请求处理管道使用。在您的单元测试中,您正在对控制器操作进行简单调用。而已。没有代码可以理解这个属性。

因此,如果用户未通过身份验证,您无需对控制器操作重定向到登录页面进行单元测试。您必须进行单元测试的是您的控制器操作用 Authorize 属性修饰。当放置在控制器操作上时,此属性将重定向到登录页面这一事实是 ASP.NET MVC 团队在框架开发期间已经进行了广泛的单元测试,因此您无需重复他们的工作。只要相信他们。

因此,典型的单元测试如下所示:

[TestMethod]
public void ChangePassword_Action_Should_Be_Accessible_Only_To_Users_Belonging_To_The_RoleToTest_Role()
{
    Expression<Func<AccountController, ActionResult>> changePwdEx = 
        x => x.ChangePassword(null);
    var authorize = (changePwdEx.Body as MethodCallExpression)
        .Method
        .GetCustomAttributes(typeof(AuthorizeAttribute), true)
        .OfType<AuthorizeAttribute>()
        .First();

    Assert.AreEqual("RoleToTest", authorize.Roles);
}

好的,现在您已经对这个控制器操作进行了单元测试,该控制器操作只能由属于该RoleToTest角色的用户访问。

在您的下一个单元测试中,您假设用户属于该角色(通过模拟相应的类)并且您断言控制器操作的主体按预期执行。

于 2012-05-02T13:16:21.263 回答