简体   繁体   English

ASP.NET MVC 3 单元测试 Membership.CreateUser 总是返回 MembershipCreateStatus 错误

[英]ASP.NET MVC 3 unit tests for Membership.CreateUser always return MembershipCreateStatus error

I'm trying to write a unit test for creating a new user and verifying that the desired redirect occurs.我正在尝试编写一个单元测试来创建一个新用户并验证是否发生了所需的重定向。 Here's my Register action, which is pretty much out-of-the-box code from a VS template:这是我的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);
}

And the following is my test, using Moq.以下是我的测试,使用起订量。 No matter what I set, I always get an error from one of the default MembershipCreateStatus error messages.无论我设置什么,我总是从默认的MembershipCreateStatus错误消息之一中收到错误消息。 For example:例如:

The password provided is invalid.提供的密码无效。 Please enter a valid password value请输入有效的密码值

or或者

The password retrieval answer provided is invalid提供的密码找回答案无效

I've tried changing the CreateUser method to only call the username, password, and email overload, but it doesn't matter.我尝试将CreateUser方法更改为仅调用用户名、密码和 email 重载,但这没关系。 It's like there's a check somewhere that is enforcing a password policy.就像在某处执行密码策略的检查一样。

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"));
}

Can someone tell me what's going on here?有人能告诉我这里发生了什么吗?

The static class/method issue strikes again! static 类/方法问题再次来袭!

the static method Membership.CreateUser is a hidden dependency here as it looks like your controller does not have a dependency, but actually it is dependent on this method, and you have not replaced (or been able to replace) this dependency in your test so you can control the interaction. static 方法Membership.CreateUser在这里是一个隐藏的依赖项,因为看起来您的 controller 没有依赖项,但实际上它依赖于此方法,并且您尚未在测试中替换(或能够替换)此依赖项,所以你可以控制交互。

What you need to do, is to make this dependency explicit.您需要做的是使这种依赖关系显式化。 Do this by introducing an interface which model the interaction required with the membership provider (say called IMembershipService ).通过引入一个接口来做到这一点,该接口 model 与会员提供者(比如称为IMembershipService )进行交互。

Create a default implementation of this which just delegates to the existing static methods like Membership.CreateUser() .创建一个默认实现,它只委托给现有的 static 方法,如Membership.CreateUser() In your controller require an instance of this interface in the constructor (or create an instance of the default implementation in the default constructor if you must - not the preferred option, but...)在您的 controller 中,需要在构造函数中使用此接口的实例(或者如果必须在默认构造函数中创建默认实现的实例 - 不是首选选项,但是......)

Then in your test create a mock of this interface and set the required expectations, and pass this mock to your controller, and verify it does what you would expect.然后在您的测试中创建此接口的模拟并设置所需的期望,并将此模拟传递给您的 controller,并验证它是否符合您的预期。

If you don't use a mock then you will keep creating new users in your db every time your test runs.如果您不使用模拟,那么每次测试运行时,您都会在数据库中不断创建新用户。 this might be ok, if you are resetting your db every time, but using a mock is simpler and quicker in the long run, although it takes a bit more effort to set up as you have to create a no null arg constructor controller and introduce a few interfaces to break up the implicit dependencies into explicit dependencies.这可能没问题,如果您每次都重置数据库,但从长远来看,使用模拟更简单更快,尽管设置需要更多的努力,因为您必须创建一个没有 null arg 构造函数 controller 并引入一些接口将隐式依赖分解为显式依赖。

The Membership.CreateUser is just a static wrapper, the task is actually delegated to the configured MembershipProvider implementation. Membership.CreateUser只是一个 static 包装器,任务实际上委托给配置的MembershipProvider实现。

If you didn't configure a membership provider the default provider is used that is configured in the machine.config.如果您未配置成员资格提供程序,则使用在 machine.config 中配置的默认提供程序。 For .NET v4.0 it looks like this on my machine:对于 .NET v4.0,它在我的机器上看起来像这样:

<membership>
   <providers>
    <add name="AspNetSqlMembershipProvider" 
             type="System.Web.Security.SqlMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" 
             connectionStringName="LocalSqlServer" 
             enablePasswordRetrieval="false" 
             enablePasswordReset="true" 
             requiresQuestionAndAnswer="true" 
             applicationName="/" 
             requiresUniqueEmail="false" 
             passwordFormat="Hashed" 
             maxInvalidPasswordAttempts="5" 
             minRequiredPasswordLength="7" 
             minRequiredNonalphanumericCharacters="1" 
             passwordAttemptWindow="10" 
             passwordStrengthRegularExpression=""/>
   </providers>
</membership>

Please note the attributes requiresQuestionAndAnswer and minRequiredPasswordLength that are enforcing certain rules by default.请注意默认情况下强制执行某些规则的属性requiresQuestionAndAnswerminRequiredPasswordLength

I guess you do configure your own MembershipProvider in the web application, but most probably you forgot to take over this configuration for the unit test (into the app.config of the test project), and hence these default settings kick in.我猜您确实在 web 应用程序中配置了自己的 MembershipProvider,但很可能您忘记了为单元测试接管此配置(进入测试项目的 app.config),因此这些默认设置开始生效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM