Osherove的负面单元测试命名约定?

时间:2012-08-09 09:06:41

标签: c# unit-testing refactoring naming-conventions

我正在尝试确定单元测试的命名约定。我喜欢Roy Osherove推荐的那个:

  

[MethodName_StateUnderTest_ExpectedBehavior]

http://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html

如果应用程序通过抛出异常正确处理错误的行为,我不确定这个负面测试标准。

因此,ExpectedBehavior在这种情况下总是“CorrectExceptionThrown”。为每个负面单元测试编写ExpectedBehavior仍然有意义还是可以使它成为可选项?

有利有弊。一方面,对于负面测试,它总是相同的,因此每次写入它都是多余的,它使单元测试方法名称变长。如果我们将其作为可选项,那么在必要时也不会为单元测试添加预期行为的风险。我也认为最好在整个项目中保持一致,所以在任何地方都应用它。

4 个答案:

答案 0 :(得分:5)

在指定作为操作结果抛出的异常时,没有什么是多余的。这实际上完全符合Roy的命名惯例,给出:

SomeMethod_ExpectionalState_ThrowsInvalidOperationException
SomeMethod_ExceptionalState_ThrowsArgumentNullException

您将获得有关您的代码的重要信息 - 抛出的异常类型。但是,当你有经典的幸福路径测试时,某些部分名称的有用性是主观的。考虑:

SomeMethod_DependencyReturnsCorrectResult_ReturnsResult
SomeMethod_WhenNothingSpecialHappens_ReturnsResult
SomeMethod_EverythingElseWorked_WorksToo

这些名字带有哪些信息?相当少。 ReturnsResult基本上意味着它有效。 NothingSpecialHappens也是相当含糊的信息。在这种情况下,删除部分名称可能是合理的。

但是请注意,可能值得考虑更改名称而不是完全删除部分名称(例如,ReturnsResult可以替换为不那么模糊的ReturnsEntityFromDatabase或{ {1}})。

最后,不要盲目跟随罗伊 - 将其视为指南,而不是惯例。约定很少适用于所有可能的情况,这一点也不例外。

答案 1 :(得分:2)

你可以写:

[Test]
public void Foo_ExceptionalCaseX1_ExceptionY1Thrown()
{
}

[Test]
public void Foo_ExceptionalCaseX2_ExceptionY2Thrown()
{
}

...

如果异常情况不同且抛出的异常类型相同,则没有冗余(即使后缀相同)。
这与编写这两个测试没什么不同:

[Test]
public void Foo_SomeCaseX1_42Returned()
{
}

[Test]
public void Foo_SomeCaseX2_42Returned()
{
}

...

你能做什么 - 在两种情况下返回42,这是现实 - 对于例外也是如此。

还有一件事:当一个人阅读一系列测试时,他们可能看起来(几乎)相同,但当其中一个人有一天失败时,幸运的开发人员将能够立即知道预期的行为是什么。每个测试都应该代表它自己。

答案 2 :(得分:1)

想象一下单个单元测试失败的情况。然后你会从CI那里得到一条消息:

Failed unit tests:

MethodName_NegativeTestParams1_CorrectExceptionThrown

没有其他背景。并且您看到问题(抛出了错误的异常)。如果您将此选项设为可选项或尝试缩短方法名称,则最终可能会使用

Failed unit tests:

MethodName_NegativeTestParams1

没有任何线索出了什么问题,直到你看看测试。

在这种情况下,除了失败的单元测试列表之外没有上下文的情况下,您应该尽可能详细地设置方法名称,并根据需要重复CorrectExceptionThrown次。

此外,CorrectExceptionThrown消息可能不那么通用:ArgumentExceptionThrown等,以防您对不同的测试有不同的例外。

因此,我会在所有情况下都包含预期的行为,尽管有时看起来似乎是不必要的重复。

答案 3 :(得分:0)

我认为这是真的;但是,这只是决定单元测试命名约定的一种方法。如果您明显知道此测试中的异常抛出,我认为.NET框架单元测试将提供一种像JUnit一样简单的方法。

@Test (IOException.class) public void testIOException() {...}