测试驱动开发的缺点?

时间:2008-09-15 16:15:51

标签: unit-testing testing tdd

采用测试驱动设计我会失去什么?

仅列出否定数据;不列出以否定形式写的福利。

31 个答案:

答案 0 :(得分:184)

如果你想做“真正的”TDD(阅读:首先用红色,绿色,重构步骤测试),那么你还需要开始使用模拟/存根,当你想测试集成点时。

当您开始使用模拟时,过一会儿,您将需要开始使用依赖注入(DI)和控制反转(IoC)容器。要做到这一点,你需要为所有东西使用接口(它们本身有很多陷阱)。

在一天结束时,你必须编写更多代码,而不是只使用“普通老方法”。您还需要编写接口,模拟类,一些IoC配置和一些测试,而不仅仅是客户类。

请记住,测试代码也应该得到维护和保养。测试应该像其他所有内容一样可读,编写好的代码需要时间。

许多开发人员并不十分清楚如何做到这些“正确的方法”。但是因为每个人都告诉他们TDD是开发软件的唯一真正方式,他们只是尽力而为。

这比人们想象的要困难得多。通常使用TDD完成的项目最终会得到许多人们无法理解的代码。单元测试经常以错误的方式测试错误的东西。并且没有人同意一个好的测试应该是什么样子,甚至不是所谓的大师。

所有这些测试都使得“更改”(与重构相反)系统的行为变得更加困难,而简单的更改变得过于庞大和耗时。

如果您阅读TDD文献,总会有一些很好的例子,但在现实生活中,您必须拥有用户界面和数据库。这是TDD变得非常困难的地方,大多数消息来源都没有提供好的答案。如果他们这样做,它总是涉及更多的抽象:模拟对象,编程到接口,MVC / MVP模式等,这需要大量的知识,并且......你必须编写更多的代码。

所以要小心......如果你没有一支热情的团队,至少有一位经验丰富的开发人员知道如何编写优秀的测试,并且知道一些关于良好架构的事情,那么在下降之前你必须要三思而后行TDD之路。

答案 1 :(得分:124)

有几个缺点(我并没有声称没有任何好处 - 特别是在编写项目的基础时 - 它最终会节省大量时间):

  • 投入大量时间。对于简单的情况,您会失去约20%的实际实施,但对于复杂的情况,您会损失更多。
  • 其他复杂性。对于复杂情况,您的测试用例难以计算,我建议在这种情况下尝试使用在调试版本/测试中并行运行的自动参考代码运行,而不是最简单的单元测试。
  • 设计影响。有时设计在开始时并不清晰,随着您的进展而不断发展 - 这将迫使您重做测试,这将导致大量的时间损失。在这种情况下,我建议推迟单元测试,直到你对设计有所了解为止。
  • 持续调整。对于数据结构和黑盒算法,单元测试将是完美的,但对于倾向于更改,调整或微调的算法,这可能会导致大量时间投入声称是不合理的。因此,当您认为它实际适合系统并且不强制设计适合TDD时,请使用它。

答案 2 :(得分:66)

当您进入大量测试时,更改系统可能需要重新编写部分或全部测试,具体取决于哪些测试因更改而失效。这可能会将相对较快的修改变成非常耗时的修改。

此外,您可能更多地基于TDD而不是实际上优秀的设计原则开始做出设计决策。虽然你可能有一个非常简单,容易的解决方案,无法测试TDD需求的方式,但你现在有一个更复杂的系统,实际上更容易出错。

答案 3 :(得分:54)

我认为对我来说最大的问题是“进入它”需要大量的时间。我仍然处于TDD旅程的开始阶段(如果您感兴趣的话,请参阅我的blog了解我的测试冒险更新)并且我已经花了小时开始。

让你的大脑进入“测试模式”需要很长时间,编写“可测试代码”本身就是一项技能。

TBH,我恭敬地不同意Jason Cohen's comments公开私人方法,这不是它的意思。 我在新的工作方式上没有比以前更多的公共方法了。但它确实涉及架构更改,允许您“热插拔”代码模块,使其他所有内容更容易测试。您应使代码的内部更易于访问。否则,我们会回到原点,一切都是公开的,那里的封装在哪里?

所以,(IMO)简而言之:

  • 思考所花费的时间(即实际上正在进行测试)。
  • 了解如何编写可测试代码所需的新知识。
  • 了解使代码可测试所需的架构更改。
  • 提高你的“TDD-Coder”技能,同时努力提高我们光荣的编程工艺所需的所有其他技能:)
  • 组织代码库以包含测试代码,而无需拧紧生产代码。

PS:如果你想要积极的链接,我已经提出并回答了几个问题,请查看我的profile

答案 4 :(得分:49)

在我练习测试驱动开发的几年里,我不得不说最大的缺点是:

将其出售给管理层

  TDD最好成对完成。首先,当您知道如何编写 if / else 语句时,很难抵制编写实现的冲动。但是一对会让你完成任务,因为你让他完成任务。可悲的是,许多公司/经理并不认为这是对资源的良好利用。当我有两个需要同时完成的功能时,为什么要为两个人写一个功能付费呢?

将其出售给其他开发者

  

有些人对编写单元测试没有耐心。有些人为他们的工作感到自豪。或者,有些就像看到复杂的方法/功能从屏幕末端流出。 TDD并不适合每个人,但我真的希望如此。对于那些继承代码的可怜人来说,这将使维护东西变得更加容易。

维护测试代码和生产代码

  

理想情况下,只有在做出错误的代码决策时,您的测试才会中断。也就是说,你认为系统是单向运行的,结果却没有。通过打破测试或一小组测试,这实际上是个好消息。您知道完全新代码将如何影响系统。但是,如果您的测试写得不好,紧密耦合,或者更糟糕的是,生成(咳嗽 VS测试),那么维持测试可以很快成为合唱团。并且,经过足够的测试开始导致他们创建的感知价值的更多工作,那么当计划被压缩时,测试将是首先被删除的东西(例如,它会变得紧张)

编写测试以覆盖所有内容(100%代码覆盖率)

  

理想情况下,如果您遵守该方法,您的代码将默认100%经过测试。通常情况下,我认为最终代码覆盖率高达90%。这通常发生在我有一些模板样式架构,并且测试基础时,我试图偷工减料而不测试模板自定义。此外,我发现当我遇到一个以前没有遇到的新障碍时,我有一个学习曲线来测试它。我承认用旧的skool方式编写一些代码行,但我真的很想拥有100%的代码。 (我想我在学校里是一个成功者,呃skool)。

然而,有了这个,我会说TDD的好处远远超过了简单的想法的负面影响,如果你能够实现一套覆盖你的应用程序但又不是那么脆弱的一组测试,那么一个改变就会破坏它们,您将能够像第1天那样在项目的第300天继续添加新功能。对于所有尝试TDD的人来说,这不会发生,因为这是他们所有错误代码的神奇子弹,所以他们认为这不行,期间。

就我个人而言,我发现使用TDD,我编写更简单的代码,如果特定的代码解决方案能够工作或不工作,我花费更少的时间进行辩论,并且我不必担心更改任何不符合标准的代码行由团队提出。

TDD是一门难以掌握的学科,我已经学习了几年,而且我一直都在学习新的测试技术。这是一项巨大的时间投资,但从长远来看,您的可持续性将远远超过您没有自动化单元测试。现在,如果只有我的老板可以解决这个问题。

答案 5 :(得分:24)

在你的第一个TDD项目中,有两大损失,时间和个人自由

你因为:

而浪费时间
  • 创建一个全面的,重构的,可维护的单元和验收测试套件,为项目的第一次迭代增加了大部分时间。从长远来看,这可能会节省时间,但同样也可以是您不必多余的时间。
  • 您需要选择并成为核心工具集的专家。单元测试工具需要通过某种模拟框架进行补充,并且都需要成为自动构建系统的一部分。您还希望选择并生成适当的指标。

你失去了个人自由,因为:

  • TDD是一种非常规范的编写代码的方式,它往往与技能范围的顶部和底部的代码相比较。始终以某种方式编写生产代码并使您的工作不断进行同行评审可能会使您最糟糕和最优秀的开发人员失望,甚至导致人员流失。
  • 嵌入TDD的大多数敏捷方法都要求您不断地与客户讨论您打算完成的事情(在这个故事/日/其他事情中)以及权衡取舍。再一次,这不是每个人的一杯茶,无论是围栏的开发者还是客户。

希望这有帮助

答案 6 :(得分:14)

TDD要求您在编写代码以通过这些测试之前规划您的类的运行方式。这是加号和减号。

我发现很难在“真空”中编写测试 - 在编写任何代码之前。根据我的经验,每当我在编写我的初级测试时忘记编写课程时不可避免地想到某些东西时,我倾向于绊倒我的测试。那么现在是时候不仅重构我的课程,而且还是我的测试。重复三到四次,这会令人沮丧。

我更喜欢先编写课程草稿,然后编写(并维护)一系列单元测试。我有一个草案后,TDD对我来说很好。例如,如果报告了错误,我将编写一个测试来利用该错误,然后修复代码以便测试通过。

答案 7 :(得分:12)

使用TDD进行原型设计可能非常困难 - 当您不确定要采取什么样的解决方案时,预先编写测试可能很困难(除了非常广泛的测试之外)。这可能是一种痛苦。

老实说,我认为绝大多数项目的“核心发展”都没有任何真正的缺点。通常是那些认为他们的代码足够好以至于他们不需要测试(从来没有)的人,而且那些只是简单的人不会为编写它们而烦恼。

答案 8 :(得分:9)

嗯,这种拉伸,你需要调试你的测试。此外,编写测试还有一定的成本,尽管大多数人都认为这是一项前期投资,可以在节省时间的调试和稳定性方面为应用程序的生命周期带来回报。

但是,我个人对它的最大问题是,正在开始实际编写测试的规则。在一个团队中,特别是一个成熟的团队,很难说服他们花费的时间是值得的。

答案 9 :(得分:7)

TDD的缺点是它通常与“敏捷”方法紧密相关,这种方法对系统的文档非常重要,而不是理解为什么测试'应该'返回一个特定的价值而不是任何其他只存在于开发者的头脑中。

一旦开发人员离开或忘记了测试返回一个特定值而不是其他一些值的原因,你就会被搞砸。 TDD很好,如果它有足够的文档,并且被人类可读(即尖头发的经理)文档所包围,当世界发生变化并且您的应用程序也需要时,可以在5年内引用它。

当我谈到文档时,这不是代码中的模糊,这是应用程序外部存在的官方文字,例如管理人员,律师和可怜的人员可以提及的用例和背景信息。必须在2011年更新您的代码。

答案 10 :(得分:7)

如果您的测试不是很彻底,那么您可能会因为测试通过而陷入“一切正常”的错误感觉。从理论上讲,如果您的测试通过,代码就可以了;但如果我们第一次不需要测试就可以完美地编写代码。这里的道德是确保在调用完整的东西之前自己做一个完整性检查,不要只依赖于测试。

在这方面,如果您的健全检查发现未经测试的内容,请务必返回并为其编写测试。

答案 11 :(得分:6)

我遇到过TDD让我发疯的几种情况。说出一些:

  • 测试用例可维护性:

    如果您是一家大企业,很多机会是您不必自己编写测试用例,或者至少大部分是在您进入公司时由其他人编写的。应用程序的功能会不时更改,如果您没有系统(如HP Quality Center)来跟踪它们,您很快就会变得疯狂。

    这也意味着新团队成员需要花费大量时间来掌握测试用例的情况。反过来,这可以转化为更多的资金。

  • 测试自动化的复杂性:

    如果将部分或全部测试用例自动化为可运行机器的测试脚本,则必须确保这些测试脚本与相应的手动测试用例保持同步,并与应用程序更改保持一致。

    此外,您将花时间调试可帮助您捕获错误的代码。在我看来,大多数这些错误来自测试团队未能反映自动化测试脚本中的应用程序更改。业务逻辑,GUI和其他内部资源的更改可能会使您的脚本停止运行或运行不可靠。有时,这些变化非常微妙,难以察觉。一旦我的所有脚本报告失败,因为它们基于表1中的信息计算,而表1现在是表2(因为有人在应用程序代码中交换了表对象的名称)。

答案 12 :(得分:5)

最大的问题是那些不知道如何编写适当的单元测试的人。他们编写的测试依赖于彼此(并且它们与Ant一起运行很好,但是当我从Eclipse运行它们时突然失败,只是因为它们以不同的顺序运行)。他们编写的测试不会特别测试任何东西 - 他们只是调试代码,检查结果,然后将其更改为test,称之为“test1”。它们扩展了类和方法的范围,只是因为为它们编写单元测试会更容易。单元测试的代码非常糟糕,所有经典的编程问题(重耦合,500行长的方法,硬编码值,代码重复)都是难以维护的。由于一些奇怪的原因,人们将单元测试视为低于“真实”代码的东西,并且他们根本不关心它们的质量。 : - (

答案 13 :(得分:4)

在测试所有代码之前,你失去了说“完成”的能力。

在运行代码之前,您将失去编写数百或数千行代码的能力。

你失去了通过调试学习的机会。

您无法灵活地发布您不确定的代码。

你失去了紧密结合模块的自由。

您将失去跳过编写低级设计文档的选项。

你失去了每个人都害怕改变的代码带来的稳定性。

你失去了“黑客”的称号。

答案 14 :(得分:4)

你在编写测试时花了很多时间。当然,这可能会在项目结束时通过更快地捕获错误来保存。

答案 15 :(得分:3)

最大的缺点是,如果你真的想要正确地做TDD,你必须在成功之前失败很多。鉴于有多少软件公司工作(每个KLOC美元),你最终会被解雇。即使您的代码更快,更清洁,更易于维护,并且错误更少。

如果您在一家由KLOC向您付款的公司(或实施的要求 - 即使未经过测试)远离TDD(或代码审查,或结对编程,或持续集成等等)。 )。

答案 16 :(得分:2)

重新关注困难的,无法预料的要求是程序员的不断祸根。以测试为导向的开发迫使您专注于已知的世俗要求,并将您的开发限制在已经想象的范围内。

考虑一下,你很可能最终设计出特定的测试用例,所以你不会有创意并开始思考“如果用户可以做X,Y和Z那就太酷了”。因此,当该用户开始对潜在的冷却要求X,Y和Z感到兴奋时,您的设计可能过于拘泥于已经指定的测试用例,并且难以调整。

这当然是一把双刃剑。如果您将所有时间都花在设计用户可能想要的每个可想象的,可想象的X,Y和Z上,那么您将不可避免地完成任何事情。如果你确实完成了某些事情,任何人(包括你自己)都不可能知道你在你的代码/设计中做了什么。

答案 17 :(得分:2)

我的第二个答案是关于初始开发时间。如果没有测试的安全性,你也会失去舒适的工作能力。我也被描述为TDD坚果吧,所以你可能会失去一些朋友;)

答案 18 :(得分:1)

您必须以不同的方式编写应用程序:使应用程序可测试的应用程序。你会惊讶地发现这有多么困难。

有些人在他们写得太难之前就找到了思考他们要写的东西的概念。诸如模拟之类的概念对某些人来说也很困难。遗留应用程序中的TDD如果不是为测试设计的话可能会非常困难。围绕非TDD友好框架的TDD也可能是一场斗争。

TDD是一项技能,所以初级开发人员可能最初会挣扎(主要是因为他们没有被教导这种方式工作)。

总的来说,虽然随着人们的熟练程度逐渐消除了缺点,你最终会抽出“臭”的代码并拥有一个更稳定的系统。

答案 19 :(得分:1)

您将失去承担多重责任的大班。 您也可能会丢失具有多重职责的大型方法。 你可能会失去一些重构能力,但你也会失去一些重构的能力。

杰森科恩说: TDD需要某个组织来代码。这可能在架构上是错误的;例如,由于私有方法不能在类外部调用,因此必须使方法非私有才能使它们可测试。

我说这表明错过了抽象 - 如果私有代码确实需要测试,它可能应该在一个单独的类中。

戴夫曼恩

答案 20 :(得分:1)

编写“随机”数据(如XML-feeds和数据库)(这并不难)编写测试可能既费时又费时。我最近花了一些时间处理天气数据源。为此编写测试令人困惑,至少因为我没有太多的TDD经验。

答案 21 :(得分:1)

所有答案都很好。我想补充一些方法来避免TDD的黑暗面:

  • 我写过应用程序来进行自己的随机自检。编写特定测试的问题是,即使你写了很多它们,它们只涵盖你想到的案例。随机测试生成器发现了你没想到的问题。

  • 许多单元测试的整个概念意味着您有可能进入无效状态的组件,如复杂的数据结构。如果你远离复杂的数据结构,那么测试就会少得多。

  • 在您的应用程序允许的范围内,不要依赖于通知,事件和副作用的正确排序。这些很容易被丢弃或乱码,因此需要进行大量测试。

答案 22 :(得分:1)

它的速度越来越慢。从长远来看,这将不会让你感到悲伤,但是你最终会编写更多的代码,所以你可能会把时间花在“测试不编码”上。这是一个有缺陷的论点,但你确实在问!

答案 23 :(得分:1)

进入它需要一些时间,并且有时间在一个项目中开始这样做但是...当我发现自动测试发现非常快的愚蠢错误时,我总是后悔没有采用测试驱动方法。此外,TDD提高了代码质量。

答案 24 :(得分:1)

  • 单元测试需要编写更多代码,因此开发成本更高
  • 维护的代码更多
  • 需要额外学习

答案 25 :(得分:0)

TDD需要某个组织来代码。这可能效率低或难以阅读。甚至在建筑上也是错误的;例如,由于private方法不能在类外调用,因此必须使方法非私有才能使它们可测试,这是错误的。

代码更改时,您还必须更改测试。通过重构,这可以是一个 很多额外的工作。

答案 26 :(得分:0)

教我的团队敏捷开发的人不相信计划,你只写了最小的要求。

他的座右铭是重构,重构,重构。我开始明白,重构意味着“不提前计划”。

答案 27 :(得分:0)

您必须确保您的测试始终是最新的,当您开始忽略红灯时,测试变得毫无意义。

你还必须确保测试是全面的,或者在出现大错误的那一刻,你最终确信让你花时间编写更多代码的闷热管理类型会抱怨。

答案 28 :(得分:0)

让我补充一点,如果您将BDD原则应用于TDD项目,您可以缓解此处列出的一些主要缺点(混淆,误解等)。如果你不熟悉BDD,你应该阅读Dan North的介绍。他提出了这个概念,以回答在工作场所应用TDD引起的一些问题。可以找到Dan的BDD简介here

我只提出这个建议,因为BDD解决了其中一些负面问题,并充当了间隙停止。收集反馈意见时,您需要考虑这一点。

答案 29 :(得分:-1)

开发时间增加:每个方法都需要测试,如果您有一个带有依赖关系的大型应用程序,则需要准备和清理数据以进行测试。

答案 30 :(得分:-5)

你失去了进行增量更改(代码重构)的能力,并且仍然感到温暖和模糊,代码完成了它应该做的事情。使用最少的显式依赖项来构造代码时,您几乎失去了自由和无痛的动机。 IOW,你将能够在没有注意的情况下嵌入大量的依赖项。如果您使用TDD,依赖关系会在编写测试时显示为疼痛/嗅觉。