在谈论单元测试时,“DAMP not DRY”是啥意思?

Posted

技术标签:

【中文标题】在谈论单元测试时,“DAMP not DRY”是啥意思?【英文标题】:What does “DAMP not DRY” mean when talking about unit tests?在谈论单元测试时,“DAMP not DRY”是什么意思? 【发布时间】:2011-09-21 03:31:46 【问题描述】:

我听说有人说单元测试(例如 nUnit、jUnit、xUnit)应该是

DAMP 不是DRY

(例如,单元测试应该包含“潮湿代码”而不是“干代码”)

他们在说什么?

【问题讨论】:

保证非 DRY 代码的单元测试没有什么特别之处。编写非 DRY 测试是懒惰的程序员试图为他们的懒惰开辟领域的借口。简而言之,干性和可读性是正交的问题。 DRYness 增加了代码导航距离,这反过来又导致了更高的理解负担。这适用于“正常”基于文本的环境。投影编辑器可以减少代码的正交性,但并非在所有情况下都可以。 我推荐这篇文章:enterprisecraftsmanship.com/posts/dry-damp-unit-tests 【参考方案1】:

Damp = '描述性和有意义的短语' - 你的单元测试应该能够被'阅读':

Readability is more important than avoiding redundant code.

来自文章:

DAMP 代表“描述性和有意义的短语”,与 DRY 相反,并不是说“一切都应该看起来像垃圾堆并且无法阅读”,因为可读性比避免冗余更重要代码。

这是什么意思以及在哪里使用它? DAMP 主要适用于编写测试代码。测试代码应该很容易理解,以至于可以接受一些冗余。

【讨论】:

【参考方案2】:

DAMP - 描述性和有意义的短语。

“DAMP not DRY”重视代码重用的可读性。测试用例中 DAMP not DRY 的想法是测试应该易于理解,即使这意味着测试用例有时会重复代码。

另请参阅Is duplicated code more tolerable in unit tests?,了解有关此观点优点的一些讨论。

它可能是由Jay Fields 创造的,与域特定语言有关。

【讨论】:

很好的答案和相关问题的链接。没有完美的 DAMP 与 DRY 选择。我们希望代码尽可能枯燥,在测试中这意味着不要太枯燥以至于测试变得难以理解。当测试失败时,我希望原因显而易见,以便开发人员可以开始修复 SUT,这意味着我倾向于在测试中使用 DAMP 代码。像大多数编程概念一样,总是有可能走得太远。如果您的单元测试代码太枯燥,以至于需要很长时间才能确定测试失败的方式和原因,那么它可能“太枯燥”了。【参考方案3】:

DAMP 代表“描述性和有意义的短语”,与 DRY 相反,并不是说“一切都应该看起来像垃圾堆并且无法阅读”,因为可读性比避免冗余更重要代码。

http://codeshelter.wordpress.com/2011/04/07/dry-and-damp-principles-when-developing-and-unit-testing/

【讨论】:

【参考方案4】:

“DRY”是“不要重复自己”

这是一个术语,用于告诉人们编写可重用的代码,这样您就不会一遍又一遍地编写类似的代码。

“DAMP”是“描述性和有意义的短语”。

该术语旨在告诉您编写代码,让正在查看它的人容易理解。如果您遵循此原则,您将拥有长且描述性的变量和函数名称等。

【讨论】:

AIUI,DRY 不仅仅是通过可重用性节省时间 - 它还可以防止不同的代码路径“不同步”。如果您跨多个类复制粘贴相同的逻辑,则需要更改时需要更新该代码的每个实例。 (不可避免地其中一个不会,并且在运动时会爆炸。)【参考方案5】:

这里已经有几个答案,但我想添加另一个,因为我认为他们不一定能解释得很好。

DRY(不要重复自己)的想法是,在您的应用程序代码中,您希望避免冗余或重复代码。如果您的代码需要多次执行某项操作,您应该为其创建一个函数或类,而不是在多个地方重复类似的代码。

这是一个众所周知的编程概念。

DAMP(描述性和意味深长的短语)适用于您的单元测试。这里的想法是您的单元测试方法名称应该长且具有描述性——实际上是描述您正在测试的内容的简短句子

例如:testWhenIAddOneAndOneIShouldGetTwo() ....

当您阅读这样的 DAMP 方法名称时,您应该准确地了解测试编写者试图实现的目标,甚至不必阅读测试代码(尽管测试代码当然也可以遵循这个概念,罗嗦变量名等)。

这是可能的,因为单元测试方法具有非常具体的输入和预期输出,因此 DAMP 原则对它们很有效。您的主应用程序代码中的方法不太可能具体到足以保证这样的名称,特别是如果您在编写它时考虑了 DRY 原则。

DAMP 和 DRY 并不矛盾——它们涵盖了代码编写方式的不同方面——但它们通常不会一起使用,因为考虑到 DRY 原则编写的方法将是通用的,不太可能适合高度特定的方法名称。因此,一般而言,如上所述,您的应用程序代码应该是 DRY,而您的单元测试代码应该是 DAMP。

我希望这有助于更好地解释它。

【讨论】:

【参考方案6】:

这是一种平衡,而不是矛盾

DAMP 和 DRY 并不矛盾,它们平衡了代码可维护性的两个不同方面。可维护的代码(易于更改的代码)是这里的最终目标。

DAMP(描述性和有意义的短语)提高了代码的可读性

要维护代码,首先需要了解代码。要理解它,你必须阅读它。考虑一下您花了多少时间阅读代码。很多。 DAMP 通过减少阅读和理解代码所需的时间来提高可维护性。

DRY(不要重复自己)推广代码的orthogonality

删除重复可确保系统中的每个概念在代码中都有一个单一的权威表示。对单个业务概念的更改会导致对代码的单个更改。 DRY 通过将更改(风险)隔离到系统中必须更改的部分来提高可维护性。

那么,为什么重复在测试中更容易被接受?

测试通常包含固有的重复,因为它们一遍又一遍地测试相同的东西,只是输入值或设置代码略有不同。但是,与生产代码不同,这种重复通常仅与单个测试夹具/文件中的场景隔离。因此,重复很少且明显,这意味着与其他类型的重复相比,它对项目构成的风险较小。

此外,删除这种重复会降低测试的可读性。以前在每个测试中重复的细节现在隐藏在一些新的方法或类中。要全面了解测试,您现在必须在脑海中将所有这些部分重新组合在一起。

因此,由于测试代码重复通常具有较小的风险,并且提高了可读性,因此很容易看出它是如何被认为是可接受的。

原则上,在生产代码中支持 DRY,在测试代码中支持 DAMP。虽然两者同样重要,但只要有一点智慧,您就可以使天平对您有利。

【讨论】:

这是一个伟大而简洁的总结。我还想指出,DAMP 测试在面对不断变化的需求时更具弹性,并且当其他人负责重写您的测试以适应新需求时,衡量测试的明显性是一个巨大的好处。 Jesper Lundberg 在这个主题上也有一篇很好的论文。 @Jason,顺便说一句,有没有链接到 “Jesper Lundberg 也有一篇关于这个主题的好论文” @JohnSaunders,您可以使用测试数据构建器模式避免一些重复:natpryce.com/articles/000714.html 干燥测试代码有可能通过引入mystery guest来创建一个晦涩的测试 我还要补充一点,编写良好的测试本质上是您应用程序的文档/cmets。因此,更具描述性有助于向其他开发人员解释您的意图。正如 OP 所说,它们在每个测试中都是独立的,因此对您的应用程序的危险是最小的。更糟糕的情况是您有冗余测试或测试设置,并且运行测试套件需要更长的时间。我宁愿在良好的测试覆盖率方面犯错。【参考方案7】:

我同意 Chris Edwards 的观点,您需要在两者之间取得平衡。另一件需要注意的事情是,如果为了消除重复,您最终在单元测试代码中添加了许多额外的结构(即,当将 DRY 发挥到极致时),您就有可能在其中引入错误。在这种情况下,您要么必须对单元测试进行单元测试,要么不测试结构。

【讨论】:

【参考方案8】:

我不希望在这里重复这项工作,但是您可以进行 DAMP 测试,但具有 DRY 的优势。另一方面,DRY 测试在某些情况下无法满足 DAMP 测试。

I've blogged about DRY vs DAMP which includes some examples.

这两种方法都不应该是您唯一的解决方案,有时 DAMP 是多余的,有时是一个很好的补充。

作为一般规则,您应该应用三规则。如果您第三次发现重复,可能值得考虑编写 DAMP 样式测试,但即便如此 not all duplication is bad。上下文很重要。

【讨论】:

以上是关于在谈论单元测试时,“DAMP not DRY”是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

在谈论 MongoDB 和 Cassandra 时,“面向文档”与键值是啥意思?

每次运行单元测试时都在擦除我的 H2 数据库是啥?

当人们谈论使用“分片”扩展网站时,他们是啥意思?

在 DAO 单元测试期间恢复数据的策略是啥?

当我们谈论 OnInit 时,Angular 2+ 中组件初始化的确切含义是啥?

单元测试读取数据库失败是啥原因