在为以下示例编写单元测试时,最佳做法是使用TestCases(例如在NUnit中),还是编写多个测试来验证功能?
假设您要测试下面的RetrieveContact方法 - 它只是在数组中找到相应的Contact,如果找到则返回结果,否则将返回null。
public class Contact
{
public string Name { get; set; }
}
private static Contact[] Contacts =
{
new Contact() { Name = "Jim" },
new Contact() { Name = "Bob" },
new Contact() { Name = "Tom" }
};
public static Contact RetrieveContact(string name)
{
return Contacts.FirstOrDefault(c => c.Name == name);
}
你会用一种方法测试这个,使用TestCases,如下所示吗?
[TestCase("Bob", "Bob")]
[TestCase(null, "ZZZ")]
public void Test_RetrievesFromContacts(string expectedName, string name)
{
var ret = RetrieveContact(name);
Assert.AreEqual(expectedName, ret?.Name);
}
或者你会写两个单独的测试 - 一个用于有效的联系人,或者一个用于无效的联系人返回null?
[Test]
public void Test_RetrieveValidContact()
{
var ret = RetrieveContact("Bob");
Assert.AreEqual("Bob", ret.Name);
}
[Test]
public void Test_RetrieveInvalidContact()
{
var ret = RetrieveContact("ZZZ");
Assert.AreEqual(null, ret);
}
由于
答案 0 :(得分:1)
JUnit中的约定是每个单元测试应测试一个特定的功能,一个不同的路径通过被测代码。此约定强烈暗示您不应将多个不相关的测试组合到单个测试方法中。
因此,在上面的示例中,您将为以下各项编写一个测试:
理由是:
if firstPathFails then log and move on to second path
等条件性注意:
@Before
方法实现此方法,从而减少每个@Test
方法的大小/可用性。答案 1 :(得分:1)
虽然您使用junit,xunit和nunit标记了帖子,但代码显然是NUnit。已发布的以junit为导向的答案对于junit来说无疑是正确的,但我将专注于您正在使用的框架和语言。
您提供的两个示例在NUnit中完全相同。在参数化方法中,每个测试用例作为独立于另一个测试用例的独立测试运行。每个结果都单独报告。
NUnit的build.gradle
只不过是创建多个测试的语法快捷方式,只有使用的值不同。您使用哪种方法是风格和命名偏好的问题。在我看来,使用TestCaseAttribute
方法对于可能决定添加新数据点的其他程序员来说更具吸引力。
如果在某些情况下您不想将要使用的数据类型作为C#中属性的构造函数参数,则还应考虑使用TestCase
。
我对您的特定用例有一个保留。当“失败”情况只返回null而没有其他副作用时,它工作正常。然而,在大多数现实场景中,失败案例会抛出异常,产生错误报告等。通常,对于失败案例或案例,通常最好采用完全独立的方法。