单元测试 反模式目录
反模式:必须至少有两个关键元素提出来正式区分实际的反模式与简单的坏习惯、坏实践或坏想法:
- 一些重复的行为、过程或结构模式,最初看起来是有益的,但最终产生的坏后果比有益结果更多,并且
- A重构的解决方案有清晰的记录,在实际实践中得到证明并且可重复。
为您“在野外”见过太多次的 TDD 反模式投票。
James Carr 的博客文章 和 有关 testdrivendevelopment yahoogroup 的相关讨论
如果您发现了一个“未命名”的帖子..他们也是。 每个反模式一个帖子,以使投票有价值。
我的既得利益是找到前 n 个子集,以便我可以在不久的将来的午餐会上讨论它们。
anti-pattern : there must be at least two key elements present to formally distinguish an actual anti-pattern from a simple bad habit, bad practice, or bad idea:
- Some repeated pattern of action, process or structure that initially appears to be beneficial, but ultimately produces more bad consequences than beneficial results, and
- A refactored solution that is clearly documented, proven in actual practice and repeatable.
Vote for the TDD anti-pattern that you have seen "in the wild" one time too many.
The blog post by James Carr and Related discussion on testdrivendevelopment yahoogroup
If you've found an 'unnamed' one.. post 'em too. One post per anti-pattern please to make the votes count for something.
My vested interest is to find the top-n subset so that I can discuss 'em in a lunchbox meet in the near future.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
闪烁测试(来源:Romilly Cocking)
该测试只是偶尔失败,而不是在特定时间,通常是由于测试中的竞争条件造成的。 通常在测试异步内容(例如 JMS)时发生。
可能是“等待看看”反模式和“The Sleeper' 反模式。
构建失败,哦,好吧,再次运行构建即可。 -- 匿名开发者
The Flickering Test (Source : Romilly Cocking)
A test which just occasionally fails, not at specific times, and is generally due to race conditions within the test. Typically occurs when testing something that is asynchronous, such as JMS.
Possibly a super set to the 'Wait and See' anti-pattern and 'The Sleeper' anti-pattern.
The build failed, oh well, just run the build again. -- Anonymous Developer
等待并查看
运行一些设置代码的测试,然后需要“等待”特定的时间才能“查看”被测代码是否按预期运行。 使用 Thread.sleep() 或等效方法的 testMethod 无疑是“等待并查看”测试。
通常,如果测试正在测试生成系统外部事件(例如电子邮件、http 请求或将文件写入磁盘)的代码,您可能会看到此情况。
这样的测试也可能是本地英雄,因为在较慢的速度上运行时它会失败盒子或超载的 CI 服务器。
观望反模式不要与睡眠者混淆。
Wait and See
A test that runs some set up code and then needs to 'wait' a specific amount of time before it can 'see' if the code under test functioned as expected. A testMethod that uses Thread.sleep() or equivalent is most certainly a "Wait and See" test.
Typically, you may see this if the test is testing code which generates an event external to the system such as an email, an http request or writes a file to disk.
Such a test may also be a Local Hero since it will FAIL when run on a slower box or an overloaded CI server.
The Wait and See anti-pattern is not to be confused with The Sleeper.
不恰当地共享固定装置 -- Tim Ottinger
测试夹具中的几个测试用例甚至不使用或不需要设置/拆卸。 部分原因是开发人员创建新测试夹具的惯性......更容易在一堆测试用例中添加一个测试用例
Inappropriately Shared Fixture -- Tim Ottinger
Several test cases in the test fixture do not even use or need the setup / teardown. Partly due to developer inertia to create a new test fixture... easier to just add one more test case to the pile
巨人
一个单元测试,虽然它有效地测试了被测对象,但可以跨越数千行并包含许多测试用例。 这可以表明被测试的系统是一个God Object(James Carr 的帖子)。
对此的一个明确标志是测试跨越了几行代码。 通常,测试非常复杂,以至于它开始包含其自身的错误或不稳定的行为。
The Giant
A unit test that, although it is validly testing the object under test, can span thousands of lines and contain many many test cases. This can be an indicator that the system under tests is a God Object (James Carr's post).
A sure sign for this one is a test that spans more than a a few lines of code. Often, the test is so complicated that it starts to contain bugs of its own or flaky behavior.
当我看到一些闪烁的 GUI 时我就会相信
对通过 GUI 测试应用程序“就像真正的用户一样”的不健康的执着/痴迷
“你必须明白眼见为实,但也知道相信就是所见。” -- 丹尼斯·维特利
I'll believe it when I see some flashing GUIs
An unhealthy fixation/obsession with testing the app via its GUI 'just like a real user'
“You must understand that seeing is believing, but also know that believing is seeing.” -- Denis Waitley
沉睡者,又名维苏威火山 -- Nick Pellow
注定会在未来某个特定时间和日期失败的测试。 这通常是由于测试使用日期或日历对象的代码时边界检查不正确造成的。 有时,如果在一天中的特定时间(例如午夜)运行,测试可能会失败。
不要将“沉睡者”与“等等看”相混淆。图案。
早在 2000 年之前,这些代码就会被替换——1960 年的许多开发人员
The Sleeper, aka Mount Vesuvius -- Nick Pellow
A test that is destined to FAIL at some specific time and date in the future. This often is caused by incorrect bounds checking when testing code which uses a Date or Calendar object. Sometimes, the test may fail if run at a very specific time of day, such as midnight.
'The Sleeper' is not to be confused with the 'Wait And See' anti-pattern.
That code will have been replaced long before the year 2000 -- Many developers in 1960
死树
创建存根但实际上并未编写测试的测试。
我实际上在我们的生产代码中看到了这一点:
我什至不知道该怎么想。
The Dead Tree
A test which where a stub was created, but the test wasn't actually written.
I have actually seen this in our production code:
I don't even know what to think about that.
今天被这个困扰了:
湿地板:
测试创建的数据保留在某处,但测试完成后不会清理。 这会导致测试(同一测试,或可能是其他测试)在后续测试运行中失败。
在我们的例子中,测试在“temp”目录中留下了一个文件,并具有第一次运行测试的用户的权限。 当不同的用户尝试在同一台机器上进行测试时:繁荣。 在詹姆斯·卡尔网站的评论中,乔金·奥尔罗格将其称为“马虎工人”,这也是《慷慨的剩菜》的灵感之一。 我更喜欢我的名字(更少侮辱,更熟悉)。
got bit by this today:
Wet Floor:
The test creates data that is persisted somewhere, but the test does not clean up when finished. This causes tests (the same test, or possibly other tests) to fail on subsequent test runs.
In our case, the test left a file lying around in the "temp" dir, with permissions from the user that ran the test the first time. When a different user tried to test on the same machine: boom. In the comments on James Carr's site, Joakim Ohlrogge referred to this as the "Sloppy Worker", and it was part of the inspiration for "Generous Leftovers". I like my name for it better (less insulting, more familiar).
布谷鸟——弗兰克·卡弗
一个单元测试与其他几个测试用例一起位于一个测试用例中,并且与测试用例中的其他测试具有相同的(可能很长)设置过程,但随后会丢弃设置中的部分或全部工件并创建自己的工件。< br>
高级症状:不恰当地共享夹具
The Cuckoo -- Frank Carver
A unit test which sits in a test case with several others, and enjoys the same (potentially lengthy) setup process as the other tests in the test case, but then discards some or all of the artifacts from the setup and creates its own.
Advanced Symptom of : Inappropriately Shared Fixture
秘密捕手——弗兰克·卡弗
由于缺乏断言,乍一看似乎没有进行任何测试。 但是“细节决定成败”..测试实际上依赖于抛出异常,并期望测试框架捕获异常并将其作为失败报告给用户。
The Secret Catcher -- Frank Carver
A test that at first glance appears to be doing no testing, due to absence of assertions. But "The devil is in the details".. the test is really relying on an exception to be thrown and expecting the testing framework to capture the exception and report it to the user as a failure.
环境破坏者
针对各种“要求”的“单元”测试开始溢出到其环境中,使用和设置环境变量/端口。 同时运行其中两个测试将导致“端口不可用”异常等。
这些测试将是间歇性的,并让开发人员说“只需再次运行它”之类的事情。
我见过的一种解决方案是随机选择要使用的端口号。 这减少了冲突的可能性,但显然并不能解决问题。 因此,如果可以的话,请始终模拟代码,以便它实际上不会分配不可共享的资源。
The Environmental Vandal
A 'unit' test which for various 'requirements' starts spilling out into its environment, using and setting environment variables / ports. Running two of these tests simultaneously will cause 'unavailable port' exceptions etc.
These tests will be intermittent, and leave developers saying things like 'just run it again'.
One solution Ive seen is to randomly select a port number to use. This reduces the possibility of a conflict, but clearly doesnt solve the problem. So if you can, always mock the code so that it doesn't actually allocate the unsharable resource.
图灵测试
由一些昂贵的工具自动生成的测试用例,该工具使用一些过于聪明的数据流分析从被测类中收集了许多很多断言。 让开发人员产生一种错误的信心,认为他们的代码经过了良好的测试,从而免除了他们设计和维护高质量测试的责任。 如果机器可以为你编写测试,为什么它不能拔出手指并自己编写应用程序!
你好傻瓜。 -- 给新学徒的世界上最聪明的计算机(来自旧的 Amiga 漫画)。
The Turing Test
A testcase automagically generated by some expensive tool that has many, many asserts gleaned from the class under test using some too-clever-by-half data flow analysis. Lulls developers into a false sense of confidence that their code is well tested, absolving them from the responsibility of designing and maintaining high quality tests. If the machine can write the tests for you, why can't it pull its finger out and write the app itself!
Hello stupid. -- World's smartest computer to new apprentice (from an old Amiga comic).
四十英尺杆测试
由于害怕与他们要测试的类太接近,这些测试的作用距离很远,被无数的抽象层和数千行代码与它们的逻辑分开检查。 因此,它们非常脆弱,并且容易受到往返于感兴趣类别的史诗般的旅程中发生的各种副作用的影响。
The Forty Foot Pole Test
Afraid of getting too close to the class they are trying to test, these tests act at a distance, separated by countless layers of abstraction and thousands of lines of code from the logic they are checking. As such they are extremely brittle, and susceptible to all sorts of side-effects that happen on the epic journey to and from the class of interest.
Doppelgänger
为了测试某些内容,您必须将被测试的部分代码复制到具有相同名称和包的新类中,并且必须使用类路径魔法或自定义类加载器来确保它是首先可见(这样您的副本就会被选中)。
此模式表明存在大量隐藏依赖项,您无法通过测试对其进行控制。
我看着他的脸……我的脸! 它就像一面镜子,但却让我的血液冻结。
Doppelgänger
In order to test something, you have to copy parts of the code under test into a new class with the same name and package and you have to use classpath magic or a custom classloader to make sure it is visible first (so your copy is picked up).
This pattern indicates an unhealthy amount of hidden dependencies which you can't control from a test.
I looked at his face ... my face! It was like a mirror but made my blood freeze.
母鸡——弗兰克·卡弗
一种常见的设置,其功能远远超出了实际测试用例的需要。 例如,当测试仅断言某些内容是否存在时,创建各种复杂的数据结构,其中填充了明显重要且唯一的值。
高级症状:不恰当地共享夹具
我不知道它的作用...无论如何我都会添加它,以防万一。 -- 匿名开发者
The Mother Hen -- Frank Carver
A common setup which does far more than the actual test cases need. For example creating all sorts of complex data structures populated with apparently important and unique values when the tests only assert for presence or absence of something.
Advanced Symptom of: Inappropriately Shared Fixture
I don't know what it does ... I'm adding it anyway, just in case. -- Anonymous Developer
全部测试
我不敢相信到现在为止还没有提到这一点,但是测试不应该打破单一职责原则。
我已经多次遇到过这种情况,违反此规则的测试显然是维护的一场噩梦。
The Test It All
I can't believe this hasn't been mentioned till now, but tests should not break the Single Responsibility Principle.
I have come across this so many times, tests that break this rule are by definition a nightmare to maintain.
Line hitter
乍一看,测试涵盖了所有内容,并且代码覆盖率工具以 100% 确认了这一点,但实际上测试仅命中代码,而没有任何输出分析。
覆盖率-vs-reachable-code
Line hitter
On the first look tests covers everything and code coverage tools confirms it with 100%, but in reality tests only hit code without any output analyses.
coverage-vs-reachable-code
二等公民 - 测试代码不像生产代码那样重构得很好,包含大量重复代码,使得维护测试变得困难。
Second Class Citizens - test code isn't as well refactored as production code, containing a lot of duplicated code, making it hard to maintain tests.
搭便车/背驮——詹姆斯·卡尔、蒂姆·奥廷格
不是编写新的测试用例方法来测试另一个/独特的特性/功能,而是在现有的测试用例中添加新的断言(及其相应的操作,即 AAA 的操作步骤)。
The Free Ride / Piggyback -- James Carr, Tim Ottinger
Rather than write a new test case method to test another/distinct feature/functionality, a new assertion (and its corresponding actions i.e. Act steps from AAA) rides along in an existing test case.
快乐路径
测试保持在快乐路径(即预期结果)上,而不测试边界和异常。
JUnit 反模式
Happy Path
The test stays on happy paths (i.e. expected results) without testing for boundaries and exceptions.
JUnit Antipatterns
本地英雄
一个测试用例,它依赖于特定于其编写的开发环境才能运行。 结果是测试在开发盒上通过,但当有人尝试在其他地方运行它时失败。
隐藏依赖
与本地英雄密切相关,这是一个单元测试,需要在测试运行之前在某处填充一些现有数据。 如果未填充该数据,测试将失败,并且几乎没有向开发人员留下任何指示,说明他们想要什么,或者为什么……迫使他们挖掘大量代码以找出它所使用的数据应该来自哪里。
遗憾的是,这种古老的 .dll 依赖于模糊且多样的 .ini 文件,这些文件在任何给定的生产系统上都经常不同步,更不用说在没有与负责这些 dll 的三个开发人员广泛协商的情况下就存在于您的计算机上。 叹。
The Local Hero
A test case that is dependent on something specific to the development environment it was written on in order to run. The result is the test passes on development boxes, but fails when someone attempts to run it elsewhere.
The Hidden Dependency
Closely related to the local hero, a unit test that requires some existing data to have been populated somewhere before the test runs. If that data wasn’t populated, the test will fail and leave little indication to the developer what it wanted, or why… forcing them to dig through acres of code to find out where the data it was using was supposed to come from.
Sadly seen this far too many times with ancient .dlls which depend on nebulous and varied .ini files which are constantly out of sync on any given production system, let alone extant on your machine without extensive consultation with the three developers responsible for those dlls. Sigh.
Chain Gang
必须按一定顺序运行的几个测试,即一个测试更改系统的全局状态(全局变量、数据库中的数据),下一个测试取决于它。
您经常在数据库测试中看到这一点。 测试不是在teardown() 中进行回滚,而是将其更改提交到数据库。 另一个常见原因是全局状态的更改没有包含在 try/finally 块中,如果测试失败,这些块就会被清除。
Chain Gang
A couple of tests that must run in a certain order, i.e. one test changes the global state of the system (global variables, data in the database) and the next test(s) depends on it.
You often see this in database tests. Instead of doing a rollback in
teardown()
, tests commit their changes to the database. Another common cause is that changes to the global state aren't wrapped in try/finally blocks which clean up should the test fail.嘲讽
有时嘲笑可能很好,而且很方便。 但有时开发人员可能会迷失自己,并努力模拟未测试的内容。 在这种情况下,单元测试包含如此多的模拟、存根和/或伪造,以至于被测试的系统根本没有被测试,而是从模拟返回的数据正在被测试。
资料来源:詹姆斯·卡尔的帖子。
The Mockery
Sometimes mocking can be good, and handy. But sometimes developers can lose themselves and in their effort to mock out what isn’t being tested. In this case, a unit test contains so many mocks, stubs, and/or fakes that the system under test isn’t even being tested at all, instead data returned from mocks is what is being tested.
Source: James Carr's post.
沉默的捕手——凯利?
如果抛出异常,则测试通过。即使实际发生的异常与开发人员预期的异常不同。
另请参阅:秘密捕手
The Silent Catcher -- Kelly?
A test that passes if an exception is thrown.. even if the exception that actually occurs is one that is different than the one the developer intended.
See Also: Secret Catcher
督察
为了实现 100% 代码覆盖率而违反封装的单元测试,但对对象中发生的情况了解得太多,任何重构尝试都将破坏现有测试,并需要在单元测试中反映任何更改。
“如何测试我的成员变量而不公开它们......只是进行单元测试?”
The Inspector
A unit test that violates encapsulation in an effort to achieve 100% code coverage, but knows so much about what is going on in the object that any attempt to refactor will break the existing test and require any change to be reflected in the unit test.
'how do I test my member variables without making them public... just for unit-testing?'
过度设置——詹姆斯·卡尔
一项测试需要大量的设置才能开始测试。 有时,需要使用数百行代码来为一项测试准备环境,其中涉及多个对象,由于所有正在进行的设置的“噪音”,这可能会导致很难真正确定测试的内容。 (来源:詹姆斯·卡尔的帖子)
Excessive Setup -- James Carr
A test that requires a huge setup in order to even begin testing. Sometimes several hundred lines of code are used to prepare the environment for one test, with several objects involved, which can make it difficult to really ascertain what is tested due to the “noise” of all of the setup going on. (Src: James Carr's post)
肛门探针
必须使用疯狂、非法或其他不健康的方式来执行其任务的测试,例如:使用 Java 的 setAccessible(true) 或扩展类以访问受保护的字段/方法或具有将测试放在某个包中以访问包全局字段/方法。
如果您看到这种模式,则表明所测试的类使用了过多的数据隐藏。
这与 Inspector 之间的区别在于,被测试的类甚至试图隐藏您需要测试的内容。 因此,您的目标不是实现 100% 的测试覆盖率,而是能够测试任何内容。 想象一个只有私有字段的类,一个没有参数且根本没有 getter 的
run()
方法。 没有办法在不违反规则的情况下对此进行测试。Michael Borgwardt 的评论:这并不是真正的测试反模式,而是处理正在测试的代码中的缺陷的实用主义。 当然,最好修复这些缺陷,但对于第三方库来说这可能是不可能的。
Aaron Digulla:我有点同意。 也许这个条目确实更适合“JUnit HOWTO”wiki,而不是反模式。 评论?
Anal Probe
A test which has to use insane, illegal or otherwise unhealthy ways to perform its task like: Reading private fields using Java's setAccessible(true) or extending a class to access protected fields/methods or having to put the test in a certain package to access package global fields/methods.
If you see this pattern, the classes under test use too much data hiding.
The difference between this and The Inspector is that the class under test tries to hide even the things you need to test. So your goal is not to achieve 100% test coverage but to be able to test anything at all. Think of a class that has only private fields, a
run()
method without arguments and no getters at all. There is no way to test this without breaking the rules.Comment by Michael Borgwardt: This is not really a test antipattern, it's pragmatism to deal with deficiencies in the code being tested. Of course it's better to fix those deficiencies, but that may not be possible in the case of 3rd party libraries.
Aaron Digulla: I kind of agree. Maybe this entry is really better suited for a "JUnit HOWTO" wiki and not an antipattern. Comments?
无名测试 -- Nick Pellow
添加该测试是为了重现错误跟踪器中的特定错误,其作者认为不保证有自己的名称。 创建一个名为 testForBUG123 的新测试,而不是增强现有的、缺少的测试。
两年后,当该测试失败时,您可能需要首先尝试在错误跟踪器中查找 BUG-123,以找出测试的意图。
The Test With No Name -- Nick Pellow
The test that gets added to reproduce a specific bug in the bug tracker and whose author thinks does not warrant a name of its own. Instead of enhancing an existing, lacking test, a new test is created called testForBUG123.
Two years later, when that test fails, you may need to first try and find BUG-123 in your bug tracker to figure out the test's intent.
The Slow Poke
运行速度极其缓慢的单元测试。 当开发人员开始测试时,他们有时间去洗手间,抽根烟,或者更糟糕的是,在一天结束回家之前开始测试。 (来源:James Carr 的帖子)
又名测试不会像应有的那样频繁运行
The Slow Poke
A unit test that runs incredibly slow. When developers kick it off, they have time to go to the bathroom, grab a smoke, or worse, kick the test off before they go home at the end of the day. (Src: James Carr's post)
a.k.a. the tests that won't get run as frequently as they should
蝴蝶
您必须测试包含始终变化的数据的内容,例如包含当前日期的结构,并且无法将结果固定为固定值。 丑陋的部分是你根本不关心这个值。 它只会使您的测试变得更加复杂,而不会增加任何价值。
蝙蝠的翅膀可以在世界的另一端引发飓风。 -- Edward Lorenz,蝴蝶效应
The Butterfly
You have to test something which contains data that changes all the time, like a structure which contains the current date, and there is no way to nail the result down to a fixed value. The ugly part is that you don't care about this value at all. It just makes your test more complicated without adding any value.
The bat of its wing can cause a hurricane on the other side of the world. -- Edward Lorenz, The Butterfly Effect