我的单元测试是否关心太多

我有两个关于我的单元测试方法的问题:

  • 我在一种测试方法中测试过多吗?
  • 我的测试方法名称如何反映所有测试期望?
  • 我问自己什么时候我的方法名称说: 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);
    }
    

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

    首先重命名你的测试方法。 您的方法签名应描述行动,情景和预期结果。

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

    public void Create_DuplicateTemplate_ModelStateIsInvalidAndReturnsPartialViewResultAndPartialViewResultOfTypeTemplateViewModel() 
    { 
    }
    

    你的测试涉及三件事情,而不是一件事情。 当它失败时,你不会马上知道它为什么失败。

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

    编辑:

    您对单一断言的单一测试方法的评论提出了一个很好的观点。 我同意你的看法,就像听起来一样好,但这往往是不够的。

    假设我有以下操作方法:

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

    我对此方法有以下单元测试:

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

    正如你所看到的,我对我的模拟设定了多重期望:

  • 我希望AutoMapper被调用两次
  • 我希望UserManagementService被调用一次
  • 在测试结束时,我有一个断言,检查用户是否被重定向到正确的路线。

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

        [TestCleanup]
        public void Cleanup()
        {
            try
            {
                this.mockRepository.VerifyAll();
            }
            finally
            {                
            }
    }
    

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


    我建议将所有“Arrange”和“Act”代码移到Setup()方法中,然后将剩余的代码拆分为三个测试。 这将使每个单独的测试更容易阅读,并让您给每个测试一个更符合其实际声明的名称。

    private TemplateViewModel _templateViewModel;
    private ITemplateDataProvider _mock2;
    private IMappingEngine _mock2;
    private TemplateController _controller;
    private ActionResult _result;
    
    [Setup]
    public void Setup(){
        // ARRANGE
        _templateViewModel = new TemplateViewModel { Name = "MyTest" };
    
        _mock1 = new Mock<ITemplateDataProvider>();
        _mock2 = new Mock<IMappingEngine>();
    
        _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.");
    
        _result = controller.Create(_templateViewModel);
    }
    
    
    [Test]
    public void Create_TemplateAlreadyExists_ModelStateIsInvalid()
    {
        Assert.IsFalse(_controller.ModelState.IsValid);
    }
    
    
    [Test]
    public void Create_TemplateAlreadyExists_ResultIsPartialViewResult()
    {
        Assert.IsInstanceOfType(typeof(PartialViewResult), _result);
    }
    
    
    [Test]
    public void Create_TemplateAlreadyExists_ResultModelMatchesTemplateModel()
    {
        Assert.AreEqual(_templateViewModel, ((PartialViewResult)_result).Model);
    }
    
    链接地址: http://www.djcxy.com/p/63307.html

    上一篇: Does my unit test care about too much

    下一篇: Android How to calculate network usage packet/data