我想测试一个可能返回 null 或不返回 null 的函数
Foo(string input)
。如果输入是 "A"
它应该返回 null。如果是 "B"
,则应返回非空。我在想以下问题:
[TestCase("A", Is.Null)]
[TestCase("B", Is.Not.Null)]
public void TestFoo(string input, Constraint expectedContraint)
{
var result = Foo(input);
Assert.That(result, expectedConstraint);
}
这会失败,因为约束不是
constant expression, typeof expression or array creation expression of an attribute parameter type
。我可以简单地用 TestCaseSource
修复它,但我想知道是否有更好的方法来做到这一点。
一个选项:
[TestCase("A", true)]
[TestCase("B", false)]
public void TestFoo(string input, bool shouldBeNull)
{
var expectedConstraint = shouldBeNull ? Is.Null : Is.Not.Null;
var result = Foo(input);
Assert.That(result, expectedConstraint);
}
但在这种情况下,它会为测试添加逻辑,这是我想避免的。
而且,我感觉
Assert.That(result, expectedConstraint)
的可读性不是最好的。
解决这个问题的一个变体是
if (shouldBeNull) {
Assert.That(result, Is.Null);
} else {
Assert.That(result, Is.Not.Null);
}
但是,这又为测试添加了一些逻辑。
避免逻辑并提高可读性的一种方法是将测试分为两个单独的测试。但在这种情况下,如果测试的设置非常复杂(这就是我想避免添加逻辑的原因),我会重复太多代码。
我认为在大多数情况下,这些解决方案中的任何一个都可以,并且没有太大区别,但我想知道是否有我错过的有趣的 NUnit 功能,或者有一些我可以应用的好的实践来编写测试更干净。谢谢!
我认为这有点固执己见,但这就是我们正在做的事情以及原因:
(不过,我们正在使用 xUnit。但这并不重要,在这里。)
[TestCase("B")]
public void FooShouldReturnNotNull(string input)
{
SomeComplexSetup();
var result = Foo(input);
Assert.That(result, Is.Not.Null);
}
[TestCase("A")]
public void FooShouldReturnNull(string input)
{
SomeComplexSetup();
var result = Foo(input);
Assert.That(result, Is.Null);
}
private void SomeComplexSetup()
{
// Just a stupid dummy for showcase
}
对于每个断言选项,我们都有一个单独的方法。因此,我们可以将测试数据源分成“类”,其中每个类都期望产生各自的结果。这使得哪个输入应该做什么变得非常清楚,而不需要测试代码内部的逻辑(这也可能有错误,我们不想测试测试,对吧?)。
可以通过分解共享代码并调用它来避免可能的 DRY 违规。 但我们不会因此而摔断腿。 一点点重复是可以接受的。如果您需要更改测试,无论如何您都需要谨慎行事。
注意:如果您的测试用例同时运行,在将共享设置分解为线程安全/能够在所有情况下同时运行时,您显然需要注意。