反模式:必须至少有两个关键元素来正式区分实际的反模式与简单的坏习惯,不良做法或坏主意:
一些重复的行动模式,过程或结构最初似乎是有益的,但最终会产生比有益结果更糟糕的后果,并且
经过重构的解决方案,清晰记录,在实际操作中得到验证并可重复使用.
投票给你在"野外"看过一次太多的TDD反模式.
詹姆斯卡尔的博客文章和 关于testdrivendevelopment yahoogroup的相关讨论
如果你发现了一个'未命名的',那么也可以发布它们.每个反模式一个帖子请投票计算一些东西.
我的既得利益是找到前n个子集,以便我可以在不久的将来在午餐盒中讨论他们.
二等公民 - 测试代码不像生产代码那样重构,包含大量重复代码,使得很难维护测试.
Free Ride/Piggyback - James Carr,Tim Ottinger
而不是编写一个新的测试用例方法来测试另一个/不同的特性/功能,一个新的断言(及其相应的行为,即来自AAA的行为步骤)在现有的测试用例中使用.
快乐的道路
测试保持在快乐路径(即预期结果)而不测试边界和异常.
JUnit Antipatterns
当地英雄
一个测试用例,它依赖于为了运行而编写的开发环境的特定内容.结果是测试在开发框中传递,但是当有人试图在其他地方运行它时失败.
隐藏的依赖
与本地英雄密切相关的单元测试需要在测试运行之前的某个地方填充一些现有数据.如果没有填充该数据,测试将失败并且几乎没有给开发人员指示它想要什么,或者为什么......迫使他们挖掘大量代码以找出它所使用的数据应该来自何处.
可悲的是看到了这一点与依赖于它在不断出在任何给定的生产系统同步模糊的和多样的.ini文件古老的.dll太多的时间,让您的机器上现存单独与不负责这些dll三个开发商广泛协商.叹.
链帮
必须以特定顺序运行的几个测试,即一个测试更改系统的全局状态(全局变量,数据库中的数据)和下一个测试(取决于它).
您经常在数据库测试中看到这一点.测试不是进行回滚,而是teardown()
将更改提交到数据库.另一个常见原因是对全局状态的更改不包含在try/finally块中,如果测试失败则会清除.
嘲弄
有时候嘲弄可以很好,也很方便.但有时开发人员可能会迷失自己,并努力模拟未经测试的内容.在这种情况下,单元测试包含许多模拟,存根和/或伪造,甚至根本没有测试被测系统,而是从模拟返回的数据正在测试.
来源:詹姆斯卡尔的帖子.
沉默的守望者 - 凯莉?
如果抛出异常则通过的测试..即使实际发生的异常与开发人员预期的异常不同.
另见:秘密守望者
[Test] [ExpectedException(typeof(Exception))] public void ItShouldThrowDivideByZeroException() { // some code that throws another exception yet passes the test }
Inspector
一个单元测试,它违反封装以努力实现100%的代码覆盖率,但是对于对象中发生的事情非常了解,任何重构的尝试都会破坏现有的测试并要求任何更改反映在单元中测试.
"如何在不公开成员变量的情况下测试我的成员变量...... 仅用于单元测试?"
过度设置 - James Carr
需要大量设置才能开始测试的测试.有时几百行代码用于为一次测试准备环境,涉及多个对象,这使得由于所有设置的"噪声"而难以真正确定测试内容.(Src:James Carr的帖子)
肛门探针
必须使用疯狂,非法或其他不健康方式执行其任务的测试:使用Java的setAccessible(true)读取私有字段或扩展类以访问受保护的字段/方法或必须将测试放在某个包中以进行访问打包全局字段/方法.
如果您看到此模式,则测试中的类使用过多的数据隐藏.
这和Inspector之间的区别在于被测试的类试图隐藏你需要测试的东西.因此,您的目标不是实现100%的测试覆盖率,而是能够测试任何东西.想象一个只有私有字段的类,run()
没有参数的方法,根本没有getter.没有违反规则就没有办法测试这个.
Michael Borgwardt的评论:这不是一个真正的测试反模式,它是处理被测试代码缺陷的实用主义.当然最好解决这些缺陷,但在第三方库的情况下可能无法做到这一点.
Aaron Digulla:我有点同意.也许这个条目真的更适合"JUnit HOWTO"维基而不是反模式.评论?
没有名字的测试 - Nick Pellow
添加的测试用于重现错误跟踪器中的特定错误,并且其作者认为不保证自己的名称.不是增强现有的缺乏测试,而是创建一个名为testForBUG123的新测试.
两年后,当测试失败时,您可能需要首先尝试在您的错误跟踪器中找到BUG-123以找出测试的意图.
慢捅
单位测试运行得非常慢.当开发人员开始时,他们有时间去洗手间,抽烟,或者更糟糕的是,在一天结束回家之前将测试关掉.(Src:James Carr的帖子)
又称不会按照应有的频率运行的测试
蝴蝶
您必须测试包含始终更改的数据的内容,例如包含当前日期的结构,并且无法将结果固定为固定值.丑陋的部分是你完全不关心这个价值.它只会使您的测试更复杂而不添加任何值.
它的翼蝙蝠可以在世界的另一边引起飓风.- 爱德华洛伦兹,蝴蝶效应
等着瞧
运行一些设置代码然后需要"等待"一段特定时间才能"看到"被测代码是否按预期运行的测试.使用Thread.sleep()或等效的testMethod肯定是"等待和看见"测试.
通常,如果测试是测试生成系统外部事件的代码(如电子邮件,http请求或将文件写入磁盘),则可能会看到此情况.
这样的测试也可能是本地英雄,因为它在较慢的盒子或过载的CI服务器上运行时会失败.
Wait and See反模式不要与The Sleeper相混淆.
闪烁测试(来源:Romilly Cocking)
偶尔失败的测试,而不是在特定时间,并且通常是由于测试中的竞争条件.通常在测试异步的东西时发生,例如JMS.
可能是超级设置为" 观望 "反模式和" 沉睡者 "反模式.
构建失败,哦,好吧,再次运行构建. - 匿名开发者
不恰当的共享夹具 - Tim Ottinger
测试夹具中的几个测试用例甚至不使用或需要设置/拆卸.部分原因是由于开发人员的惯性来创建一个新的测试夹具...更容易再添加一个测试用例
巨人
单元测试虽然可以有效地测试被测对象,但可以跨越数千行并包含许多测试用例.这可以指示被测系统是上帝对象(詹姆斯卡尔的帖子).
这个测试的一个明确标志是跨越几行代码的测试.通常,测试非常复杂,以至于它开始包含其自身或片状行为的错误.
当我看到一些闪烁的GUI时,我会相信这是
一个不健康的固定/痴迷通过它的GUI测试应用程序'就像一个真正的用户'
通过GUI测试业务规则是一种可怕的耦合形式.如果您通过GUI编写数千个测试,然后更改GUI,则会有数千个测试中断.
相反,当您运行这些测试时,通过GUI仅测试GUI事物,并将GUI耦合到虚拟系统而不是真实系统.通过不涉及GUI的API测试业务规则. - 鲍勃马丁
"你必须明白,眼见为实,但也知道相信正在看到." - Denis Waitley
沉睡者,又名维苏威火山 - 尼克波洛
在某个特定时间和日期注定要失败的测试.这通常是在测试使用Date或Calendar对象的代码时由不正确的边界检查引起的.有时,如果在一天中非常特定的时间(例如午夜)运行,测试可能会失败.
"沉睡者"不要与" 等待和看见 "反模式相混淆.
该代码将在2000年之前很久就被替换 - 1960年的许多开发人员
Cuckoo - Frank Carver
一个单元测试,与其他几个测试案例一起位于测试用例中,并且与测试用例中的其他测试一样享有相同(可能很长)的设置过程,但随后从设置中丢弃部分或全部工件并创造自己的.
高级症状:不恰当的共享夹具
死树
创建存根的测试,但实际上没有编写测试.
我在生产代码中实际看到过这个:
class TD_SomeClass { public void testAdd() { assertEquals(1+1, 2); } }
我甚至不知道该怎么想.
今天得到了一点:
湿地:
测试创建在某处持久保存的数据,但测试在完成时不会清理.这导致测试(相同的测试,或可能是其他测试)在后续测试运行中失败.
在我们的例子中,测试在"临时"目录中留下了一个文件,其中包含第一次运行测试的用户的权限.当不同的用户试图在同一台机器上进行测试时:繁荣.在James Carr网站的评论中,Joakim Ohlrogge将其称为"邋Worker的工人",这是"慷慨剩饭"的灵感的一部分.我更喜欢我的名字(更少侮辱,更熟悉).
秘密捕手 - 弗兰克卡弗
由于没有断言,乍一看似乎没有进行测试.但是"魔鬼在细节中"......测试实际上是依赖于抛出异常并期望测试框架捕获异常并将其作为失败报告给用户.
[Test] public void ShouldNotThrow() { DoSomethingThatShouldNotThrowAnException(); }
四十英尺极点测试
由于害怕过于接近他们试图测试的类,这些测试在远处进行,由无数的抽象层和他们正在检查的逻辑中的数千行代码分开.因此,它们非常脆弱,容易受到各种感兴趣的史诗之旅的各种副作用的影响.
图灵测试
一个由一些昂贵的工具自动生成的测试用例,它使用一些过于聪明的数据流分析,从被测试的类中收集了许多很多断言.让开发人员误以为他们的代码经过了充分的测试,使他们免于承担设计和维护高质量测试的责任.如果机器可以为你编写测试,为什么它不能拉出来并自己编写应用程序!
你好笨. - 世界上最聪明的电脑给新学徒(来自古老的Amiga漫画).
环境破坏者
针对各种"要求"的"单元"测试开始扩散到其环境中,使用和设置环境变量/端口.同时运行其中两个测试将导致"不可用端口"异常等.
这些测试将是间歇性的,让开发人员说"再次运行".
我见过的一个解决方案是随机选择要使用的端口号.这减少了冲突的可能性,但显然无法解决问题.因此,如果可以,请始终模拟代码,以便它实际上不会分配不可共享的资源.
分身
为了测试某些东西,你必须将部分测试代码复制到一个具有相同名称和包的新类中,你必须使用classpath magic或自定义类加载器来确保它首先可见(因此你的副本被选中)向上).
此模式表示您无法通过测试控制的不健康的隐藏依赖项.
我看着他的脸......我的脸!它就像一面镜子,但让我的血液冻结了.
母鸡 - 弗兰克卡弗
一种常见的设置,其功能远远超过实际测试用例所需.例如,当测试仅声明某些事物的存在与否时,创建各种复杂数据结构,这些数据结构填充了明显重要且唯一的值.
高级症状:不恰当的共享夹具
我不知道它做了什么......无论如何我都要添加它,以防万一. - 匿名开发者
全部测试
我不敢相信到目前为止还没有提到这一点,但是测试不应该违反单一责任原则.
我遇到过这么多次,违反这条规则的测试从定义来说是一个难以维持的噩梦.
线路击球手
在第一次看测试涵盖了一切,代码覆盖工具100%确认它,但实际上测试只在没有任何输出分析的情况下命中代码.
覆盖-VS-可达代码