单元测试命名最佳实践

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

这个问题之前已经在单元测试的一些常用命名约定是什么?讨论过了

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

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

450323 次浏览
< p >看: # EYZ0 < / p >

对于测试方法名称,我个人认为使用详细的和自文档化的名称非常有用(以及进一步解释测试正在做什么的Javadoc注释)。

# EYZ0建议:

  • 每个“单元”(程序的类)一个测试夹具。测试fixture本身就是类。测试夹具的名称应该是:

    [name of your 'unit']Tests
    
  • Test cases (the test fixture methods) have names like:

    test[feature being tested]
    

For example, having the following class:

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


// other methods and properties
}

测试夹具应该是:

class PersonTests {


testAgeCalculationWithNoBirthDate() { ... }


// or


testCalculateAge() { ... }
}

我喜欢这样的命名风格:

OrdersShouldBeCreated();
OrdersWithNoProductsShouldFail();

等等。 这对于非测试人员来说非常清楚问题所在

在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

我应该补充一点,将您的测试放在同一个包中,但位于与被测试的源代码并行的目录中,一旦您准备好部署它,就可以消除代码的膨胀,而不必执行一堆排除模式。

我个人喜欢《JUnit口袋指南》中描述的最佳实践…很难有一本书比JUnit!

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

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

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

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

假设我正在测试SaveStrings()方法。我将命名这个测试CanSaveStrings()

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

# EYZ0

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

当然,在英语中名词“Tests”和动词“Tests”是一样的,这是很有用的。

在命名测试时,你的创造力是没有限制的,这样我们就可以得到完整的句子标题。

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

例子包括:

  • 检测(例如DetectsInvalidUserInput)
  • 抛出(例如ThrowsOnNotFound)
  • Will(例如WillCloseTheDatabaseAfterTheTransaction)

等。

另一个选择是用“that”而不是“if”。

后者节省了我的击键虽然和描述更准确地我在做什么,因为我不知道,< em > < / em >测试的行为是目前,但我测试如果< em > < / em >它是。

(# EYZ0)

在使用上述命名约定一段时间后,我发现,在处理接口时,如果前缀可能令人困惑。碰巧的是,测试类IfSerializer.cs看起来非常类似于“打开文件选项卡”中的接口ISerializer.cs。 当在测试、被测试的类和它的接口之间来回切换时,这会非常烦人。因此,我现在将选择而不是如果作为前缀

此外,我现在只对我的测试类中的方法使用“_”来分隔我的测试方法名称中的单词,因为它在其他任何地方都不是最佳实践:

[Test] public void detects_invalid_User_Input()

我觉得这个更容易读。

(# EYZ0)

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

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

“单位”在域中也经常使用。例如# EYZ0。一个名为MeasurementUnitTest的类是“Measurement”或“MeasurementUnit”的测试吗?

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

# EYZ0。我使用c#工作,并且我将我的测试类保持在与它们所测试的类相同的命名空间中。这比使用单独的测试名称空间更方便。当然,测试类在不同的项目中。

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

WhenDivisorIsNonZero_ExpectDivisionResult
WhenDivisorIsZero_ExpectError
WhenInventoryIsBelowOrderQty_ExpectBackOrder
WhenInventoryIsAboveOrderQty_ExpectReducedInventory

更新(2021年7月)

距离我最初的答案(将近12年)已经有一段时间了,在这段时间里,最佳实践发生了很大变化。因此,我倾向于更新自己的答案,并为读者提供不同的命名策略。

许多评论和回答指出,我在最初的回答中提出的命名策略不抵抗重构,最终导致名称难以理解,我完全同意这一点。

在过去的几年里,我最终使用了一种更易于理解的命名模式,其中测试名称描述了我们想要测试的内容,在弗拉基米尔•Khorikov所描述的行中。

一些例子是:

  • # EYZ0
  • # EYZ0
  • # EYZ0

但正如您所看到的,这是一个非常灵活的模式,但最重要的是,阅读名称就知道测试是关于什么,而不包括可能随着时间变化的技术细节。

要命名项目和测试类,我仍然坚持最初的答案模式。

原答案(2009年10月)

我喜欢Roy Osherove的命名策略。是这样的:

# EYZ0

它以结构化的方式包含了方法名所需的所有信息。

工作单元可以小到一个方法、一个类,也可以大到多个类。它应该表示在这个测试用例中要测试的所有东西,并且这些东西都在控制之下。

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

# EYZ0

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

我使用Given-When-Then概念。 看看这篇短文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));
}
}

为什么# EYZ0 ?

我发现它迫使测试编写人员用这样一句话来命名测试:“应该[处于某种状态][在行动发生之后/之前/当][行动发生]”。

是的,写“Should"到处都有点重复,但正如我所说,它迫使作者以正确的方式思考(这对新手来说很好)。此外,它通常会产生一个可读的英文测试名称。

# EYZ0:

我注意到Jimmy Bogard也是“应该”的粉丝,甚至有一个名为<强> < /应该强>的单元测试库。

更新(4年后…)

对于那些感兴趣的人,我命名测试的方法多年来一直在发展。我上面描述的应该模式的一个问题是不容易一眼就知道哪个方法正在测试。对于OOP,我认为以被测试的方法开始测试名称更有意义。对于一个设计良好的类,这应该导致可读的测试方法名称。我现在使用类似于<method>_Should<expected>_When<condition>的格式。显然,根据上下文,你可能想用Should/When动词来代替更合适的动词。例子: # EYZ0 < / p >

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