单元测试真的用作文档吗?


22

我无法数出“单元测试是被测代码文档的重要来源”这一类读语句的次数。我不否认他们是真实的。

但就我个人而言,我从来没有发现自己将它们用作文档。对于我使用的典型框架,方法声明记录了它们的行为,而这就是我所需要的。我假设单元测试将备份该文档中所述的所有内容,并可能还会添加一些其他内部内容,因此,一方面它会复制文档,而另一方面可能会添加一些不相关的内容。

所以问题是:什么时候将单元测试用作文档?什么时候评论不能涵盖所有内容?通过开发人员扩展源代码?他们公开了哪些文档本身无法公开的有用和相关的内容?


4
没想到直接将单元测试用作文档。我认为单元测试通常是不可读的,因为许多开发人员不会花时间来清晰地编写它们。
superM 2012年

10
评论可能是错误的。

2
我知道你是有关单元测试的具体要求,但我也想指出,整合/系统测试,真正有用的文档也一样,只是在不同的级别
JK。

3
我见过将单元测试更好地描述为“单元实验”的情况。他们对外部因素的依赖使他们几乎毫无用处。他们也不清楚。(是的,我的长远目标是将它们重构为更好的模型,但我也做其他事情……)
Donal Fellows 2012年

4
@Ant单元测试调用实际代码并记录预期的响应,并将其与实际响应进行比较。关键在于调用的代码是否正确-测试记录了如何调用它。

Answers:


17

它们不是绝对的参考文档

请注意,以下许多内容也适用于注释,因为它们可能与测试等代码不同步(尽管执行性较差)。

因此,最后,理解代码的最佳方法是拥有可读的工作代码

如果根本不写硬连线的低级代码部分或特别棘手的条件,那么附加文档将至关重要。

  • 测试可能不完整:
    • API已更改且未经过测试,
    • 编写代码的人为最简单的测试方法(而不是最重要的测试方法)编写测试,然后又没有时间完成测试。
  • 测试可能会过时。
  • 测试可能会以非显而易见的方式短路并且无法实际执行。

但是它们仍然是有益的文档补充

但是,当对某个特定类的功能有疑问时,尤其是在冗长,晦涩且缺少注释(您知道该类...)的情况下,我会迅速尝试查找其测试类并检查:

  • 他们实际尝试检查的内容(提示最重要的提示,除非开发人员犯了上述仅实施“简单”测试的错误),
  • 以及是否有极端情况。

另外,如果使用BDD风格编写,则它们可以很好地定义类的合同。打开您的IDE(或使用grep)仅查看方法名称和tada:您有一个行为列表。

回归和错误也需要测试

同样,为回归和错误报告编写测试也是一个好习惯:修复某些问题,编写测试以重现案例。回顾它们时,例如,这是查找相关错误报告以及所有有关旧问题的详细信息的好方法。

我想说它们是对真实文档的很好的补充,至少在这方面是宝贵的资源。如果使用得当,这是一个很好的工具。如果您在项目的早期就开始进行测试并养成习惯,那么它可能是一个很好的参考文档。在已有不良代码习惯的现有项目中,代码库已经散乱了,请谨慎处理。


请问为什么我被否决了?是什么打扰您或您不同意什么?
haylem 2012年

2
参数的(IMO)最佳部分是以最小的字体编写的-理解代码的最佳方法是首先使代码具有可读性。我会将其更改为“可读且有效的代码”,但我同意。然后,如果您再次查看单元测试,则运行的测试是可以工作的代码(并且像所有代码一样,应该是可读的),因此,做好的话实际上是相当不错的文档(如果经常过于本地化)。
Joris Timmermans 2012年

@MadKeithV:谢谢。我更新了“可读和有效的代码”,并将其提高了一点。
haylem 2012年

11

一种解释是单元测试是“可执行文档”。您可以对代码运行单元测试,它会告诉您它是否仍然像编写测试通过时那样仍在执行。这样,单元就可以在某个时间点以可执行方式测试“文档”系统的功能。

另一方面,我也很少真正阅读单元测试代码作为“文档”来理解功能。单个单元测试过于本地化,过于具体和过于抽象,以致于无法告诉您有关所测试类背后的实际系统的更多信息。


5

如果通过文档您的意思是我想了解一下代码的工作方式,那么单元测试是在预期边缘情况错误(即bug)情况下代码单元如何工作的完美示例。同样,可以在编写代码之前创建测试,从而从业务/需求的角度看,代码应该做什么。

他们会取代文件吗?没有。

它们是文档的有用附录吗?是。


4

我将单元测试视为:

  • 一种证明文档正确的方法(假设文档与api实现匹配)。
  • 向开发人员演示如何使用特定功能的方法;单元测试治具/单元测试本身通常足够小,可以快速从中学习。
  • 显然可以发现任何回归错误。

在某种程度上,它们可以看作是对现有文档的补充,而不是文档。


3

我将再问您一个问题来回答您的问题。

使用新的API /例程时,您多久发出一次帮助来查找您要使用的事物的代码示例?如果您没有切换到Google进行代码示例在线搜索?

这正是将单元测试用作文档的时间。

  • 实际上,单元测试可能比普通的代码示例更为严格,因为您应该有多个测试(示例)。
  • 希望您的单元测试能够说明正确的用法。例如,它们通过普通对象或模拟对象清楚地显示了所有必要的依赖关系。(否则,它们并不是特别好的单元测试。)
  • 注意:如果您的注释或“常规文档”提供了代码示例,则实际上您在违反DRY原则。随着时间的流逝,这些代码示例很容易变得不正确,而通过定期执行的单元测试,这些代码示例的可能性大大降低。
  • 如果单元测试是彻底的(通常为if),则它们应提供其他信息:
    • 清楚地说明了所有已知的边缘情况。
    • 可以抛出的所有预期异常。
    • 先前发现的所有错误(扩展被测单元比为该单元编写新客户端时,这可能更有用)。
    • 与该单位关联的所有基本业务规则。(如果有)

我怀疑有很多原因不能使单元测试成为文档,尽管它们可以很好地补充传统文档:

  • 我敢建议通常情况下,测试本身编写得不够好。其他答案已经提到测试:
    • 不完整
    • 令人困惑。(我已经看到测试用例不能直接调用被测方法-在调用之前,您要深入3/4层调用堆栈,并且调用该方法的前提条件分散在复杂的类层次结构中的不同位置。 )
    • 过时的。(通常,测试过时后会失败,但这并非总是如此)。
  • 只要需要一个示例,通常在生产代码中就有许多使用示例。
  • 被测单元编写得很好(自我记录),因此这些方法不需要示例。我希望!
  • 以我作为程序员的经验,我们倾向于热衷于深入了解下周二的 RTFM ...
  • 违反DRY原则的文档和注释。

2

TL; DR单元测试和API注释是互补的-有些事情最好用代码来描述,而另一些最好用散文来描述。

单元测试主要用于记录特殊情况和边缘条件,这些条件很难(而且麻烦)在API注释中描述。同样,API注释通常指向想要使用API​​的人。

如果要修改代码,通常需要了解更多信息,其中有些很难放入注释中(这些注释很快就会过时)。在这种情况下,单元测试也可以作为文档。

一个示例:您有一个方法m (a, b)执行某种计算。由于向后兼容的要求,它必须是a=0和的特殊情况输入a=-1,但只有在b为NULL 时才可以。将其添加到注释中是复杂,冗长的,并且如果以后删除要求,则可能会过时。

如果进行一些单元测试来检查的行为m(0, NULL)m(-1, x)那么您将获得以下好处:

  • 正确行为的描述清楚,减少了误解
  • 人们在更改代码时不能忽略要求,与注释不同

但是对于您的示例,如果注释中根本没有记录该行为,则用户可能会在这种情况下获得意外结果。这绝对不是一件好事。
stijn 2012年

@stijn:是的。在那种情况下,最好的方法可能是在文档中简要提及特殊情况,再加上对凌乱细节进行单元测试。
sleske 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.