命名单元测试类和测试方法的最佳实践是什么?
这在之前的SO中讨论过,在什么是单元测试的一些流行命名约定?
我不知道这是否是一种非常好的方法,但目前在我的测试项目中,我在每个生产类和测试类之间都有一对一的映射,例如Product和ProductTest。
在我的测试类中,我有我正在测试的方法的名称,一个下划线,然后是情况和我期望发生的事情,例如Save_ShouldThrowExceptionWithNullName()。
命名单元测试类和测试方法的最佳实践是什么?
这在之前的SO中讨论过,在什么是单元测试的一些流行命名约定?
我不知道这是否是一种非常好的方法,但目前在我的测试项目中,我在每个生产类和测试类之间都有一对一的映射,例如Product和ProductTest。
在我的测试类中,我有我正在测试的方法的名称,一个下划线,然后是情况和我期望发生的事情,例如Save_ShouldThrowExceptionWithNullName()。
当前回答
Kent Beck建议:
每个“单元”(程序的类)一个测试夹具。测试fixture本身就是类。测试夹具的名称应该是: [您的“单元”名称]测试 测试用例(测试夹具方法)的名称如下: 测试[正在测试的功能]
例如,拥有以下类:
class Person {
int calculateAge() { ... }
// other methods and properties
}
测试夹具应该是:
class PersonTests {
testAgeCalculationWithNoBirthDate() { ... }
// or
testCalculateAge() { ... }
}
其他回答
更新(2021年7月)
距离我最初的答案(将近12年)已经有一段时间了,在这段时间里,最佳实践发生了很大变化。因此,我倾向于更新自己的答案,并为读者提供不同的命名策略。
许多评论和回答指出,我在最初的回答中提出的命名策略不抵抗重构,最终导致名称难以理解,我完全同意这一点。
在过去的几年里,我最终使用了一种更易于理解的命名模式,其中测试名称描述了我们想要测试的内容,如Vladimir Khorikov所描述的那样。
一些例子是:
Add_credit_updates_customer_balance Purchase_without_funds_is_not_possible Add_affiliate_discount
但正如您所看到的,这是一个非常灵活的模式,但最重要的是,阅读名称就知道测试是关于什么,而不包括可能随着时间变化的技术细节。
要命名项目和测试类,我仍然坚持最初的答案模式。
原答案(2009年10月)
我喜欢Roy Osherove的命名策略。是这样的:
[UnitOfWork_StateUnderTest_ExpectedBehavior]
它以结构化的方式包含了方法名所需的所有信息。
工作单元可以小到一个方法、一个类,也可以大到多个类。它应该表示在这个测试用例中要测试的所有东西,并且这些东西都在控制之下。
对于程序集,我使用典型的.Tests结尾,我认为这是相当普遍的,对于类也是如此(以Tests结尾):
[名称的类UnderTestTests]
以前,我使用Fixture作为后缀而不是Tests,但我认为后者更常见,然后我改变了命名策略。
在VS + NUnit中,我通常在项目中创建文件夹,将功能测试分组在一起。然后我创建单元测试fixture类,并以我正在测试的功能类型命名它们。[Test]方法按照Can_add_user_to_domain命名:
- MyUnitTestProject
+ FTPServerTests <- Folder
+ UserManagerTests <- Test Fixture Class
- Can_add_user_to_domain <- Test methods
- Can_delete_user_from_domain
- Can_reset_password
我认为最重要的事情之一是保持命名约定的一致性(并与团队的其他成员一致)。很多时候,我看到在同一个项目中使用了大量不同的约定。
我用“给定时”的概念。 看看这篇短文http://cakebaker.42dh.com/2009/05/28/given-when-then/。本文从BDD的角度描述了这个概念,但是您也可以在TDD中使用它,而不需要进行任何更改。
我喜欢遵循测试的“Should”命名标准,同时以被测单元(即类)命名测试夹具。
为了说明(使用c#和NUnit):
[TestFixture]
public class BankAccountTests
{
[Test]
public void Should_Increase_Balance_When_Deposit_Is_Made()
{
var bankAccount = new BankAccount();
bankAccount.Deposit(100);
Assert.That(bankAccount.Balance, Is.EqualTo(100));
}
}
为什么“应该”?
我发现它迫使测试编写人员用这样一句话来命名测试:“应该[处于某种状态][在动作发生之后/之前/当][发生时]”
是的,到处写“应该”确实有点重复,但正如我所说,它迫使作者以正确的方式思考(所以对新手来说很好)。此外,它通常会产生一个可读的英文测试名称。
更新:
我注意到Jimmy Bogard也是“should”的粉丝,他甚至有一个名为“should”的单元测试库。
更新(4年后…)
For those interested, my approach to naming tests has evolved over the years. One of the issues with the Should pattern I describe above as its not easy to know at a glance which method is under test. For OOP I think it makes more sense to start the test name with the method under test. For a well designed class this should result in readable test method names. I now use a format similar to <method>_Should<expected>_When<condition>. Obviously depending on the context you may want to substitute the Should/When verbs for something more appropriate. Example: Deposit_ShouldIncreaseBalance_WhenGivenPositiveValue()