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

只列出负面内容;不要以否定形式列出好处。

有帮助吗?

解决方案

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

  • 大笔投资。 对于简单的情况,您会损失大约 20% 的实际实现,但对于复杂的情况,您会损失更多。
  • 额外的复杂性。 对于复杂的情况,您的测试用例很难计算,我建议在这种情况下尝试使用将在调试版本/测试运行中并行运行的自动参考代码,而不是最简单情况的单元测试。
  • 设计影响。 有时,设计一开始并不明确,并随着您的进展而不断发展 - 这将迫使您重做测试,这将产生巨大的时间损失。在这种情况下,我建议推迟单元测试,直到您对设计有所了解为止。
  • 持续调整。 对于数据结构和黑盒算法来说,单元测试是完美的,但对于容易改变、调整或微调的算法来说,这可能会导致大量的时间投入,人们可能会认为这是不合理的。因此,当您认为它确实适合系统时就使用它,而不是强迫设计适合 TDD。

其他提示

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

当您开始使用模拟时,一段时间后,您将需要开始使用依赖注入 (DI) 和控制反转 (IoC) 容器。为此,您需要对所有内容使用接口(接口本身有很多陷阱)。

归根结底,与仅采用“简单的旧方法”相比,您必须编写更多的代码。您不仅需要编写一个客户类,还需要编写一个接口、一个模拟类、一些 IoC 配置和一些测试。

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

许多开发人员不太明白如何“以正确的方式”完成所有这些工作。但是因为每个人都告诉他们 TDD 是开发软件的唯一真正方法,所以他们只是尽力而为。

这比人们想象的要困难得多。通常,使用 TDD 完成的项目最终会产生大量没人真正理解的代码。单元测试经常以错误的方式测试错误的东西。没有人同意一个好的测试应该是什么样子,即使是所谓的专家也不同意。

所有这些测试使得“改变”(与重构相反)系统的行为变得更加困难,并且简单的改变变得太困难且耗时。

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

所以要小心...如果您没有一支热情的团队和至少一名经验丰富的开发人员,他们知道如何编写良好的测试,并且还了解一些有关良好架构的知识,那么在走 TDD 道路之前,您真的必须三思而后行。

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

此外,您可能会开始更多地基于 TDD 而不是实际良好的设计原则来制定设计决策。虽然您可能有一个非常简单、容易的解决方案,但无法按照 TDD 要求的方式进行测试,但您现在拥有一个更加复杂的系统,实际上更容易出错。

我认为对我来说最大的问题是“投入其中”所花费的巨大时间损失。我的 TDD 之旅还处于起步阶段(请参阅我的 博客 如果您有兴趣,请更新我的测试冒险)并且我确实花了 小时 入门。

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

老实说,我谨不同意 杰森·科恩的评论 关于将私有方法公开,这不是它的目的。 在我的新工作方式中,我并没有比以前更多地公开方法. 。然而,它确实涉及架构更改,并允许您“热插拔”代码模块以使其他所有内容更易于测试。你应该 不是 使代码的内部更易于访问来执行此操作。否则我们又回到了一切都是公开的状态,其中的封装在哪里?

所以,(IMO)简而言之:

  • 思考所花费的时间(即实际上正在摸索 测试).
  • 了解如何编写可测试代码所需的新知识。
  • 了解使代码可测试所需的架构更改。
  • 提高您的“TDD-Coder”技能,同时尝试提高我们辉煌的编程工艺所需的所有其他技能:)
  • 组织您的代码库以包含测试代码,而不会破坏您的生产代码。

附:如果您想要积极的链接,我已经提出并回答了几个相关问题,请查看我的 轮廓.

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

卖给管理层

TDD 最好成对进行。其一,当您 知道 如何写一个 如果别的 陈述。但一对会让你专注于任务,因为你让他专注于任务。可悲的是,许多公司/经理并不认为这是对资源的良好利用。当我有两个功能需要同时完成时,为什么要花钱让两个人编写一个功能呢?

卖给其他开发商

有些人就是没有耐心编写单元测试。有些人对他们的工作感到非常自豪。或者,有些人只是喜欢看到复杂的方法/函数从屏幕末端消失。TDD 并不适合所有人,但我真的希望如此。对于那些继承代码的可怜人来说,这将使维护东西变得更加容易。

与生产代码一起维护测试代码

理想情况下,只有当您做出错误的代码决策时,您的测试才会中断。也就是说,您认为系统以一种方式工作,但事实证明并非如此。通过破坏一个测试或一组(小)测试,这实际上是个好消息。你知道 确切地 您的新代码将如何影响系统。但是,如果您的测试编写得不好、耦合紧密,或者更糟糕的是,生成的(咳嗽 VS Test),那么维护你的测试可以很快成为一个合唱团。并且,在足够的测试开始导致比它们所创造的感知价值更多的工作之后,当时间表变得压缩时(例如,测试)将是第一个被删除的测试。到了关键时刻)

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

理想情况下,如果您遵循该方法,您的代码将默认经过 100% 测试。通常情况下,我的代码覆盖率最终会达到 90% 以上。当我有一些模板样式架构并且测试了基础,并且我尝试走捷径而不测试模板自定义时,通常会发生这种情况。另外,我发现当我遇到以前没有遇到过的新障碍时,我在测试它时有一个学习曲线。我承认我用旧的 skool 方式编写了一些代码,但我真的很喜欢 100% 的方式。(我想我在学校是一个成绩优异的人,er skool)。

然而,我想说的是,TDD 的好处远远超过了负面因素,因为一个简单的想法是,如果您能够实现一组很好的测试来覆盖您的应用程序,但又不会脆弱到一个更改就会破坏所有测试,那么您就会能够像第一天一样在项目的第 300 天继续添加新功能。对于所有尝试 TDD 的人来说,这种情况并不会发生,他们认为 TDD 是解决他们所有充满错误的代码的灵丹妙药,因此他们认为它不起作用,就这样。

就我个人而言,我发现使用 TDD,我可以编写更简单的代码,花更少的时间争论特定的代码解决方案是否有效,并且我不用担心更改任何不符合所规定标准的代码行。团队。

TDD 是一门很难掌握的学科,我已经从事了几年,而且我仍然一直在学习新的测试技术。这是一项巨大的前期投入,但从长远来看,您的可持续性将比没有自动化单元测试的情况要好得多。现在,如果我的老板能解决这个问题就好了。

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

你浪费时间是因为:

  • 创建一套全面的、重构的、可维护的单元和验收测试套件会增加项目第一次迭代的大量时间。从长远来看,这可能会节省时间,但同样也可能是您不需要腾出的时间。
  • 您需要选择一组核心工具并成为其专家。单元测试工具需要由某种模拟框架来补充,并且两者都需要成为自动化构建系统的一部分。您还想选择并生成适当的指标。

您失去个人自由是因为:

  • TDD 是一种非常严格的代码编写方式,往往会与处于技能等级顶部和底部的人员产生冲突。始终以某种方式编写生产代码并使您的工作接受持续的同行评审可能会吓坏最差和最好的开发人员,甚至导致人员流失。
  • 大多数嵌入 TDD 的敏捷方法要求您不断与客户讨论您打算完成的任务(在这个故事/一天/无论什么)以及权衡是什么。再说一遍,这并不是每个人都喜欢的,无论是开发商还是客户。

希望这可以帮助

TDD 要求您在编写代码以通过这些测试之前计划好您的类将如何运行。这既是优点也是缺点。

我发现在编写任何代码之前很难在“真空”中编写测试。根据我的经验,每当我在编写课程时不可避免地想到一些在编写初始测试时忘记的事情时,我往往会在测试中绊倒。然后不仅要重构我的类,还要重构我的测试。重复此三四次,可能会令人沮丧。

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

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

老实说,我不认为对于绝大多数项目的“核心开发”有任何真正的缺点;人们对它的谈论比应有的要多得多,通常是那些相信自己的代码足够好以至于不需要测试(从来都不需要)的人以及那些根本懒得编写它们的人。

好吧,在这个扩展中,您需要调试您的测试。此外,编写测试需要一定的时间成本,尽管大多数人都认为这是一项前期投资,可以在应用程序的整个生命周期内获得回报,既可以节省调试时间,又可以提高稳定性。

不过,我个人遇到的最大问题是遵守实际编写测试的纪律。在一个团队中,尤其是一个成熟的团队中,很难让他们相信所花费的时间是值得的。

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

在这一点上,如果您的健全性检查发现某些未测试的内容,请务必返回并为其编写测试。

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

一旦开发人员离开或忘记测试返回一个特定值而不是其他值的原因,你就完蛋了。如果 TDD 有充分的文档记录并且周围有人类可读的内容(即尖头发的经理)文档可以在 5 年后当世界发生变化并且您的应用程序也需要时参考。

当我谈到文档时,这不是代码中的简介,这是应用程序外部存在的官方文字,例如经理、律师和必须更新的可怜的傻瓜可以参考的用例和背景信息你2011年的代码。

我遇到过几次 TDD 让我抓狂的情况。举一些例子:

  • 测试用例可维护性:

    如果你在一家大企业,很多时候你不需要自己写测试用例,或者至少大部分是你进入公司时由别人写的。应用程序的功能会不时发生变化,如果您没有适当的系统(例如 HP 质量中心)来跟踪它们,您很快就会发疯。

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

  • 测试自动化复杂性:

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

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

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

您会浪费大量时间来编写测试。当然,这可能会在项目结束时通过更快地捕获错误来保存。

在测试所有代码之前,您将无法说自己已“完成”。

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

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

您将失去发布不确定代码的灵活性。

您失去了紧密耦合模块的自由。

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

你会失去每个人都害怕更改的代码所带来的稳定性。

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

最大的缺点是,如果你真的想正确地进行 TDD,那么在成功之前你必须经历很多次失败。考虑到有多少家软件公司在运作(每 KLOC 美元),你最终会被解雇。即使您的代码更快、更干净、更易于维护并且错误更少。

如果你在一家按 KLOC(或已实施的要求,即使未经测试)支付工资的公司工作,请远离 TDD(或代码审查、结对编程或持续集成等)。ETC。ETC。)。

我附议关于初始开发时间的答案。如果没有安全的测试,您也将失去舒适工作的能力。我也被描述为 TDD 疯子,所以你可能会失去一些朋友;)

重新关注困难的、不可预见的需求是程序员持续的祸根。测试驱动的开发迫使您关注已知的、平凡的需求,并将您的开发限制在已经想象的范围内。

想想看,您很可能最终会针对特定的测试用例进行设计,因此您不会发挥创造力并开始思考“如果用户可以执行 X、Y 和 Z,那就太酷了”。因此,当该用户开始对潜在的酷需求 X、Y 和 Z 感到兴奋时,您的设计可能过于严格地关注已经指定的测试用例,并且很难调整。

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

它被认为较慢。从长远来看,这并不正确,因为它会在未来拯救你的痛苦,但你最终会编写更多的代码,所以可以说你把时间花在“测试而不是编码”上。这是一个有缺陷的论点,但你确实问了!

为 XML 源和数据库等“随机”数据编写测试可能既困难又耗时(没那么难)。我最近花了一些时间处理天气数据源。为此编写测试非常令人困惑,至少因为我在 TDD 方面没有太多经验。

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

杰森·科恩说过类似的话:TDD 需要对您的代码进行一定的组织。这在架构上可能是错误的;例如,由于私有方法不能在类外部调用,因此必须将方法设为非私有以使它们可测试。

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

戴夫·曼

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

有些人发现在写之前思考自己要写什么的概念太难了。诸如模拟之类的概念对某些人来说也可能很困难。如果遗留应用程序不是为测试而设计的,那么 TDD 可能会非常困难。围绕对 TDD 不友好的框架进行 TDD 也可能是一场斗争。

TDD 是一项技能,因此初级开发人员一开始可能会遇到困难(主要是因为他们没有被教导如何以这种方式工作)。

总的来说,随着人们变得熟练,缺点就会得到解决,并且您最终会抽象出“臭”代码并拥有更稳定的系统。

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

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

都很好回答。我想添加一些方法来避免 TDD 的阴暗面:

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

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

  • 在您的应用程序允许的范围内,避免依赖于通知、事件和副作用的正确顺序的设计。这些很容易掉落或混乱,因此需要进行大量测试。

TDD 需要对您的代码进行一定的组织。这可能效率低下或难以阅读。或者甚至在架构上是错误的;例如,因为 private 方法不能在类外部调用,您必须将方法设为非私有才能使其可测试,这是错误的。

当代码更改时,您也必须更改测试。通过重构,这可能是很多额外的工作。

让我补充一点,如果您将 BDD 原则应用于 TDD 项目,您可以减轻此处列出的一些主要缺点(混乱、误解等)。如果您不熟悉 BDD,您应该阅读 Dan North 的介绍。他提出这个概念是为了回答在工作场所应用 TDD 时出现的一些问题。可以找到 Dan 对 BDD 的介绍 这里.

我提出这个建议只是因为 BDD 解决了其中一些负面因素并起到了止损的作用。在收集反馈时您需要考虑这一点。

你必须确保你的测试始终是最新的,当你开始忽略红灯的那一刻,测试就变得毫无意义了。

您还必须确保测试是全面的,否则一旦出现大错误,您最终说服让您花时间编写更多代码的闷闷不乐的管理类型就会抱怨。

教我团队敏捷开发的人不相信计划,你只为最微小的需求写那么多。

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top