asp.net-mvc - ASP.NET MVC Controller 的代码中应该测试什么?

标签 asp.net-mvc unit-testing testing

标题不是很具体,但让我们举个例子。我们有 ASP.NET MVC 操作代码:

[HttpPost]
[ExportModelStateToTempData]
public RedirectToRouteResult ChangePassword(int id, UserChangePasswordVM changePassword)
{
    if (ModelState.IsValid)
    {
        var user = _userService.GetUserByID(id);

        //Checking old password. Administrators can change password of every user, 
        //providing his password instead of user's old password.
        bool oldPasswordIsCorrect = _loginService.CheckPassword(
            CurrentPrincipal.IsInRole(CTRoles.IsAdmin) ?
            CurrentPrincipal.User.UserName : user.UserName,
            changePassword.OldPassword);

        if (oldPasswordIsCorrect)
        {
            TempDataWrapper.Message =
                _userService.ChangePassword(user.UserName, changePassword.NewPassword) ?
                CTRes.PasswordChangedSuccessfully : CTRes.ErrorProcessingRequest;
        }
        else
        {
            ModelStateWrapper.AddModelError("ChangePassword.OldPassword",
                CTRes.CurrentPasswordIsNotValid);
        }
    }

    return RedirectToAction(ControllerActions.Edit, new { id });
}

这是一个简单的方法。它采用用户 ID 和密码更改表单的 View 模型。如果模型状态有效,它将从服务层检索用户并调用函数来检查他的旧密码。管理员不必提供用户的旧密码,他们自己的密码就足够了。如果密码正确,则调用更改用户密码的函数。如果成功或失败,适当的消息将放置在 TempData 中。操作以重定向到用户编辑页面结束,其中包含更改密码的表单并显示所有错误。

我有几个问题:

  • 这段代码中应该测试什么?
  • 代码中的 if 语句很少。您会为每个场景编写测试吗?
  • 您在 Controller 中测试什么?

代码中使用的接口(interface)和类(实现被注入(inject)到构造函数中,但这并不重要):

public interface IModelStateWrapper
{
    void AddModelError(string name, string error);
    bool IsValid { get; }
}

public interface IUserService
{
    User GetUserByID(int id);
    bool ChangePassword(string userName, string newPassword);
}

public interface ILoginService
{
    bool CheckPassword(string userName, string password);
}

public interface ITempDataWrapper
{
    string Message { get; set; }
}

public class UserChangePasswordVM : IValidatableObject
{
    [DataType(DataType.Password)]
    public string OldPassword { get; set; }

    [DataType(DataType.Password)]
    public string NewPassword { get; set; }

    [DataType(DataType.Password)]
    public string NewPasswordConfirmation { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (string.IsNullOrEmpty(NewPassword))
            yield return new ValidationResult(CTRes.PasswordNotEmpty, new[] { "NewPassword" });

        if (string.IsNullOrEmpty(NewPasswordConfirmation))
            yield return new ValidationResult(CTRes.ConfirmPassword, new[] { "NewPasswordConfirmation" });

        if (NewPassword != null)
            if (!NewPassword.Equals(NewPasswordConfirmation))
                yield return new ValidationResult(CTRes.PasswordsDontMatch, new[] { "NewPasswordConfirmation" });
    }
}

最佳答案

单元测试应该简单且易于理解。您应该避免在一次测试中测试很多场景。因为如果某个断言失败,那么您不知道接下来的断言会发生什么。单元测试是一种文档。您应该将较大的测试分解为较小的测试,这样您就可以在单元测试中获得良好的控制。

我最近开始进行单元测试,特别是针对 ASP.NET MVC 项目。每当我看到 if..else 时,我都会进行两次测试。如果我有这样的 Controller 操作,那么以下是我将编写的单元测试。

<强>1。测试操作结果

这是一个简单的测试,在此测试中,我将检查输出操作结果是否包含操作名称 ControllerActions.Edit 和路由值中的 id。由于无论条件如何,您总是返回具有相同值的相同操作结果,因此一个单元测试就足够了。

<强>2。角色测试

所以在这里我将编写两个单元测试,一个用于admin,另一个用于其余部分。我将为 _loginService 创建一个模拟并设置期望,以便当用户是 admin 时,会使用我设置的值调用 _loginServiceCurrentPrincipal.User.UserName 中。 (CurrentPrincipal 是一个自定义对象吗?我不确定您将如何模拟它)。

在非管理测试中,我将在模拟对象中设置期望,以便将 user.UserName 传递给 _loginService 并具有我期望的值。

在后面的测试中,我必须模拟 _userService 并 stub GetUserByID 方法以返回自定义用户。

<强>3。测试旧密码正确/不正确

这里我将编写两个测试用例。如果旧密码正确,无论我在 TempData 中获取某些值,也不应该出现模型错误,而其他测试则相反。

<强>4。密码修改成功/失败测试

这里我们可能需要两个测试用例来测试密码修改成功或因异常失败时TempData中返回的消息。

关于asp.net-mvc - ASP.NET MVC Controller 的代码中应该测试什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11270136/

相关文章:

asp.net - 如何在 linq to sql 中删除?

jquery - 用户点击后如何渲染部分 View (使用 Razor )?

testing - 如何更改 Rallys 测试文件夹状态

scala - 如何编写依赖 Druid 的集成测试?

testing - GEB 找到第一个可见元素

javascript - 添加列,然后按类别分隔数据

asp.net-mvc - ASP.NET MVC - 从 URL 中删除 Controller 名称

python - 用方法补丁(装饰器)覆盖类补丁

c# - FakeItEasy如何设置多个泛型函数的返回值?

python - 对 API 包装器进行单元测试