C# 我的单元测试关心的太多了吗
我对我的单元测试方法有两个顾虑:C# 我的单元测试关心的太多了吗,c#,asp.net-mvc,unit-testing,C#,Asp.net Mvc,Unit Testing,我对我的单元测试方法有两个顾虑: 我是否在一种测试方法中测试过多 我的测试方法名称如何反映所有测试期望 我问自己,当我的方法名说:ReturnInvalidModelState时,我的另外两个断言不正确。至少关于方法名 [Test] public void Create_TemplateAlreadyExists_ReturnInvalidModelState() { // ARRANGE TemplateViewModel templateViewModel = new Tem
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_TemplateAlreadyExists_ReturnInvalidModelState()
{
//安排
TemplateViewModel TemplateViewModel=新的TemplateViewModel{
Name=“MyTest”
};
Mock mock1=新Mock();
Mock mock2=新Mock();
TemplateController控制器=
新的TemplateController(mock1.Object、mock2.Object);
mock1.Setup(m=>m.TemplateExists(“MyTest”))。返回(true);
//将ModelState.IsValid设置为false
controller.ModelState.AddModelError(“名称”,
“此名称已存在。”);
//表演
ActionResult=controller.Create(templateViewModel);
//断言
Assert.IsFalse(controller.ModelState.IsValid);
IsInstanceOfType(typeof(PartialViewResult),result);
AreEqual(templateViewModel,((PartialViewResult)result).Model);
}
[HttpPost]
公共操作结果创建(TemplateViewModel TemplateViewModel)
{
如果(ModelState.IsValid)
&&!\u templateDataProvider.TemplateExists(templateViewModel.Name))
{
模板=
Mapper.Map(templateViewModel);
_templateDataProvider.AddTemplate(模板);
返回新的JsonNetResult(new{success=true});
}
AddModelError(“名称”,“此名称已存在”);
返回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");
}
你是对的,我有三个断言而不是一个,但是我以这样的方式构造我的代码,似乎我只有一个断言。我建议将所有的“排列”和“动作”代码移动到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);
}
私有TemplateViewModel\u TemplateViewModel;
私有ITemplateDataProvider_mock2;
私人IMappingEngine_mock2;
私有模板控制器_控制器;
私人诉讼结果(private ActionResult);;
[设置]
公共作废设置(){
//安排
_templateViewModel=new templateViewModel{Name=“MyTest”};
_mock1=新Mock();
_mock2=新Mock();
_控制器=新的TemplateController(_mock1.Object,_mock2.Object);
_mock1.Setup(m=>m.TemplateExists(“MyTest”))。返回(true);
//将ModelState.IsValid设置为false
_controller.ModelState.AddModelError(“名称”,
“此名称已存在。”);
_结果=controller.Create(_templateViewModel);
}
[测试]
public void Create_TemplateAlreadyExists_modelstateinvalid()
{
Assert.IsFalse(_controller.ModelState.IsValid);
}
[测试]
public void Create_TemplateAlreadyExists_ResultIsPartialViewResult()
{
Assert.IsInstanceOfType(typeof(PartialViewResult),\u result);
}
[测试]
public void Create_TemplateAlreadyExists_ResultModelMatchesTemplateModel()
{
AreEqual(_templateViewModel,((PartialViewResult)_result.Model);
}
是否关闭?科沃德…他没有在这里写评论。我建议在每个测试方法中保留一个断言,这有助于避免在一个测试方法中进行太多测试。不建议在一个方法中进行过多的测试或断言。好的,那么我将删除关于modelstate的断言,因为如果modelstate.IsValid=true,则必须有PartialViewResult。您同意吗?然后我仍然留下了2个断言=2个测试。通常,单元测试还涉及模拟,我必须验证方法调用,这也是一个断言。我认为1测试==1断言是远离现实的宗教思想。“你怎么看?”我不同意。当然,会有例外,但在大多数情况下,每个测试一个断言是很好和实用的。它简单地为您提供了更干净、更易于阅读的代码。不过,实现这一点的方法是将尽可能多的代码移出测试本身(例如,请参见我的答案)。以这种方式工作将帮助您避免在同一测试中放置多个断言,以避免重复同一代码太多次。我发现有一个问题:我无法为更新\u Template\u xxx、删除\u Template\u xxx和获取\u Template\u xxx测试运行此设置。当你想做一个Create\u Template\u ModelStateIsValid测试时,你不能运行这个安装程序,因为AddModelError。。。你的建议对我来说似乎不切实际。嗯,这些问题有几种解决办法。可以在不同的文件中创建这些测试,例如,在创建模板.cs
时调用,在获取模板.cs
时调用,在删除模板.cs
时调用。这样,您可以针对每种情况使用不同的设置,并在每种情况下创建多个测试。另一种选择是将大部分设置方法保留在此处,但根据不同情况的需要,将最后一行(或最后两行)移动到每个测试中。你仍然可以减少重复,提高阅读能力
[TestCleanup]
public void Cleanup()
{
try
{
this.mockRepository.VerifyAll();
}
finally
{
}
}
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);
}