203

反模式:必须至少存在两个关键元素才能正式区分实际的反模式与简单的坏习惯、坏做法或坏主意:

  • 一些重复的行动模式、过程或结构,最初看起来是有益的,但最终产生的不良后果多于有益结果,以及
  • 一个重构的解决方案,有明确的文档记录,在实际实践中得到证明并且可重复。

投票支持您曾经多次“在野外”看到的 TDD 反模式。
James Carr 的博客文章和 关于测试驱动开发的相关讨论 yahoogroup

如果您发现了一个“未命名”的.. 也发布它们。请为每个反模式发布一篇文章,以使选票有意义。

我的既得利益是找到前 n 个子集,以便我可以在不久的将来在午餐盒会议上讨论它们。

4

31 回答 31

70

二等公民- 测试代码没有像生产代码那样重构,包含大量重复代码,难以维护测试。

于 2008-12-02T14:37:26.600 回答
67

搭便车/捎带——James Carr、Tim Ottinger
不是编写新的测试用例方法来测试另一个/不同的特性/功能,而是在现有的测试用例中使用新的断言(及其相应的操作,即从 AAA 执行的步骤) .

于 2008-12-02T13:15:03.990 回答
64

快乐之路

测试保持在愉快的路径上(即预期的结果),而不测试边界和异常。

JUnit 反模式

于 2008-12-02T13:43:08.270 回答
59

当地英雄

一个测试用例,它依赖于特定于编写它的开发环境才能运行。结果是测试通过了开发盒,但是当有人试图在其他地方运行它时失败了。

隐藏的依赖

与本地英雄密切相关,这是一个单元测试,需要在测试运行之前在某处填充一些现有数据。如果没有填充该数据,测试将失败,并且几乎不会向开发人员说明它想要什么,或者为什么……迫使他们挖掘大量代码以找出它所使用的数据应该来自哪里。


可悲的是,很多次使用古老的 .dll 依赖于模糊多样的 .ini 文件,这些文件在任何给定的生产系统上都经常不同步,更不用说在没有与负责这些 dll 的三个开发人员进行广泛协商的情况下存在于您的机器上。叹。

于 2008-12-02T11:40:20.670 回答
58

链帮

必须按特定顺序运行的几个测试,即一个测试更改系统的全局状态(全局变量、数据库中的数据),下一个测试依赖于它。

您经常在数据库测试中看到这一点。测试不是在 中进行回滚,而是teardown()将它们的更改提交到数据库。另一个常见的原因是对全局状态的更改没有包含在 try/finally 块中,如果测试失败,这些块会清理它们。

于 2008-12-02T12:30:01.553 回答
56

嘲弄
有时候,嘲弄可能很好,而且很方便。但有时开发人员可能会迷失自我,并努力模拟未测试的内容。在这种情况下,单元测试包含如此多的模拟、存根和/或伪造,以至于被测系统甚至根本没有被测试,而是从模拟返回的数据是正在测试的。

资料来源:詹姆斯卡尔的帖子。

于 2008-12-02T11:25:13.150 回答
40

沉默的捕手——凯利?
如果抛出异常,则测试通过..即使实际发生的异常与开发人员预期的异常不同。
另见:秘密捕手

[Test]
[ExpectedException(typeof(Exception))]
public void ItShouldThrowDivideByZeroException()
{
   // some code that throws another exception yet passes the test
}
于 2008-12-02T14:17:34.457 回答
34

Excessive Setup -- James Carr
一项测试需要大量设置才能开始测试。有时数百行代码用于为一项测试准备环境,其中涉及多个对象,由于所有设置正在进行的“噪音”,这可能难以真正确定测试的内容。(来源:詹姆斯卡尔的帖子

于 2008-12-02T11:57:44.597 回答
34

Inspector
一种单元测试,它违反封装以实现 100% 的代码覆盖率,但对对象中发生的事情非常了解,以至于任何重构尝试都会破坏现有测试并需要在单元中反映任何更改测试。


“我如何在不公开它们的情况下测试我的成员变量……用于单元测试?”

于 2008-12-02T11:26:25.457 回答
32

肛门探针

必须使用疯狂、非法或其他不健康的方式来执行其任务的测试,例如:使用 Java 的setAccessible(true)读取私有字段或扩展类以访问受保护的字段/方法或必须将测试放在某个包中才能访问打包全局字段/方法。

如果您看到这种模式,则测试中的类使用了太多的数据隐藏。

这与 The Inspector 的区别在于,被测试的类甚至试图隐藏你需要测试的东西。所以你的目标不是达到 100% 的测试覆盖率,而是能够测试任何东西。想想一个只有私有字段的类,一个run()没有参数且根本没有 getter 的方法。在不违反规则的情况下,没有办法对此进行测试。


Michael Borgwardt 的评论:这并不是真正的测试反模式,而是处理被测试代码中的缺陷的实用主义。当然,最好修复这些缺陷,但对于 3rd 方库,这可能是不可能的。

Aaron Digulla:我有点同意。也许这个条目真的更适合“JUnit HOWTO”wiki 而不是反模式。注释?

于 2008-12-02T12:35:11.400 回答
26

没有名字的测试——尼克·佩洛

为重现错误跟踪器中的特定错误而添加的测试,其作者认为不保证有自己的名称。创建一个名为 testForBUG123 的新测试,而不是增强现有的、缺乏的测试。

两年后,当该测试失败时,您可能需要首先尝试在错误跟踪器中找到 BUG-123 以确定测试的意图。

于 2008-12-03T10:35:20.300 回答
25

慢戳

运行速度非常慢的单元测试。当开发人员开始测试时,他们有时间去洗手间,抽根烟,或者更糟糕的是,在一天结束回家之前开始测试。(来源:詹姆斯卡尔的帖子

也就是那些不会像他们应该的那样频繁运行的测试

于 2008-12-02T11:30:23.483 回答
20

蝴蝶

您必须测试包含始终变化的数据的东西,例如包含当前日期的结构,并且无法将结果确定为固定值。丑陋的部分是你根本不关心这个值。它只会使您的测试更加复杂,而不会增加任何价值。

它翅膀的蝙蝠可以在世界的另一端引起飓风。——爱德华·洛伦兹,蝴蝶效应

于 2008-12-02T12:46:21.533 回答
19

等着瞧

运行一些设置代码的测试,然后需要“等待”特定的时间,然后才能“查看”被测代码是否按预期运行。使用 Thread.sleep() 或等效的 testMethod 肯定是“等待和查看”测试。

通常,如果测试正在测试生成系统外部事件(例如电子邮件、http 请求或将文件写入磁盘)的代码,您可能会看到这一点。

这样的测试也可能是本地英雄,因为在较慢的机器或过载的 CI 服务器上运行时它会失败。

不要将 Wait and See 反模式与The Sleeper混淆。

于 2008-12-04T01:12:47.923 回答
19

闪烁测试(来源:Romilly Cocking)

只是偶尔失败的测试,而不是在特定时间失败,通常是由于测试中的竞争条件。通常在测试异步的东西时发生,例如 JMS。

可能是“ Wait and See ”反模式和“ The Sleeper ”反模式的超级集。

构建失败,哦,好吧,再次运行构建。-- 匿名开发者

于 2008-12-04T09:37:39.137 回答
17

不恰当地共享夹具——Tim Ottinger
测试夹具中的几个测试用例甚至不使用或不需要设置/拆卸。部分是由于开发人员惯性创建一个新的测试夹具......更容易添加一个测试用例堆

于 2008-12-02T13:12:14.960 回答
16

巨人

一个单元测试,虽然它有效地测试被测对象,但可以跨越数千行并包含许多测试用例。这可能表明被测系统是上帝对象(James Carr 的帖子)。

一个确定的标志是一个跨越几行代码的测试。通常,测试非常复杂,以至于它开始包含自身的错误或不稳定的行为。

于 2008-12-02T11:27:25.703 回答
15
于 2008-12-04T07:39:32.947 回答
14

沉睡者,又名维苏威火山——尼克·佩洛

注定在未来某个特定时间和日期失败的测试。这通常是由于在测试使用 Date 或 Calendar 对象的代码时不正确的边界检查引起的。有时,如果在一天中非常特定的时间(例如午夜)运行,测试可能会失败。

'The Sleeper' 不应与' Wait And See ' 反模式混淆。

该代码将在 2000 年之前被替换——许多开发人员在 1960 年

于 2008-12-04T01:01:30.240 回答
11

死树

创建存根的测试,但实际上并未编写测试。

我实际上已经在我们的生产代码中看到了这一点:

class TD_SomeClass {
  public void testAdd() {
    assertEquals(1+1, 2);
  }
}

我什至不知道该怎么想。

于 2009-10-06T16:10:10.153 回答
11

今天被这个咬了:

Wet Floor
测试创建的数据保存在某处,但测试完成后不会清理。这会导致测试(相同的测试,或可能的其他测试)在后续测试运行中失败。

在我们的例子中,测试在“temp”目录中留下了一个文件,该文件具有第一次运行测试的用户的权限。当不同的用户尝试在同一台机器上进行测试时:繁荣。在 James Carr 网站上的评论中,Joakim Ohlrogge 将其称为“邋遢的工人”,这也是“慷慨的剩菜”灵感的一部分。我更喜欢我的名字(不那么侮辱,更熟悉)。

于 2010-01-27T23:01:51.093 回答
11

The Cuckoo -- Frank Carver
一个单元测试,它与其他几个测试一起放在一个测试用例中,并享受与测试用例中的其他测试相同(可能很长)的设置过程,但随后丢弃了设置中的部分或全部工件并创建自己的。
高级症状:不恰当地共享夹具

于 2008-12-02T13:23:06.937 回答
10

The Secret Catcher -- Frank Carver
由于没有断言,乍一看似乎没有进行测试的测试。但是“魔鬼在细节中”..测试实际上是依赖于抛出异常并期望测试框架捕获异常并将其作为失败报告给用户。

[Test]
public void ShouldNotThrow()
{
   DoSomethingThatShouldNotThrowAnException();
}
于 2008-12-02T14:07:53.243 回答
10

四十英尺杆测试

由于害怕与他们试图测试的类太近,这些测试在远处进行,被无数抽象层和数千行代码与他们正在检查的逻辑隔开。因此,它们非常脆弱,并且容易受到在往返感兴趣的班级的史诗般的旅程中发生的各种副作用的影响。

于 2008-12-04T00:32:59.877 回答
10

图灵测试

一个测试用例由一些昂贵的工具自动生成,该工具有很多很多断言,这些断言是使用一些过于聪明的半数据流分析从被测类中收集的。让开发人员误以为他们的代码已经过良好测试,从而免除了他们设计和维护高质量测试的责任。如果机器可以为您编写测试,为什么它不能伸出手指并自己编写应用程序!

你好笨蛋。-- 世界上最聪明的电脑到新学徒(来自旧的 Amiga 漫画)。

于 2008-12-04T00:42:43.727 回答
10

环境破坏者

针对各种“要求”的“单元”测试开始溢出到其环境中,使用和设置环境变量/端口。同时运行其中两个测试将导致“不可用端口”异常等。

这些测试将是间歇性的,让开发人员说“再运行一次”之类的话。

我见过的一种解决方案是随机选择要使用的端口号。这减少了发生冲突的可能性,但显然不能解决问题。因此,如果可以,请始终模拟代码,使其实际上不会分配不可共享的资源。

于 2008-12-04T00:58:22.617 回答
9

分身

为了测试某些东西,您必须将部分被测代码复制到具有相同名称和包的新类中,并且您必须使用类路径魔术或自定义类加载器来确保它首先可见(因此选择您的副本向上)。

此模式表明您无法从测试中控制的隐藏依赖项数量不正常。

我看着他的脸……我的脸!它就像一面镜子,却让我的血液冻结。

于 2008-12-02T12:39:36.383 回答
7

测试一切

我不敢相信直到现在还没有提到这一点,但测试不应该违反单一责任原则

我已经遇到过很多次了,打破这条规则的测试显然是一场噩梦。

于 2012-09-30T04:15:55.817 回答
7

The Mother Hen -- Frank Carver
一个常见的设置,它的功能远远超出了实际测试用例的需要。例如,当测试仅断言某物的存在或不存在时,创建各种复杂的数据结构,其中填充了明显重要且唯一的值。
高级症状:不恰当地共享夹具

我不知道它是做什么的......无论如何我都会添加它,以防万一。-- 匿名开发者

于 2008-12-02T13:26:10.737 回答
6

线击球手

乍一看,测试涵盖了所有内容,代码覆盖率工具 100% 确认了这一点,但实际上测试只命中代码而没有任何输出分析。

覆盖率与可达代码

于 2012-04-25T20:34:02.443 回答
0

连体双胞胎

人们称之为“单元测试”但实际上是集成测试的测试,因为它们没有与依赖项隔离(文件配置,数据库,服务,换句话说,在你的测试中没有测试的部分,人们变得懒惰并且没有隔离)并且由于应该被存根或模拟的依赖关系而失败。

于 2015-01-26T09:26:40.340 回答