0

asp.netフォームの承認をテストするためのモッキングを正常にセットアップしましたが、RoleメンバーシップとAuthorize属性で予期しない動作が見られます。具体的には、以下に示すように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 に答える