Membership.CreateUser的ASP.NET MVC 3单元测试始终返回MembershipCreateStatus错误

Mon*_*ome 2 c# tdd unit-testing moq asp.net-mvc-3

我正在尝试编写单元测试来创建新用户并验证是否发生了所需的重定向.这是我的Register动作,它是VS模板中几乎开箱即用的代码:

[HttpPost]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        MembershipCreateStatus createStatus;
        Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);

        if (createStatus == MembershipCreateStatus.Success)
        {
            FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
            return RedirectToAction("Index", "Home");
        }
        else
        {
            ModelState.AddModelError(String.Empty, ErrorCodeToString(createStatus));
        }
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}
Run Code Online (Sandbox Code Playgroud)

以下是我的测试,使用Moq.无论我设置什么,我总是从其中一个默认MembershipCreateStatus错误消息中得到错误.例如:

提供的密码无效.请输入有效的密码值

要么

提供的密码检索答案无效

我已经尝试将CreateUser方法更改为仅调用用户名,密码和电子邮件重载,但这并不重要.这就像是在某处执行密码策略的检查.

public void RegisterPost_WithAuthenticatedUser_RedirectsToHomeControllerIfSuccessful()
{
    // Arrange
    var accountController = new AccountController();
    var mockContext = GetMockRequestContext();

    ControllerContext controllerContext = new ControllerContext(mockContext.Object, accountController);
    accountController.ControllerContext = controllerContext;

    RegisterModel registerModel = new RegisterModel() { UserName = "someone", Email = "someone@example.com", Password = "user", ConfirmPassword = "password" };

    // Act
    var result = accountController.Register(registerModel);

    // Assert
    Assert.That(result.RouteData.Values["Controller"], Is.EqualTo("Home"));
    Assert.That(result.RouteData.Values["Action"], Is.EqualTo("Index"));
}
Run Code Online (Sandbox Code Playgroud)

谁能告诉我这里发生了什么?

Sam*_*der 11

静态类/方法问题再次发生!

静态方法Membership.CreateUser是一个隐藏的依赖,因为它看起来你的控制器没有依赖,但实际上它依赖于这个方法,你没有替换(或能够替换)你的测试中的这种依赖,所以你可以控制互动.

你需要做的是明确这个依赖.通过引入一个界面来完成此操作,该界面模拟了成员资格提供者所需的交互(比如称为IMembershipService).

创建一个默认实现,只需委托现有的静态方法,如Membership.CreateUser().在您的控制器中需要在构造函数中使用此接口的实例(或者如果必须,则在默认构造函数中创建默认实现的实例 - 不是首选选项,但是......)

然后在您的测试中创建一个这个接口的模拟并设置所需的期望,并将此模拟传递给您的控制器,并验证它是否符合您的预期.

如果您不使用模拟,那么每次测试运行时,您都将继续在数据库中创建新用户.这可能没问题,如果你每次都重置你的数据库,但从长远来看使用模拟更简单,更快,虽然设置需要更多的努力,因为你必须创建一个无空的arg构造函数控制器并介绍一些接口将隐式依赖项分解为显式依赖项.