单元测试命名最佳实践


564

命名单元测试类和测试方法的最佳实践是什么?

之前,在SO上讨论了哪些流行的单元测试命名约定?

我不知道这是否是一个很好的方法,但是目前在我的测试项目中,我在每个生产类和一个测试类(例如Product和)之间都有一对一的映射ProductTest

在测试类中,我有一些方法,这些方法带有要测试的方法的名称,下划线,然后是情况和我希望发生的事情,例如Save_ShouldThrowExceptionWithNullName()



1
这无法回答您的问题,但值得一读:haacked.com/archive/2012/01/02/structuring-unit-tests.aspx
Robs

3
Google样式指南说:test<MethodUnderTest>_<state>,例如testPop_emptyStack google-styleguide.googlecode.com/svn/trunk/javaguide.html 5.2.3方法名称。如有疑问,请关注Google。
西罗Santilli郝海东冠状病六四事件法轮功

2
@CiroSantilli六四事件法轮功包卓轩接下来的句子说:“没有一种正确的方法来命名测试方法”。去搞清楚。
user2418306 '16

1
甚至几年后,我仍然更喜欢Phil Haack的推荐
pimbrouwers 18/09/17

Answers:


524

我喜欢Roy Osherove的命名策略,如下所示:

[UnitOfWork_StateUnderTest_ExpectedBehavior]

它以结构化方式具有方法名称所需的所有信息。

工作单位可以小到单个方法,一个类,也可以大到多个类。它应该表示在此测试用例中要测试且受控制的所有事物。

对于程序集,我使用典型的.Tests结尾,我认为该结尾很普遍,并且对于类(以结尾Tests)也是如此:

[NameOfTheClassUnderTestTests]

以前我使用Fixture作为后缀而不是Tests,但是我认为后者更为常见,然后我更改了命名策略。


228
对我来说,将方法名称放在测试方法中是没有意义的。如果您重命名方法怎么办?没有重构工具将为您重命名测试。最终,您最终会手动重命名测试方法,或者更可能使用错误命名的测试。就像有评论一样。更糟糕的是,根本不注释代码。
霹雳霹雳州

80
@Peri,我认为这是一个折衷。一方面,您的测试名称可能已过时,另一方面,您无法确定测试所使用的方法。我发现后者经常出现。
Joel McBeth 2012年

15
要添加到Peri的注释中,所有方法都要负责某些操作,例如UpdateManager.Update()。考虑到这一点,我倾向于称呼我的测试WhenUpdating_State_BehaviourWhenUpdating_Behaviour_State。这样,我可以测试类的特定操作,同时避免在测试名称中放入方法名称。但是最重​​要的是,当我看到失败的测试名称时,我必须要知道什么业务逻辑失败了
Ramunas,2012年

8
如果您使用这些工具重构/重命名,Resharper和IntelliJ都可能会找到您的测试方法,并愿意为您重命名。还要尝试在注释中提及方法名称并对其进行更新。
杰夫·马丁

62
好的方法名称通常与方法执行的动作相同。如果必须在方法后命名测试或方法执行的操作之间做出决定,则可能提示您应重命名方法。(尽管并非在所有情况下都如此)
Kaadzia 2013年

121

我喜欢遵循“应”命名标准进行测试,同时在被测单元(即课程)之后命名测试夹具

进行说明(使用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年后...)

对于那些感兴趣的人,多年来我对测试的命名方式已经发展。上面我描述的Should模式的问题之一是,一眼就很难知道正在测试哪种方法。对于OOP,我认为从被测方法开始测试名称更有意义。对于设计良好的类,这应导致可读的测试方法名称。我现在使用类似于的格式<method>_Should<expected>_When<condition>。显然,根据上下文,您可能需要将Should / When动词替换为更合适的内容。例: Deposit_ShouldIncreaseBalance_WhenGivenPositiveValue()


32
假设测试有效,也许甚至更好,更少冗余,只需写一个句子说明其作用即可increasesBalanceWhenDepositIsMade()
hotshot309

3
最近,看到一篇文章提到了类似的命名约定(希望我将其标记为书签)。旨在使测试列表在按测试夹具排序时非常易于阅读。您会看到类似“ BankAccount”的内容,然后在其下(在不同的行上)“ Should_Increase_Balance_When_Deposit_Is_Made”,“ Should_Decrease_Balance_When_Withdrawal_Is_Made”等。读取的内容非常像规范,这与TDD有关。
Simon Tewsi 2013年

找到了这篇文章。在Justin Etheredge的CodeThinked博客中,它开始与Moq 3的模拟—第1部分
Simon Tewsi 2013年

11
我也使用Should and When,但相反。例如,WhenCustomerDoesNotExist_ShouldThrowException()。对我而言,这比应在何时(即在某些情况下应该有一定的预期结果)更有意义。这也适合AAA(安排,行为,声明)...声明位于结尾...而不是开头;-)
bytedev

2
@Schneider:考虑“应该” =“推荐”然后是可选的,我想知道:使用“必须” =“必须”然后是必需/强制性在词汇上会更好吗?例如,RFC使两者有所不同。因此,建议通过或要求通过测试?
blackwizard

79

我喜欢这种命名方式:

OrdersShouldBeCreated();
OrdersWithNoProductsShouldFail();

等等。对于非测试人员来说,这确实是问题所在。


63
但@ hotshot309,他可以使用.NET - .NET大小写约定
王牌

2
@Ace,我完全同意您的看法,并在发布此评论大约一分钟后注意到了这一点。我发誓我看到自己的错误时就删除了它,但是不知何故,我想我没有。对于那个很抱歉。
hotshot309 2012年

3
@CoffeeAddict,因为标识符中的下划线是<del> aberration </ del>,在C#中并非真正惯用语
Sklivvz 2013年

2
我也想避免使用的should,我会更喜欢will这样OrdersWithNoProductsWillFail()
克林

4
@Calin在我看来,使用Will并不是真的很合适,这样做实际上是在错误地告诉读者,测试绝不会失败...如果您Will将来表达一些可能不会发生的事情,错误地使用它Should是更好的选择,因为它表明您想要/希望发生某件事,但是它没有或没有,当测试运行时它会告诉您它是否失败/成功,因此您不能真正暗示事先,这是合乎逻辑的解释,您的是什么?为什么要避免Should
Eyal Solnik '16

51

肯特·贝克Kent Beck)建议:

  • 每个“单元”(程序类别)一个测试夹具。测试装置本身就是类。测试夹具名称应为:

    [name of your 'unit']Tests
    
  • 测试用例(测试夹具方法)的名称如下:

    test[feature being tested]
    

例如,具有以下类别:

class Person {
    int calculateAge() { ... }

    // other methods and properties
}

测试夹具为:

class PersonTests {

    testAgeCalculationWithNoBirthDate() { ... }

    // or

    testCalculateAge() { ... }
}

4
我希望更多的人会遵循这些准则。不久之前,我不得不重命名20多个测试方法,因为它们的名称类似于“ ATest”,“ BasicTest”或“ ErrorTest”。

85
给定类的后缀,'test'的方法前缀不会变得多余吗?
加文·米勒

50
记得肯特写那本书的时候。属性不是发明的。因此,方法名称中的名称Test指示测试框架该方法是测试。自2002
Thomas Jespersen 2010年

14
testCalculateAge ...这是您的测试方法的无意义名称。“测试”是多余的(您是否用“方法”前缀命名所有方法?)。名称的其余部分没有经过测试的条件或预期的条件。CalculateAge是否正在测试该方法?.....谁知道...
bytedev

1
我想补充一点,在使用此策略时,需要文档来指定预期的输出。作为“ test”前缀的旁注;一些单元测试框架需要特定的前缀或后缀才能识别测试。用'Abstract'前缀抽象类不被认为是多余的(因为它是自记录的),那么为什么'Test'不能同样适用呢?
siebz0r

17

类名。对于测试装置名称,我发现“测试”在许多领域的通用语言中非常普遍。例如,在工程领域:StressTest和化妆品领域:SkinTest。很抱歉不同意Kent,但在我的测试装置中使用了“ Test”(StressTestTest?)中令人困惑。

“单元”在域中也经常使用。例如MeasurementUnit。是称为MeasurementUnitTest“ Measurement”或“ MeasurementUnit”的测试的类吗?

因此,我喜欢对所有测试类使用“ Qa”前缀。例如QaSkinTestQaMeasurementUnit。它永远不会与域对象混淆,并且使用前缀而不是后缀意味着所有测试夹具都可以在视觉上一起使用(如果您在测试项目中有伪造品或其他支持类,则很有用)

命名空间。我使用C#工作,并且将测试类与它们正在测试的类放在同一命名空间中。比拥有单独的测试名称空间更方便。当然,测试类在另一个项目中。

测试方法名称。我喜欢将我的方法命名为WhenXXX_ExpectYYY。它使前提条件变得清晰,并有助于自动化文档编制(例如TestDox)。这与Google测试博客上的建议类似,但前提条件和期望值之间存在更大的区别。例如:

WhenDivisorIsNonZero_ExpectDivisionResult
WhenDivisorIsZero_ExpectError
WhenInventoryIsBelowOrderQty_ExpectBackOrder
WhenInventoryIsAboveOrderQty_ExpectReducedInventory

您谈到了测试方法名称和测试夹具名称。测试夹具名称被映射到生产类别。您在测试中的哪里写生产方法名称?
2011年

12

我使用Given-When-Then概念。看看这篇简短的文章http://cakebaker.42dh.com/2009/05/28/given-when-then/。本文从BDD的角度描述了此概念,但是您也可以在TDD中使用它,而无需进行任何更改。


Give-When-Then与MethodName_Scenario_ExpectedBehavior相同,不是吗?
曙光

1
不完全是。Given_When_Then进一步涉及:GivenAnEntity_WhenSomeActionHappens_ThatResultIsExpected测试应该表达测试行为而不是实现的意愿。
plog17

1
“那时给予”通常被称为小黄瓜。它是来自Cucumber,JBehave和Behat工具的DSL。
bytedev

+1这是imo的最佳方法。将方法的执行方式与您期望的结果去耦非常强大,并且避免了其他注释中描述的许多问题。


7

我认为最重要的事情之一就是您的命名约定保持一致(并与团队中的其他成员达成一致)。很多时候,我看到在同一项目中使用了许多不同的约定。


7

我最近想出了以下约定来命名测试,它们的类和包含项目,以便最大程度地描述它们:

可以说我正在测试名称空间中Settings项目中的类MyApp.Serialization

首先,我将使用MyApp.Serialization.Tests名称空间创建一个测试项目。

在这个项目中,当然在命名空间中,我将创建一个名为IfSettings(保存为IfSettings.cs)的类。

可以说我正在测试该SaveStrings()方法。->我将命名测试CanSaveStrings()

当我运行此测试时,它将显示以下标题:

MyApp.Serialization.Tests.IfSettings.CanSaveStrings

我认为这很好地告诉了我,它正在测试什么。

当然,在英语中名词“ Tests”与动词“ tests”相同是有用的。

在命名测试时,您的创造力没有限制,因此我们可以为它们获得完整的句子标题。

通常,测试名称必须以动词开头。

示例包括:

  • 检测(例如DetectsInvalidUserInput
  • 抛出(例如ThrowsOnNotFound
  • 会(例如WillCloseTheDatabaseAfterTheTransaction

等等

另一种选择是使用“ that”代替“ if”。

后者可以节省我的击键次数,并且可以更准确地描述我在做什么,因为我不知道存在已测试的行为,但正在测试是否存在。

[ 编辑 ]

在使用上述命名约定一段时间之后,我发现在使用接口时,If前缀可能会造成混淆。碰巧的是,测试类IfSerializer.cs看起来与“打开文件”选项卡中的接口ISerializer.cs非常相似。在测试,要测试的类及其接口之间来回切换时,这可能会很烦人。结果我现在会选择如果作为前缀。

另外,我现在只在测试类中使用方法(因为在其他任何地方都不被视为最佳实践),因此使用“ _”来分隔测试方法名称中的单词,如下所示:

[Test] public void detects_invalid_User_Input()

我发现这更容易阅读。

[ 结束编辑 ]

我希望这能产生更多的想法,因为我认为命名测试非常重要,因为它可以为您节省大量时间,否则这些时间会花在尝试理解测试的功能上(例如,在长时间的中断后恢复项目) 。


2

在VS + NUnit中,我通常在项目中创建文件夹以将功能测试分组在一起。然后,我创建单元测试夹具类,并以要测试的功能类型命名。[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

2

我应该补充说,一旦准备好部署测试,而不必执行大量排除模式,则将测试保留在相同的程序包中,但与被测试源的并行目录中,可以消除代码的膨胀。

我个人喜欢《 JUnit Pocket Guide》中描述的最佳实践……很难击败JUnit的合著者写的书!


3
不相信这实际上可以解决手头的问题-您可以编辑并引用JUnit Pocket Guide吗?谢谢!
Nate-Wilkins 2014年

0

Foo类的测试用例的名称应为FooTestCase或类似的名称(FooIntegrationTestCase或FooAcceptanceTestCase)-因为它是一个测试用例。请参阅http://xunitpatterns.com/了解一些标准命名约定,例如测试,测试用例,测试夹具,测试方法等。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.