我正在使用nunit在C#中编写一些单元测试。请考虑以下代码:
[Test ()]
public void TestCarCost () {
for (int i = 0; i < Examples.exampleCount; i++) {
Car car = new Car(Examples[i]);
Assert.AreEqual (car.getCost (), Examples[i].cost, "Test " + (i + 1) + " failed");
}
}
让我们说例子是一个带有一些静态数据的类,用于测试汽车类型的不同可能输入,你可以看到我试图测试car.getCost()函数中的任何错误。现在在循环中使用它会在某种程度上感觉不对,因为例如当任何断言失败时它总是会将您发送到同一行代码。而且,据我所知,只要[Test()]中的断言失败,nunit会立即终止其余的测试代码。这意味着如果我在循环中拥有所有内容并且断言nr 1失败,我就不会看到其他断言是否失败。明确地编写所有测试本质上是编写copypeasted代码,因此它也感觉不对。在这种情况下,什么是良好做法?是否可以在单元测试中使用大量类似的代码?我缺少一些优雅的解决方案吗?
答案 0 :(得分:7)
NUnit内置了对parameterized tests的支持。如果您的示例数据很简单(即值可以显示在属性中,例如string
),那么您的测试可以使用TestCase
attributes,如下所示:
[TestCase("Example Value 1", ExpectedResult=123.4)]
[TestCase("Example Value 2", ExpectedResult=567.8)]
[TestCase("Example Value 3", ExpectedResult=901.2)]
public void TestCarCost (string exampleValue) {
Car car = new Car(exampleValue);
return car.GetCost();
}
如果您的示例值(实际上是测试输入)无法显示在属性中,您可以使用TestCaseSource attribute来指示将为您提供值的成员:
[TestCaseSource(nameof(Examples))]
public void TestCarCost (ExampleInput exampleValue) {
Car car = new Car(exampleValue);
return car.GetCost();
}
public IEnumerable<ITestCaseData> Examples {
get {
yield return new TestCaseData(new ExampleInput(1,2)).Returns(123.4);
yield return new TestCaseData(new ExampleInput(3,4)).Returns(567.8);
yield return new TestCaseData(new ExampleInput(5,6)).Returns(901.2);
}
}
答案 1 :(得分:5)
最好使用TestCaseAttribute
代替循环。请在以下link中详细了解。
在你的情况下应该是:
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
public void TestCarCost (int id)
{
Car car = new Car(Examples[id]);
Assert.AreEqual (car.getCost (), Examples[id].cost, "Test " + (i + 1) + " failed");
}
在这里,您可以为单个测试方法创建多个测试用例。对于每个测试用例,您可以在TestCase()
属性中提供参数,并将id
作为测试方法传递。
基于id
,您可以访问您的资源`例子[id]。&#39;
我宁愿使用这种方式,因为我可以监控每个案例,并且它不需要额外的逻辑。
此外,如果您在单元测试中添加其他逻辑,则会增加失败的可能性,并且您将依赖于该逻辑的准确性。