【问题标题】:Should I change the naming convention for my unit tests?我应该更改单元测试的命名约定吗?
【发布时间】:2009-06-19 15:13:36
【问题描述】:
我目前对我的单元测试使用一个简单的约定。如果我有一个名为“EmployeeReader”的类,我创建一个名为“EmployeeReader.Tests”的测试类。然后我在测试类中为该类创建所有测试,名称如下:
- Reading_Valid_Employee_Data_Correctly_Generates_Employee_Object
- Reading_Missing_Employee_Data_Throws_Invalid_Employee_ID_Exception
等等。
我最近一直在阅读有关 BDD 中使用的 different type of naming convention 的信息。我喜欢这个命名的可读性,最终得到一个测试列表,例如:
- When_Reading_Valid_Employee(夹具)
- Employee_Object_Is_Generated(方法)
- Employee_Has_Correct_ID(方法)
- When_Reading_Missing_Employee(夹具)
- An_Invalid_Employee_ID_Exception_Is_Thrown(方法)
等等。
有人用过这两种命名方式吗?您能否提供任何建议、好处、缺点、陷阱等来帮助我决定是否为我的下一个项目切换?
【问题讨论】:
标签:
unit-testing
tdd
naming-conventions
bdd
【解决方案1】:
我一直使用的命名约定是:
functionName_shouldDoThis_whenThisIsTheSituation
例如,这些是堆栈的“pop”函数的一些测试名称
pop_shouldThrowEmptyStackException_whenTheStackIsEmpty
pop_shouldReturnTheObjectOnTheTopOfTheStack_whenThereIsAnObjectOnTheStack
【解决方案2】:
您的第二个示例(每个逻辑“任务”都有一个夹具,而不是每个类都有一个夹具)的优点是您可以为每个任务设置不同的 SetUp 和 TearDown 逻辑,从而简化您的各个测试方法并使它们更多可读。
您无需将其中一个作为标准。我们混合使用这两种方法,具体取决于我们必须为每个类测试多少不同的“任务”。
【解决方案3】:
我觉得第二种更好,因为它使您的单元测试对其他人更具可读性,因为长行会使代码看起来更难阅读或更难浏览。如果您仍然对测试的作用感到不明确,您可以添加 cmets 来澄清这一点。
【解决方案4】:
您引用的第二个命名约定背后的部分原因是您正在同时创建测试和行为规范。您确定事情发生的背景以及在该背景下实际应该发生的事情。 (根据我的经验,观察/测试方法通常以“should_”开头,因此您会得到标准的“When_the_invoicing_system_is_told_to_email_the_client”、“should_initiate_connection_to_mail_server”格式。)
有些工具可以反映您的测试装置并输出格式良好的 html 规格表,去除下划线。您最终会得到与实际代码同步的可读文档(只要您的测试覆盖率高且准确)。
根据您正在处理的故事/功能/子系统,这些规范可以向非程序员利益相关者展示和理解,以进行验证和反馈,这是敏捷和 BDD 的核心。
【解决方案5】:
我使用第二种方法,它确实有助于描述您的软件应该做什么。我还使用嵌套类来描述更详细的上下文。
本质上,测试类是上下文,可以嵌套,方法都是一行断言。例如,
public class MyClassSpecification
{
protected MyClass instance = new MyClass();
public class When_foobar_is_42 : MyClassSpecification
{
public When_foobar_is_42() {
this.instance.SetFoobar( 42 );
}
public class GetAnswer : When_foobar_is_42
{
private Int32 result;
public GetAnswer() {
this.result = this.GetAnswer();
}
public void should_return_42() {
Assert.AreEqual( 42, result );
}
}
}
}
这将在我的测试运行器中为我提供以下输出:
MyClassSpecification+When_foobar_is_42+GetAnswer
should_return_42
【解决方案6】:
我一直在沿着您在问题中描述的两条道路以及其他一些道路...您的第一个替代方案非常简单明了,并且对于大多数人来说很容易理解。我个人更喜欢 BDD 样式(您的第二个示例),因为它隔离了不同的上下文并将对这些上下文的观察分组。唯一真正的缺点是它会生成更多代码,因此在您看到整洁的测试之前,开始这样做会感觉有点麻烦。此外,如果您使用继承来重用夹具设置,您需要一个输出继承链的测试运行程序。考虑一个类“An_empty_stack”,并且您想重用它,然后您再做另一个类:“When_five_is_pushed_on:An_empty_stack”您希望将其作为输出,而不仅仅是“When_five_is_pushed_on”。如果您的测试运行程序不支持这一点,您的测试将包含冗余信息,例如:“When_five_is_pushed_on_empty_stack : An_empty_stack”只是为了使输出更好。