Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ASP.NET MVC 3成员资格单元测试。CreateUser始终返回MembershipCreateStatus错误_C#_Unit Testing_Asp.net Mvc 3_Tdd_Moq - Fatal编程技术网

C# ASP.NET MVC 3成员资格单元测试。CreateUser始终返回MembershipCreateStatus错误

C# ASP.NET MVC 3成员资格单元测试。CreateUser始终返回MembershipCreateStatus错误,c#,unit-testing,asp.net-mvc-3,tdd,moq,C#,Unit Testing,Asp.net Mvc 3,Tdd,Moq,我正在尝试编写一个单元测试,用于创建新用户并验证是否发生了所需的重定向。下面是我的注册操作,它几乎是VS模板中的现成代码: [HttpPost] public ActionResult Register(RegisterModel model) { if (ModelState.IsValid) { // Attempt to register the user MembershipCreateStatus createStatus;

我正在尝试编写一个单元测试,用于创建新用户并验证是否发生了所需的重定向。下面是我的
注册
操作,它几乎是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);
}
下面是我的测试,使用最小起订量。无论我设置了什么,我总是从默认的
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"));
}

有人能告诉我这里发生了什么吗?

成员资格。CreateUser只是一个静态包装器,任务实际上委托给配置的
成员资格提供程序
实现

如果未配置成员资格提供程序,将使用machine.config中配置的默认提供程序。对于.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>

请注意默认情况下强制执行某些规则的属性
requiresQuestionAndAnswer
minRequiredPasswordLength


我猜您确实在web应用程序中配置了自己的MembershipProvider,但很可能您忘记了为单元测试(进入测试项目的app.config)接管此配置,因此这些默认设置开始生效。

静态类/方法问题再次出现

静态方法
Membership.CreateUser
在这里是一个隐藏的依赖项,因为它看起来您的控制器没有依赖项,但实际上它依赖于此方法,并且您在测试中没有替换(或能够替换)此依赖项,因此您可以控制交互

您需要做的是使这个依赖关系显式化。通过引入一个接口来实现这一点,该接口模拟了与成员资格提供者的交互(比如称为
IMembershipService

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

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


如果不使用mock,那么每次测试运行时都会在数据库中创建新用户。如果每次都重置db,这可能没问题,但从长远来看,使用模拟会更简单、更快,尽管设置起来需要花费更多的精力,因为您必须创建一个无空arg构造函数控制器,并引入一些接口来将隐式依赖项分解为显式依赖项。

您的
GetMockRequestContext()如何设置
look like?@abatishchev我的朋友指出,我正在测试ASP.NET中已经测试过的内部组件,所以我正在改变我的测试方法。但是它看起来是这样的:
Mock-GetMockContext(string-roleName,bool-isInRole,bool-isAuthenticated){var-mockContext=new-Mock();var-fakeUser=new-Mock();fakeUser.Setup(p=>p.isInRole(roleName)).Returns(isInRole);fakeUser.Setup(p=>p.Identity.isAuthenticated).Returns(isAuthenticated);mockContext.Setup(c=>c.HttpContext.User).Returns(fakeUser.Object);}
+1虽然这可能会起作用(并且是一个解决方案),但我认为更好的解决方案是通过引入接口来打破对静态方法的依赖。虽然这种方法可行,但对于单元测试来说不是一个好方法。通过突然添加AspNetSqlMembershipProvider,您的测试将依赖于SQL数据库,这将分别影响测试的性能和可重复性。正如Sam所指出的,使用一个抽象来代替。这也是我的方法,并且实际上是我在所处理的应用程序上所做的。我认为默认的asp.net mvc 3应用程序已经附带了IMembershipService。