有时候我会遇到一些没有任何断言的单元测试。今天早上我遇到的一个特例是在满足某个条件时测试是否写入了一个日志文件。假设如果没有抛出错误,测试就通过了。
我个人对此并无意见,但写一个没有任何关联断言的单元测试似乎有点“代码异味”。
想知道大家对此有什么看法?
有时候我会遇到一些没有任何断言的单元测试。今天早上我遇到的一个特例是在满足某个条件时测试是否写入了一个日志文件。假设如果没有抛出错误,测试就通过了。
我个人对此并无意见,但写一个没有任何关联断言的单元测试似乎有点“代码异味”。
想知道大家对此有什么看法?
这只是一个非常基本的测试,应该将其作为文档记录下来。它只验证运行时不会出错。这类测试最糟糕的地方在于它们给人一种虚假的安全感。你的代码覆盖率会提高,但这是虚幻的。味道很糟。
这将是官方的方法:
// Act
Exception ex = Record.Exception(() => someCode());
// Assert
Assert.Null(ex);
这些被称为烟雾测试,很常见。它们是基本的健全性检查。但是它们不应该是你拥有的唯一测试类型。你仍然需要在另一个测试中进行某种形式的验证。
这样的测试不太可靠。至少应该检查文件是否被写入,或者修改时间是否已更新。
我见过很多这样编写的测试,最终没有测试任何东西,即代码并没有起作用,但也没有崩溃。
如果你有一些明确的要求,需要测试的代码不能抛出异常,并且你想明确指出这一点(测试作为需求文档),那么我会这样做:
try
{
unitUnderTest.DoWork()
}
catch
{
Assert.Fail("code should never throw exceptions but failed with ...")
}
...但对我来说,这仍然有点不对劲,可能是因为它试图证明一个否定的命题。
从某种意义上说,您正在进行一种隐含的断言 - 即代码不会抛出异常。当然,实际上获取文件并找到适当的行会更有价值,但我想总比什么都没有好。
这是一个很好的实用解决方案,特别是如果没有测试的话。
问题在于,如果所有调用的函数都是无操作的,则测试将通过。但有时验证副作用是否符合预期是不可行的。在理想情况下,会有足够的时间为每个测试编写检查...但我不住在那里。
我使用此模式的另一个地方是将一些性能测试嵌入到单元测试中,因为这是一个简单的方法来确保每次构建时都运行它们。测试不做任何断言,但会测量测试所需的时间并记录下来。
void TestLogDoesNotThrowException(void) {
log("blah blah");
}
这个测试如何验证日志是否在没有断言的情况下被写入?
一般来说,我认为这种情况发生在集成测试中,只要某些事情成功完成就足够了。在这种情况下,我可以接受。
如果我在单元测试中反复看到这种情况,我会好奇这些测试的实际用处。
编辑:在OP提供的例子中,有一些可测试的结果(日志文件结果),因此假设没有抛出错误就表示它有效是懒惰的。
我之前看过类似的情况,我认为这只是为了提高代码覆盖率而已。它可能并没有真正测试代码行为。无论如何,我同意在测试中记录其(意图),以增加清晰度。
Assertions.assertDoesNotThrow(() -> someCode());
- caduceusAssert.DoesNotThrow()
,但它的缺点是将Act和Assert结合在一行中。对于那些希望更严格遵守AAA的人来说,首选的方法是使用Record.Exception
。 - Brad Wilson