Osherove的负面单元测试命名规则是什么?

4
我正在考虑为单元测试选择一种命名约定。我喜欢Roy Osherove推荐的这种方式:
[MethodName_StateUnderTest_ExpectedBehavior]
具体请参见http://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html
但是,对于我们通过抛出异常来测试应用程序是否正确处理错误行为的负面测试,我对这个标准表示怀疑。在这种情况下,ExpectedBehavior 总是“CorrectExceptionThrown”。对于每个负面单元测试,写入ExpectedBehavior 是否仍然有意义,还是可以选择不写?
这样做有利有弊。一方面,对于负面测试,它总是相同的,因此每次写它都会显得多余,而且会使单元测试方法名称变长。如果我们将其设置为可选项,则存在预期行为未被添加到必要的单元测试中的风险。另一方面,我认为最好在整个项目中保持一致,无论在何处都应该采用相同的方式。

我不确定仅因为重复就可以称之为“冗余”。每种情况下它都在描述测试的某些内容。 - Magnus Hoff
4个回答

5
指定抛出哪个异常作为操作结果并不是多余的。这实际上完全符合Roy的命名约定,因为:
SomeMethod_ExpectionalState_ThrowsInvalidOperationException
SomeMethod_ExceptionalState_ThrowsArgumentNullException

您将获得关于您的代码的重要信息 - 抛出异常的类型。然而,当您进行经典的"happy path"测试时,一些部分的名称的实用性是主观的。请考虑以下事项:
SomeMethod_DependencyReturnsCorrectResult_ReturnsResult
SomeMethod_WhenNothingSpecialHappens_ReturnsResult
SomeMethod_EverythingElseWorked_WorksToo

这些名称所携带的信息很少。 ReturnsResult 基本上意味着它有效。NothingSpecialHappens 也是相当模糊的信息。在这种情况下,可以有理由省略部分名称。
然而,请注意,与其完全删除名称的一部分(例如,ReturnsResult 可以替换为更不含糊的 ReturnsEntityFromDatabaseReturnsSerializedValue),改名可能更值得考虑。
最后,不要盲目地遵循 Roy - 把它看作是指导方针,而不是惯例。惯例很少适用于所有可能的情况,这个也不例外。

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,这是现实的 - 异常也一样。

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


1

想象一下当单元测试失败时的情况。然后你会收到来自CI的这样的消息:

Failed unit tests:

MethodName_NegativeTestParams1_CorrectExceptionThrown

没有其他上下文。你看到问题了(抛出了错误的异常)。如果你将其变成可选项或试图缩短方法名称,可能会导致

Failed unit tests:

MethodName_NegativeTestParams1

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

在这种情况下,当您没有上下文,只有一系列失败的单元测试时,您应该尽可能详细地命名方法名称,并重复多次CorrectExceptionThrown

此外,CorrectExceptionThrown消息可以更具体:例如,在不同的测试中有不同的异常,可以使用ArgumentExceptionThrown等。

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


0

我认为这是正确的;然而,这只是一种决定单元测试命名约定的方式。如果你明确知道在这个测试中抛出的异常,我认为.NET框架单元测试将提供一种像JUnit那样简单的方法。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接