我对我的单元测试方法有两个顾虑:
>我在一种测试方法中测试过多吗?
>我的测试方法名称如何反映所有测试期望?
我问自己,当我的方法名称是:ReturnInvalidModelState,然后我的其他两个断言是不正确的.至少关于方法名称……
[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 { } }
所以你是对的,我有三个断言而不是一个断言,但是我以这样的方式构造我的代码,所以看起来我只有一个断言.