当前位置:  开发笔记 > 编程语言 > 正文

单元测试命名最佳实践

如何解决《单元测试命名最佳实践》经验,为你挑选了9个好方法。

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

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

我不知道这是一个非常好的方法,但是目前在我的测试项目中,我在每个生产类和测试类之间都有一对一的映射,例如ProductProductTest.

在我的测试类中,然后我有方法,其中包含我正在测试的方法的名称,下划线,然后是情况和我期望发生的事情,例如Save_ShouldThrowExceptionWithNullName().



1> Marc Climent..:

我喜欢Roy Osherove的命名策略,它是以下内容:

[UnitOfWork_StateUnderTest_ExpectedBehavior]

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

工作单元可以是单个方法,类,也可以是多个类.它应代表在此测试用例中要测试的所有内容并受到控制.

对于程序集,我使用典型的.Tests结尾,我认为它非常普遍,对于类(结束于Tests)是相同的:

[NameOfTheClassUnderTestTests]

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


对我来说,将方法名称放在测试方法中是没有意义的.如果您重命名方法怎么办?没有重构工具会为您重命名测试.最终,您最终手动重命名测试方法,或者更有可能错误地命名测试.就像评论一样.更糟糕的是,根本没有评论代码.
@Peri,我认为这是一个权衡.一方面,您的测试名称可能会过时,另一方面您无法确定测试的测试方法.我发现后者经常出现.
好的方法名称通常与方法执行的操作相同.**如果您必须在方法之后命名测试或方法执行的操作**之间决定**,这可能是一个提示,您应该重命名您的方法**.(虽然不是在所有情况下)
要添加Peri的注释 - 所有方法都负责某些操作,例如`UpdateManager.Update()`.考虑到这一点,我倾向于将测试称为"WhenUpdating_State_Behaviour"或"WhenUpdating_Behaviour_State".这样我就可以测试类的特定操作,同时避免在测试名中放入方法名.但最重要的是,当我看到失败测试的名称时,我必须弄清楚业务逻辑失败的原因
我发现测试名称中的方法名称没有问题.如果API发生变化,您将为这些新方法编写新的测试.旧测试保留并测试已弃用的API,直到它被删除.如果*重命名*没有向后兼容性的公共方法,则必须更改测试以使用新方法(即add-new/retire-old的快捷方式).重命名测试是最安全的部分.
如果使用这些工具重构/重命名,Resharper和IntelliJ都可能会找到您的测试方法并提供给您重命名.还会尝试查看您提及方法名称的注释,并更新它们.
@Peri:非常有见地.类名也会出现同样的问题.假设有可能将测试放在一个简单命名为"Test"的私有嵌套类中,但没有单元测试框架支持这一点,并且大多数程序员都拒绝运输单元测试代码.我曾经写过一个单元测试来反映所有的单元测试并断言它们的命名是正确的,但是丢失了那些代码.
@Ramunas:我同意你的看法.使用您希望作为测试名称的一部分进行测试的操作,而不是可能发生更改的方法名称是非常合理的.
我实际上点击了这个链接,这个答案与建议的使用有偏差.它应该是`[UnitOfWork_StateUnderTest_ExpectedBehavior]`,*不*需要方法名称.
@Peri - 相反,将方法名称包含在测试名称中是有意义的(当您测试单个方法时 - 通常是这样),因为这就是测试所测试的内容.它对阅读测试套件的人来说具有描述性和帮助性.是的,它将测试名称与方法名称相结合,但重命名测试是微不足道的,风险很低.在测试名称中包括测试方法的名称只是反映了测试名称和被测方法名称之间的高内聚性.
UnitTest中的方法名称看起来很吵。为什么不编写看起来像业务逻辑语句的单元测试!例如,我的一种测试方法如下:“ The_sum_of_all_probabilities_for_a_set_of_events_must_equal_to_1()。因此,单元测试类应充满业务逻辑语句。我们编写单元测试以覆盖所有业务逻辑。为什么要关心哪种方法执行任务只要涵盖所有业务逻辑,您的班级就应该承担非常有限的责任(单一责任原则),因此将不难发现哪种方法涵盖了什么。
@PiJei我的测试名称陈述了有关被测系统(SUT)的事实。它与具有[事实]属性而不是[测试]的Xunit配合使用效果很好。因此,例如对于StringCalculator,测试可以命名为Calculates_sum,Adds_numbers或Throws_for_negative_numbers。该方法称为Sum(),Calculate()或AddNumbers()都没关系。而且,如果我重命名,关于我的SUT的事实仍然有效,因此我不需要重命名测试。

2> Jack Ukleja..:

我喜欢跟着"应该"命名标准测试而命名的测试夹具被测单元后(即类).

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

对于那些感兴趣的人,我的命名测试方法已经发展了多年.一个与该问题是否应模式我上面描述的作为其不容易知道一目了然地看出哪些方法是测试.对于OOP,我认为使用测试方法启动测试名称更有意义.对于设计良好的类,这应该导致可读的测试方法名称.我现在使用类似的格式_Should_When.显然,根据上下文,您可能希望将"应该/何时"动词替换为更合适的内容.例: Deposit_ShouldIncreaseBalance_WhenGivenPositiveValue()


也许更好,更少多余,只要写一个句子告诉它做什么,假设测试工作:`increaseBalanceWhenDepositIsMade()`.
我也使用应该和当然但反之亦然.例如WhenCustomerDoesNotExist_ShouldThrowException().对我而言,这比当时应该更有意义(即在某种情况下应该有一定的预期结果).这也符合AAA(安排,行动,断言)......断言是最终......不是开始;-)
最近看到一篇文章提到了类似的命名约定(希望我给它添加书签).旨在使测试列表在按测试夹具排序时非常易读.你会看到类似"BankAccount"的东西(在不同的行上)"Should_Increase_Balance_When_Deposit_Is_Made""Should_Decrease_Balance_When_Withdrawal_Is_Made"等等.读取非常像规范,这是TDD的全部内容.
@Schneider:考虑"should"="推荐"然后是可选的,我想知道:使用"shall"="must"然后需要/强制使用它会不会更好.例如,RFC在两者之间产生差异.因此建议或要求通过测试?

3> Sklivvz..:

我喜欢这种命名方式:

OrdersShouldBeCreated();
OrdersWithNoProductsShouldFail();

等等.对于非测试者来说,问题是什么是非常明确的.


但是@ hotshot309,他可能正在使用.NET - [.NET Capitalization Conventions](http://msdn.microsoft.com/en-us/library/ms229043.aspx)
@Calin在我看来,使用`Will`并不是真的*合适*并且通过这样做你实际上错误地告诉读者**测试**将不会失败...如果你使用`Will`来表达未来可能不会发生的事情,你不正确地使用它,而"应该"是更好的选择,因为它表明你想要/希望某些事情发生但它没有或不能,当测试运行时告诉你它是否失败/成功所以你不能事先暗示,这是合乎逻辑的解释,你的是什么?为什么你要避免`应该'?
@CoffeeAddict因为标识符中的下划线是 aberration 在C#中不是真正的惯用语
@Ace,我完全赞同你,并在发表此评论后约一分钟注意到了这一点.我发誓,当我看到自己的错误时,我删除了它,但不知何故,我猜我没有.对于那个很抱歉.
我也想避免使用`should`我更喜欢`will`所以`OrdersWithNoProductsWillFail()`

4> Sergio Acost..:

Kent Beck建议:

每个'单元'一个测试夹具(程序类).测试装置本身就是课程.测试夹具名称应为:

[name of your 'unit']Tests

测试用例(测试夹具方法)的名称如下:

test[feature being tested]

例如,具有以下类:

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

    // other methods and properties
}

测试夹具将是:

class PersonTests {

    testAgeCalculationWithNoBirthDate() { ... }

    // or

    testCalculateAge() { ... }
}


在给出类的后缀的情况下,'test'的方法前缀不会变得多余吗?
请记住,当肯特写这本书时.属性不是发明的.因此,方法名称中的名称Test向测试框架指示该方法是测试.自2002年以来也发生了很多事情.
testCalculateAge ...这对于您的测试方法来说是一个毫无意义的名称."test"是多余的(你用"方法"前缀命名所有方法吗?).名称的其余部分没有受到测试或预期的条件.CalculateAge是被测试的方法吗?.....谁知道......
我希望更多人遵循这些准则.不久前,我不得不重命名超过20种测试方法,因为它们的名称类似于"ATest","BasicTest"或"ErrorTest".

5> 小智..:

班级名称.对于测试夹具名称,我发现"测试"在许多领域的普遍存在的语言中非常普遍.例如,在工程领域:StressTest和化妆品领域:SkinTest.很抱歉不同意肯特,但在我的测试装置(StressTestTest?)中使用"测试" 令人困惑.

"单位"在域名中也被大量使用.例如MeasurementUnit.是一个称为MeasurementUnitTest"测量"或"MeasurementUnit"的测试类吗?

因此,我喜欢为我的所有测试类使用"Qa"前缀.例如QaSkinTestQaMeasurementUnit.它永远不会与域对象混淆,并且使用前缀而不是后缀意味着所有测试夹具在视觉上共存(如果您的测试项目中有假货或其他支持类,则非常有用)

命名空间.我在C#中工作,并且我将测试类保持在与他们正在测试的类相同的命名空间中.它比拥有单独的测试命名空间更方便.当然,测试类是在不同的项目中.

测试方法名称.我喜欢命名我的方法WhenXXX_ExpectYYY.它使前提条件清晰,并有助于自动化文档(la TestDox).这类似于Google测试博客上的建议,但更多地分离了前提条件和期望.例如:

WhenDivisorIsNonZero_ExpectDivisionResult
WhenDivisorIsZero_ExpectError
WhenInventoryIsBelowOrderQty_ExpectBackOrder
WhenInventoryIsAboveOrderQty_ExpectReducedInventory



6> Pashec..:

我使用Given-When-Then概念.看看这篇短文http://cakebaker.42dh.com/2009/05/28/given-when-then/.文章根据BDD描述了这个概念,但您也可以在TDD中使用它而不做任何更改.



7> Ates Goral..:

请参阅:http: //googletesting.blogspot.com/2007/02/tott-naming-unit-tests-responsibly.html

对于测试方法名称,我个人觉得使用详细和自我记录的名称非常有用(与Javadoc注释一起进一步解释测试正在做什么).



8> Thorsten Lor..:

我最近提出了以下约定,用于命名我的测试,它们的类和包含项目,以便最大化它们的描述:

假设我正在测试MyApp.Serialization命名空间中项目中的Settings类.

首先,我将使用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.当在测试,被测试的类及其接口之间来回切换时,这会非常烦人.结果我现在会选择如果作为前缀.

另外我现在使用 - 仅用于我的测试类中的方法,因为它在其他任何地方都不被认为是最佳实践 - "_"用于分隔我的测试方法名称中的单词,如:

[测试] public void detected_invalid_User_Input()*

我发现这更容易阅读.

[ 结束编辑 ]

我希望这会产生更多的想法,因为我认为命名测试非常重要,因为它可以为您节省大量时间,否则将花费大量时间来试图了解测试正在做什么(例如,在延长的中断后恢复项目) .



9> bytedev..:

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

推荐阅读
保佑欣疼你的芯疼
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有