反模式 : 必须至少存在两个关键元素,才能正式地将实际的反模式与简单的坏习惯、坏实践或坏想法区分开来:
投票支持 TDD 反模式,您已经在“野外”看到过太多次了。 James Carr 的博客文章相关讨论: testdrivenddevelopment yahoogroup
如果你发现了一个“未命名”的. . 发布他们太。 每个反模式请一个职位使得投票有意义的东西。
我的既得利益是找到顶部 n 子集,这样我就可以在不久的将来的午餐会上讨论它们。
来源:James Carr的帖子。
“我如何测试我的成员变量而不使它们为公共…”只是用于单元测试?'
巨大的
一个单元测试,尽管它是有效地测试被测对象,但它可以跨越数千行,并包含许多许多测试用例。这可以是被测试系统是上帝的对象的指示符(James Carr的帖子)。
对于这种情况,一个明确的迹象是测试跨越了不止几行代码。通常,测试是如此复杂,以至于它开始包含自己的错误或不可靠的行为。
慢戳
运行极其缓慢的单元测试。当开发人员开始测试时,他们有时间去洗手间,抽根烟,或者更糟的是,在一天结束回家之前开始测试。(Src: 詹姆斯·卡尔的帖子)
也就是那些不会像它们应该的那样频繁地运行的测试
本地英雄
一个测试用例,它依赖于编写它的开发环境中特定的东西来运行。结果是测试通过了开发箱,但是当有人试图在其他地方运行它时失败了。
隐藏的依赖性
与本地英雄密切相关的单元测试,需要在测试运行之前填充一些现有数据。如果这些数据没有被填充,测试就会失败,并且几乎没有给开发人员留下它想要什么或为什么的提示……迫使他们挖掘大量的代码来找出它所使用的数据应该来自哪里。
令人遗憾的是,我们已经多次看到古老的.dll依赖于模糊而多样的.ini文件,这些文件在任何给定的生产系统上都是不同步的,更不用说在没有与负责这些dll的三个开发人员广泛协商的情况下在您的机器上存在。叹息。
过度的设置——James Carr 一个甚至开始测试都需要巨大设置的测试。有时要用几百行代码来为一个测试准备环境,涉及到几个对象,由于所有设置的“噪音”,这可能很难真正确定测试的是什么。(Src: 詹姆斯·卡尔的帖子) < / p >
以铁链锁住一群做苦工的囚犯
必须以特定顺序运行的几个测试,即一个测试改变了系统的全局状态(全局变量,数据库中的数据),而下一个测试依赖于它。
您经常在数据库测试中看到这种情况。测试不是在teardown()中进行回滚,而是将更改提交给数据库。另一个常见的原因是对全局状态的更改没有包装在try/finally块中,如果测试失败,这些块将被清理。
teardown()
肛门探测器
一个必须使用疯狂的、非法的或其他不健康的方式来执行其任务的测试,例如:使用Java的种setAccessible(真正的)读取私有字段或扩展一个类来访问受保护的字段/方法,或必须将测试放在某个包中以访问包的全局字段/方法。
如果您看到这种模式,则说明测试中的类使用了过多的数据隐藏。
这与Inspector之间的区别在于,被测类甚至试图隐藏需要测试的内容。因此,您的目标不是实现100%的测试覆盖率,而是能够测试任何东西。想象一个只有私有字段的类,一个没有参数的run()方法,根本没有getter。在不违反规则的情况下,没有办法进行测试。
run()
Michael Borgwardt评论:这并不是一个真正的测试反模式,它是处理被测试代码中的缺陷的实用主义。当然,最好是修复这些缺陷,但在第三方库的情况下,这可能是不可能的。
Aaron Digulla:我有点同意。也许这个条目真的更适合“JUnit HOWTO”wiki,而不是反模式。评论?
面貌极相似的人
为了测试某些东西,您必须将测试中的部分代码复制到具有相同名称和包的新类中,并且必须使用类路径魔法或自定义类加载器来确保它首先是可见的(这样您的副本就会被拾取)。
此模式表明您无法从测试中控制隐藏依赖项的不健康数量。
我看着他的脸……我的脸!它就像一面镜子,但让我的血液凝固。
蝴蝶
您必须测试一些包含随时变化的数据的东西,比如包含当前日期的结构,并且没有办法将结果固定为一个固定的值。糟糕的是,您根本不关心这个值。它只会使您的测试更加复杂,而不会增加任何价值。
它翅膀上的蝙蝠可以在世界的另一端引发飓风。——爱德华·洛伦兹,蝴蝶效应
共用夹具不当——Tim Ottinger 测试夹具中的几个测试用例甚至不使用或不需要设置/拆卸。部分原因是开发人员惰性地创建了一个新的测试夹具…向
搭便车/搭便车——James Carr, Tim Ottinger 与其编写一个新的测试用例方法来测试另一个截然不同的特性/功能,不如在现有的测试用例中使用一个新的断言(及其相应的操作,即从AAA开始的Act步骤)
布谷鸟——Frank Carver 一个单元测试,它与其他几个测试用例一起位于一个测试用例中,并享受与测试用例中的其他测试相同的(可能很长的)设置过程,但随后会从设置中丢弃部分或全部工件并创建自己的 高级症状:共用夹具不当
母鸡妈妈——Frank Carver 一个通用的设置,它所做的远远超过实际测试用例所需要的。例如,创建各种复杂的数据结构,填充明显重要和唯一的值,而测试只断言存在或不存在某些东西 高级症状:共用夹具不当
我不知道它能做什么…以防万一,我还是加进去了。—匿名开发人员
愉快路径
测试保持在满意的路径上(即预期的结果),而不测试边界和异常。
JUnit反模式
秘密捕手——Frank Carver 由于缺少断言,乍一看似乎没有进行任何测试的测试。但是“细节决定成败”。测试实际上依赖于要抛出的异常,并期望测试框架捕获异常并将其作为失败报告给用户。< / p >
[Test] public void ShouldNotThrow() { DoSomethingThatShouldNotThrowAnException(); }
沉默的捕手——Kelly?< br > 如果抛出异常则通过的测试。即使实际发生的异常与开发人员预期的异常不同 参见:秘密的捕手
[Test] [ExpectedException(typeof(Exception))] public void ItShouldThrowDivideByZeroException() { // some code that throws another exception yet passes the test }
二等公民 -测试代码不像生产代码那样重构,包含大量重复的代码,使得维护测试变得困难。
没有名字的测试—尼克佩洛
添加到错误跟踪器中以重现特定错误的测试,并且其作者认为不需要有自己的名称。不是增强现有的、缺乏的测试,而是创建一个名为testForBUG123的新测试。
两年后,当测试失败时,您可能需要首先尝试在错误跟踪器中找到bug -123,以弄清楚测试的意图。
40英尺杆子测试
由于害怕与要测试的类过于接近,这些测试与要测试的逻辑隔着无数抽象层和数千行代码。因此,它们非常脆弱,容易受到各种副作用的影响,这些副作用发生在往返感兴趣的班级的史诗般的旅程中。
图灵测试
由一些昂贵的工具自动生成的测试用例,该工具使用一些过于聪明的数据流分析,从被测类中收集了许多断言。让开发人员产生一种错误的信心,认为他们的代码经过了良好的测试,使他们免于设计和维护高质量测试的责任。如果机器可以为你编写测试,为什么它不能抽出手指来自己编写应用程序呢!
你好笨。——世界上最聪明的电脑给新学徒(来自旧的Amiga漫画)。
环境破坏者
一个用于各种“需求”的“单元”测试开始溢出到其环境中,使用和设置环境变量/端口。同时运行两个这样的测试会导致“端口不可用”异常等。
这些测试将是断断续续的,开发人员会说“再运行一次”之类的话。
我看到的一个解决方案是随机选择一个端口号来使用。这降低了冲突的可能性,但显然不能解决问题。因此,如果可以,总是模拟代码,这样它就不会分配不可共享资源。
沉睡者,又名维苏威火山—尼克佩洛
注定在未来某个特定时间和日期失败的测试。这通常是由于在测试使用Date或Calendar对象的代码时不正确的边界检查造成的。有时,如果在一天中非常特定的时间(如午夜)运行测试可能会失败。
不要将“The Sleeper”与“等着瞧”反模式混淆。
该代码早在2000年之前就将被替换—1960年的许多开发者
等着瞧
一种运行一些设置代码的测试,然后需要“等待”一段特定的时间,才能“看到”被测代码是否按预期运行。使用Thread.sleep()或等效的testMethod肯定是一个“等待和观察”测试。
通常,如果测试正在测试生成系统外部事件(如电子邮件、http请求或将文件写入磁盘)的代码,您可能会看到这种情况。
这样的测试也可以是当地的英雄,因为当它在较慢的机器或过载的CI服务器上运行时,它将失败。
不要将Wait and See反模式与的卧铺混淆。
通过GUI测试业务规则 是一种可怕的形式的耦合。如果 您编写了数千个测试 然后改变你的GUI, 成千上万的测试中断 相反,只通过GUI测试GUI内容,并将 图形用户界面以虚拟系统代替 真正的系统,当你做这些测试的时候。 通过API测试业务规则 不涉及GUI。——Bob Martin
__abc0——__abc1
闪烁测试(来源:Romilly Cocking)
一个测试只是偶尔失败,而不是在特定的时间,通常是由于测试中的竞争条件。通常在测试异步的东西时发生,比如JMS。
可能是'等着瞧'反模式和'的卧铺'反模式的超集。
构建失败了,那就再运行一次构建吧。—匿名开发人员
死树
一个创建了存根,但实际上没有编写测试的测试。
实际上,我在我们的产品代码中看到过这种情况:
class TD_SomeClass { public void testAdd() { assertEquals(1+1, 2); } }
我都不知道该怎么想了。
今天被这个咬了一口:
在我们的例子中,测试在“temp”目录中留下了一个文件,该文件具有第一次运行测试的用户的权限。当不同的用户尝试在同一台机器上测试时:砰。在James Carr网站上的评论中,Joakim Ohlrogge将其称为“邋遢的工人”,这也是“慷慨的剩菜”的灵感来源之一。我更喜欢我的名字(不那么侮辱人,更熟悉)。
线打击
乍一看,测试覆盖了所有内容,代码覆盖率工具也100%地证实了这一点,但实际上测试只击中了代码,没有任何输出分析。
< a href = " https://stackoverflow.com/questions/10129864/coverage-vs-reachable-code " > coverage-vs-reachable-code < / >
测试一切
我不敢相信直到现在还没有提到这一点,但测试不应该破坏单一责任原则。
我遇到过很多次这样的情况,破坏这个规则的测试从定义上来说是维护的噩梦。
连体双胞胎
人们称之为“单元测试”的测试实际上是集成测试,因为它们没有与依赖项(文件配置、数据库、服务,换句话说,其他没有在测试中测试的部分,人们懒惰而没有隔离)隔离开来,并且由于应该stub或mock的依赖项而失败。