我的单位测试过多了吗?

Eli*_*eth 7 c# asp.net-mvc unit-testing

我对我的单元测试方法有两个顾虑:

  1. 我在一种测试方法中测试过多吗?
  2. 我的测试方法名称如何反映所有测试期望?

我问自己,当我的方法名称说:ReturnInvalidModelState然后我的另外Asserts两个不正确.至少关于方法名称......

[Test]
public void Create_TemplateAlreadyExists_ReturnInvalidModelState()
{
    // ARRANGE
    TemplateViewModel templateViewModel = new TemplateViewModel { 
        Name = "MyTest" 
    };

    Mock<ITemplateDataProvider> mock1 = new Mock<ITemplateDataProvider>();
    Mock<IMappingEngine> mock2 = new Mock<IMappingEngine>();

    TemplateController controller = 
        new TemplateController(mock1.Object, mock2.Object);
    mock1.Setup(m => m.TemplateExists("MyTest")).Returns(true);
    // Set ModelState.IsValid to false
    controller.ModelState.AddModelError("Name", 
                                        "This name already exists.");

    // ACT
    ActionResult result = controller.Create(templateViewModel);

    // ASSERT
    Assert.IsFalse(controller.ModelState.IsValid);
    Assert.IsInstanceOfType(typeof(PartialViewResult), result);
    Assert.AreEqual(templateViewModel, ((PartialViewResult)result).Model);
}

[HttpPost]
public ActionResult Create(TemplateViewModel templateViewModel)
{
    if (ModelState.IsValid
        && !_templateDataProvider.TemplateExists(templateViewModel.Name))
    {

        Template template = 
            Mapper.Map<TemplateViewModel, Template>(templateViewModel);

        _templateDataProvider.AddTemplate(template);
        return new JsonNetResult(new { success = true });
    }
    ModelState.AddModelError("Name", "This name already exists.");
    return PartialView(templateViewModel);
}
Run Code Online (Sandbox Code Playgroud)

小智 2

是的,我认为你测试的东西太多了。

首先重命名您的测试方法。您的方法签名应描述操作、场景和预期结果。

如果我要重命名你的方法,那么我最终会得到以下结果:

public void Create_DuplicateTemplate_ModelStateIsInvalidAndReturnsPartialViewResultAndPartialViewResultOfTypeTemplateViewModel() 
{ 
}
Run Code Online (Sandbox Code Playgroud)

你的测试涉及三件事,而不是一件。当它失败时,您不会立即知道失败的原因。

考虑将其重构为更小的测试并封装一些排列逻辑,以便可以重用。

编辑:

您在关于具有单个断言的单个测试方法的评论中提出了很好的观点。我同意你的观点,虽然听起来不错,但通常还不够。

假设我有以下操作方法:

[HttpPost]
public ActionResult Register(NewUserViewModel newUser)
{
    if (!ModelState.IsValid)
        return View(newUser);

    var newUserDTO = Mapper.Map<NewUserViewModel, NewUserDTO>(newUser);
    var userDTO = UserManagementService.RegisterUser(newUserDTO);

    var result = Mapper.Map<UserDTO, UserViewModel>(userDTO);

    TempData.Add("RegisteredUser", result);
    return RedirectToAction("RegisterSuccess");
}
Run Code Online (Sandbox Code Playgroud)

我对此方法进行了以下单元测试:

        [TestMethod]
        public void Register_HttpPost_ValidViewModel_ReturnsRegisterSuccess()
        {
            // Arrange
            var autoMapperMock = this.mockRepository.DynamicMock<IMapper>();
            var userManagementServiceMock = this.mockRepository.DynamicMock<IUserManagementService>();

            var invalidRegistrationViewModel = new NewUserViewModel
            {
                LastName = "Lastname",
                FirstName = "Firstname",
                Username = null
            };

            autoMapperMock.Expect(a => a.Map<UserDTO, UserViewModel>(Arg<UserDTO>.Is.Anything)).Repeat.Once().Return(null);
            autoMapperMock.Expect(a => a.Map<NewUserViewModel, NewUserDTO>(Arg<NewUserViewModel>.Is.Anything)).Repeat.Once().Return(null);
            userManagementServiceMock.Expect(s => s.RegisterUser(Arg<NewUserDTO>.Is.Anything)).Repeat.Once();

            autoMapperMock.Replay();

            var controller = new AccountController
            {
                Mapper = autoMapperMock,
                UserManagementService = userManagementServiceMock
            };

            this.mockRepository.ReplayAll();

            // Act
            var result = (RedirectToRouteResult)controller.Register(invalidRegistrationViewModel);

            // Assert
            Assert.IsTrue((string)result.RouteValues["Action"] == "RegisterSuccess");
        }
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我对我的模拟设置了多个期望:

  • 我希望 AutoMapper 被调用两次
  • 我希望 UserManagementService 被调用一次

在测试结束时,我有一个断言来检查用户是否被重定向到正确的路线。

那么我在哪里检查我的断言呢?我创建了另一种方法来确保满足我的期望:

    [TestCleanup]
    public void Cleanup()
    {
        try
        {
            this.mockRepository.VerifyAll();
        }
        finally
        {                
        }
}
Run Code Online (Sandbox Code Playgroud)

所以你是对的,我有三个断言而不是一个,但我以这样的方式构建我的代码,所以看起来我只有一个断言。