采用测试驱动设计会给我带来什么损失?
仅列出底片;不要列出以负面形式写的福利。
采用测试驱动设计会给我带来什么损失?
仅列出底片;不要列出以负面形式写的福利。
Answers:
有几个缺点(我并不是说没有好处-特别是在编写项目基础时-最终会节省很多时间):
如果要进行“真实的” TDD(请阅读:首先使用红色,绿色和重构步骤进行测试),那么当您要测试集成点时,还必须开始使用模拟/存根。
当您开始使用模拟时,过一会儿,您将要开始使用依赖注入(DI)和控制反转(IoC)容器。为此,您需要对所有内容使用接口(本身有很多陷阱)。
最终,您必须编写更多的代码,而不是仅仅按照“简单的旧方法”编写代码。您不仅需要编写一个客户类,还需要编写一个接口,一个模拟类,一些IoC配置和一些测试。
请记住,测试代码也应该维护和保养。测试应该与其他所有内容一样易读,并且编写好的代码需要时间。
许多开发人员不太了解如何“正确地”完成所有这些操作。但是因为每个人都告诉他们TDD是开发软件的唯一真实方法,所以他们会尽力而为。
这比人们想象的要难得多。通常,使用TDD完成的项目最终会产生很多真正没人能理解的代码。单元测试经常测试错误的东西,错误的方式。没有人同意好的测试应该是什么样子,甚至没有所谓的专家。
所有这些测试使“更改”(与重构相反)系统行为变得更加困难,简单的更改变得非常困难且耗时。
如果您阅读TDD文献,总是会有一些很好的例子,但是在现实生活中的应用程序中,通常必须有一个用户界面和一个数据库。这是TDD变得非常困难的地方,而且大多数资料来源都没有提供好的答案。如果这样做的话,它总是涉及更多的抽象:模拟对象,接口编程,MVC / MVP模式等,这又需要大量的知识,并且...您必须编写更多的代码。
所以要小心...如果您没有一支热情的团队,但至少没有一位经验丰富的开发人员,他们知道如何编写良好的测试,并且还了解一些有关良好体系结构的知识,那么在走TDD之路之前,您确实必须三思而行。 。
当您达到大量测试的地步时,更改系统可能需要重新编写部分或全部测试,具体取决于哪些测试由于更改而无效。这可能会使相对快速的修改变成非常耗时的修改。
同样,您可能开始更多地基于TDD而非真正好的设计原则来做出设计决策。尽管您可能有一个非常简单,简单的解决方案,无法测试TDD的要求,但您现在拥有了一个更加复杂的系统,实际上更容易出错。
if part of the system is covered by tests and they pass, then everything is fine (including design)
。
我认为对我来说最大的问题是“投入”大量的时间。我仍然非常热爱TDD(如果您有兴趣的话,请参阅我的博客以获取更新我的测试历程)的开始,而我实际上已经花费了数小时来入门。
使您的大脑进入“测试模式”需要很长时间,而编写“可测试的代码”本身就是一项技能。
TBH,我谨对此表示不同意,Jason Cohen关于公开私有方法的评论并非如此。在我的新工作方式中,我没有比以前更多的公开方法了。但是,它确实涉及架构更改,并允许您“热插拔”代码模块,以使其他所有内容更易于测试。您不应该使代码的内部更易于访问。否则,我们将回到一切公开的状态,其中的封装在哪里?
因此,(IMO)简而言之:
PS:如果您想获得肯定的链接,我已经询问并回答了几个问题,请查看我的个人资料。
在我从事测试驱动开发的几年中,我不得不说最大的缺点是:
TDD最好成对完成。例如,当您知道如何编写if / else语句时,很难抵制只编写实现的冲动。但是一对会让您继续执行任务,因为您让他继续执行任务。可悲的是,许多公司/经理并不认为这是对资源的良好利用。当我同时需要完成两项功能时,为什么要花两个人来编写一项功能?
有些人只是没有耐心编写单元测试。有些人为他们的工作感到骄傲。或者,有些就像看到复杂的方法/功能从屏幕末端流了出来。TDD并非适合所有人,但我真的希望如此。对于那些继承代码的可怜的灵魂来说,这将使维护工作变得更加容易。
理想情况下,仅当您做出错误的代码决策时,测试才会中断。也就是说,您认为系统以一种方式工作,但事实却并非如此。通过破坏一个测试或一组(少量)测试,这实际上是个好消息。您确切地知道您的新代码将如何影响系统。但是,如果您的测试写得不好,耦合紧密,或者更糟糕的是生成了(咳嗽 VS测试),那么保持测试可以很快成为合唱团。并且,在足够多的测试开始进行更多工作以达到他们所创造的感知价值之后,当日程安排变得压缩时(例如,时间变得紧迫),这些测试将是首先要删除的东西。
再次,理想情况下,如果您遵循该方法,则默认情况下将对您的代码进行100%的测试。通常,以为,我最终得到的代码覆盖率高达90%。当我拥有一些模板样式架构,并且已经测试了基础,并且尝试偷工减料而不测试模板定制时,通常会发生这种情况。另外,我发现当我遇到以前从未遇到过的新障碍时,我在测试它时会遇到学习障碍。我承认我会以传统的笨拙方式编写一些代码行,但我真的很喜欢100%的代码。(我想我在学校里是个超卓成就者,er skool)。
但是,我要说的是,TDD的好处远远超过了一个简单的想法的缺点,即如果您可以实现涵盖您的应用程序的一组好的测试,但又不那么脆弱,以至于一次更改会破坏所有这些测试,您将能够像在第1天一样在项目的第300天继续添加新功能。并非所有尝试TDD的人都认为这是所有错误代码的魔术子弹,因此他们认为可以工作,期间。
我个人发现使用TDD可以编写更简单的代码,花费更少的时间来讨论某个特定的代码解决方案是否可以工作,并且我不担心更改任何不符合以下条件的代码行:团队。
TDD是一门很难掌握的学科,我从事这门课程已经有几年了,我仍然一直在学习新的测试技术。这是一笔巨大的时间投资,但是从长远来看,与没有自动化的单元测试相比,您的可持续性将大大提高。现在,如果只有我的老板能弄清楚这一点。
在您的第一个TDD项目中,有两个重大损失,时间和人身自由
您浪费时间是因为:
您失去个人自由是因为:
希望这可以帮助
TDD要求您在编写代码通过这些测试之前,计划一下类将如何运行。这既是加号也是减号。
我发现在编写任何代码之前很难在“真空”中编写测试。根据我的经验,每当我不可避免地在编写类时却想到一些在编写初始测试时忘记了的东西时,我都会跳过测试。然后是时候不仅重构我的类,还重构我的测试了。重复三到四次,可能会令人沮丧。
我更喜欢先编写课程草稿,然后编写(并维护)一系列单元测试。草稿后,TDD对我来说很好。例如,如果报告了一个错误,我将编写一个测试以利用该错误,然后修复代码,以便测试通过。
好了,这需要您调试测试。同样,编写测试会花费一定的时间,尽管大多数人都认为这是一项前期投资,可以在节省时间的调试和稳定性方面在应用程序的生命周期内获得回报。
但是,我个人遇到的最大问题是要提高纪律以实际编写测试。在团队中,尤其是已建立的团队中,很难说服他们花费的时间是值得的。
TDD的缺点是,它通常与“敏捷”方法紧密相关,该方法不重视系统文档,而是理解为什么测试“应该”返回一个特定值而不是其他任何值仅存在于开发人员的头。
一旦开发人员离开或忘记了测试返回一个特定值而不返回其他特定值的原因,您就被搞砸了。如果已充分记录了TDD,并使用了易于理解的文档(例如,尖尖的经理),则可以在5年内(当世界变化且您的应用也需要使用它时)引用TDD。
当我谈论文档时,这不是代码的模糊,而是应用程序外部存在的官方书面材料,例如用例和背景信息,经理,律师和需要更新的可怜树液可以参考这些信息您的代码在2011年。
我遇到过几种情况,TDD使我发疯。列举一些:
测试用例的可维护性:
如果您在大型企业中,很多机会是您不必自己编写测试用例,或者至少当您进入公司时,其中大多数是由其他人编写的。应用程序的功能会不时更改,如果没有适当的系统(例如HP Quality Center)来跟踪它们,您将很快发疯。
这也意味着新团队成员需要大量时间来掌握测试用例的进展。反过来,这可以转化为更多的资金需求。
测试自动化的复杂性:
如果将部分或全部测试用例自动化到机器可运行的测试脚本中,则必须确保这些测试脚本与其相应的手动测试用例保持同步,并且与应用程序更改保持一致。
另外,您将花费时间调试有助于捕获错误的代码。我认为,这些错误中的大多数来自测试团队未能在自动化测试脚本中反映应用程序更改。业务逻辑,GUI和其他内部内容的更改会导致脚本停止运行或运行不可靠。有时这些变化非常微妙,难以检测。一旦我的所有脚本都报告失败,因为它们的计算基于表1的信息,而表1现在是表2(因为有人在应用程序代码中交换了表对象的名称)。
最大的问题是不知道如何编写适当的单元测试的人。他们编写了相互依赖的测试(并且与Ant一起运行时效果很好,但是当我从Eclipse运行它们时,突然突然失败了,只是因为它们以不同的顺序运行)。他们编写的测试不会对任何东西进行特别的测试-他们只是调试代码,检查结果,然后将其更改为test,将其称为“ test1”。它们扩大了类和方法的范围,只是因为为它们编写单元测试会更容易。单元测试的代码非常糟糕,存在所有经典的编程问题(重耦合,500行长的方法,硬编码的值,代码重复),难以维护。由于某些奇怪的原因,人们将单元测试视为劣于“真实”代码,而他们却没有 根本不在乎它们的质量。:-(
最大的缺点是,如果您确实想正确执行TDD,则必须先失败很多,然后才能成功。鉴于有多少软件公司在工作(按KLOC美元计),您最终将被解雇。即使您的代码更快,更干净,更易于维护且漏洞更少。
如果您在一家按KLOC付费的公司(或已实施的要求-即使未经测试)则远离TDD(或代码审查,结对编程,或持续集成等)。
在测试所有代码之前,您将失去说自己“完成”的能力。
您将失去在运行之前编写数百或数千行代码的能力。
您失去了通过调试学习的机会。
您将失去不确定的代码发布灵活性。
您失去了紧密耦合模块的自由。
您将失去跳过编写低级设计文档的选项。
您将失去每个人都不敢更改的代码所带来的稳定性。
重新关注困难的,不可预见的需求是程序员的祸根。测试驱动的开发迫使您将精力集中在已知的普通需求上,并将您的开发限制在已经想到的范围内。
考虑一下,您很可能最终会针对特定的测试用例进行设计,因此您将不会变得很有创意,并开始思考“如果用户可以执行X,Y和Z,那将很酷”。因此,当用户开始对潜在的散热要求X,Y和Z感到兴奋时,您的设计可能过于严格地专注于已经指定的测试用例,并且将很难进行调整。
当然,这是一把双刃剑。如果您花费所有时间为用户可能想要的每种可能的,可想象的X,Y和Z进行设计,那么您将不可避免地永远不会完成任何事情。如果您完成了某些工作,那么任何人(包括您自己)都将不可能知道您在代码/设计中正在做什么。
为诸如XML提要和数据库之类的“随机”数据编写测试可能很困难并且很耗时(不是那么困难)。我最近花了一些时间来处理天气数据供稿。至少因为我对TDD没有太多的经验,所以为此编写测试非常令人困惑。
一切都很好。我将添加一些方法来避免TDD的阴暗面:
我已经编写了应用程序来进行自己的随机自检。编写特定测试的问题是,即使您编写了大量测试,它们也只能涵盖您想到的情况。随机测试生成器会发现您没有想到的问题。
大量单元测试的整个概念意味着您拥有的组件可能会进入无效状态,例如复杂的数据结构。如果您远离复杂的数据结构,则要进行的测试要少得多。
在您的应用程序允许的范围内,请避开依赖于通知,事件和副作用的正确排序的设计。这些很容易掉落或混乱,因此需要大量测试。
TDD需要您的代码的特定组织。这可能效率低下或难以阅读。甚至在结构上是错误的;例如,由于private
不能在类外部调用方法,因此必须使方法成为非私有方法以使其可测试,这是错误的。
当代码更改时,您还必须更改测试。通过重构,这可能会花费很多额外的工作。
让我补充一下,如果将BDD原理应用于TDD项目,则可以缓解此处列出的一些主要缺点(混乱,误解等)。如果您不熟悉BDD,则应阅读Dan North的介绍。他提出这个概念是为了回答在工作场所中使用TDD引起的一些问题。Dan的BDD简介可在此处找到。
我之所以提出这一建议,是因为BDD解决了其中的一些负面问题,并起到了一定的作用。您需要在收集反馈时考虑这一点。
开发时间增加:每种方法都需要测试,并且如果您有一个具有依赖项的大型应用程序,则需要准备和清理数据以进行测试。