我正在ASP.NET MVC的绿地爱好应用程序上尝试TDD,并开始获得以下测试方法:
[Test]
public void Index_GetRequest_ShouldReturnPopulatedIndexViewModel()
{
var controller = new EmployeeController();
controller.EmployeeService = GetPrePopulatedEmployeeService();
var actionResult = (ViewResult)controller.Index();
var employeeIndexViewModel = (EmployeeIndexViewModel)actionResult.ViewData.Model;
EmployeeDetailsViewModel employeeViewModel = employeeIndexViewModel.Items[0];
Assert.AreEqual(1, employeeViewModel.ID);
Assert.AreEqual("Neil Barnwell", employeeViewModel.Name);
Assert.AreEqual("ABC123", employeeViewModel.PayrollNumber);
}
现在我知道理想情况下测试只会有一个Assert.xxx()
调用,但这是否意味着我应该重构以上内容以使用以下名称分隔测试:
...大多数测试都是重复的代码(因此不止一次被测试并且违反了“快速测试”建议)?这似乎对我来说是极端的,所以如果我和我一样正确,那么“每个测试的一个断言”建议的真实含义是什么?
答案 0 :(得分:3)
对我来说这似乎是极端的,这就是为什么我还要为每个测试编写多个断言。我已经进行了> 500次测试,每次测试只写一个断言就会将其打到至少2500次,我的测试需要10分钟才能运行。
由于良好的休息跑步者(例如Resharper's)可以让你快速看到测试失败的线路,你仍然可以轻松找出测试失败的原因。如果你不介意额外的努力,你也可以添加一个断言描述(“断言工资单号码正确”),这样你甚至可以看到这个甚至没有查看源代码。有了这个,每次测试只有一个断言的理由很少。
答案 1 :(得分:2)
在他的“单位测试艺术”一书中,Roy Osherove谈到了这个问题。他也赞成在单元测试中只测试一个事实,但他指出这并不总是只意味着一个断言。在这种情况下,您正在测试给定GetRequest
Index
方法ShouldReturnPopulatedIndexViewModel
。在我看来,填充的视图模型应该包含一个ID,一个名称,和一个PayrollNumber,因此对此所有这些内容进行断言测试是完全合理的。
但是,如果你真的想要拆分断言(例如,如果你正在测试需要类似设置但在逻辑上不一样的各个方面),那么你可以这样做而不需要太多努力:< / p>
[Test]
public void Index_GetRequest_ShouldReturnPopulatedIndexViewModel()
{
var employeeDetailsViewModel = SetupFor_Index_GetRequest();
Assert.AreEqual(1, employeeDetailsViewModel.ID);
}
[Test]
public void Index_GetRequest_ShouldReturnPopulatedIndexViewModel()
{
var employeeDetailsViewModel = SetupFor_Index_GetRequest();
Assert.AreEqual("Neil Barnwell", employeeDetailsViewModel.Name);
}
[Test]
public void Index_GetRequest_ShouldReturnPopulatedIndexViewModel()
{
var employeeDetailsViewModel = SetupFor_Index_GetRequest();
Assert.AreEqual("ABC123", employeeDetailsViewModel.PayrollNumber);
}
private EmployeeDetailsViewModel SetupFor_Index_GetRequest()
{
var controller = new EmployeeController();
controller.EmployeeService = GetPrePopulatedEmployeeService();
var actionResult = (ViewResult)controller.Index();
var employeeIndexViewModel = (EmployeeIndexViewModel)actionResult.ViewData.Model;
var employeeDetailsViewModel = employeeIndexViewModel.Items[0];
return employeeDetailsViewModel;
}
也可以说,由于这些测试需要相同的设置,因此他们应该获得自己的夹具并使用单个[SetUp]
方法。但是这种方法有一个缺点。它可能导致比实际的真实类更多的单元测试类,这可能是不可取的。
答案 2 :(得分:1)
我使用辅助类来包含断言。这使测试方法保持整洁,并专注于他们实际尝试建立的内容。它看起来像:
public static class MvcAssert
{
public static void IsViewResult(ActionResult actionResult)
{
Assert.IsInstanceOfType<ViewResult>(actionResult);
}
public static void IsViewResult<TModel>(ActionResult actionResult, TModel model)
{
Assert.IsInstanceOfType<ViewResult>(actionResult);
Assert.AreSame(model, ((ViewResult) actionResult).ViewData.Model);
}
public static void IsViewResult<TModel>(ActionResult actionResult, Func<TModel, bool> modelValidator)
where TModel : class
{
Assert.IsInstanceOfType<ViewResult>(actionResult);
Assert.IsTrue(modelValidator(((ViewResult) actionResult).ViewData.Model as TModel));
}
public static void IsRedirectToRouteResult(ActionResult actionResult, string action)
{
var redirectToRouteResult = actionResult as RedirectToRouteResult;
Assert.IsNotNull(redirectToRouteResult);
Assert.AreEqual(action, redirectToRouteResult.RouteValues["action"]);
}
}