我的单元测试是否关心太多
我有两个关于我的单元测试方法的问题:
我问自己什么时候我的方法名称说: 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");
}
正如你所看到的,我对我的模拟设定了多重期望:
在测试结束时,我有一个断言,检查用户是否被重定向到正确的路线。
那么我在哪里检查我的断言? 我创建了另一种方法来确保我的期望得到满足:
[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