采用测试驱动设计我会失去什么?
仅列出否定数; 不列出以否定形式写的福利.
如果你想做"真正的"TDD(阅读:首先用红色,绿色,重构步骤测试),那么当你想测试集成点时,你还必须开始使用模拟/存根.
当您开始使用模拟时,一段时间后,您将需要开始使用依赖注入(DI)和控制反转(IoC)容器.要做到这一点,你需要为所有东西使用接口(它们本身有很多陷阱).
在一天结束时,你必须编写更多代码,而不是只用"普通老方法".您还需要编写接口,模拟类,一些IoC配置和一些测试,而不仅仅是客户类.
请记住,测试代码也应该得到维护和保养.测试应该像其他所有内容一样可读,并且编写好的代码需要时间.
许多开发人员并不十分清楚如何做到这些"正确的方法".但是因为每个人都告诉他们TDD是开发软件的唯一真正方式,他们只是尽力而为.
这比人们想象的要困难得多.通常使用TDD完成的项目最终会得到许多人们无法理解的代码.单元测试经常以错误的方式测试错误的东西.并且没有人同意一个好的测试应该是什么样子,甚至不是所谓的大师.
所有这些测试都使得"更改"(与重构相反)系统的行为变得更加困难,而简单的更改变得太难和耗时.
如果您阅读TDD文献,总会有一些非常好的例子,但通常在现实生活中,您必须拥有用户界面和数据库.这是TDD变得非常困难的地方,大多数消息来源都没有提供好的答案.如果他们这样做,它总是涉及更多的抽象:模拟对象,编程到接口,MVC/MVP模式等,这需要大量的知识,并且......你必须编写更多的代码.
所以要小心......如果你没有一支热情的团队,至少有一位经验丰富的开发人员知道如何编写好的测试,并且知道一些关于良好架构的知识,那么在走向TDD之前你必须要三思而后行.
几个缺点(我并没有声称没有任何好处 - 特别是在编写项目的基础时 - 它最终会节省大量时间):
大量投资.对于简单的情况,你会失去大约20%的实际实现,但对于复杂的情况,你会失去更多.
额外的复杂性.对于复杂情况,您的测试用例难以计算,我建议在这种情况下尝试使用在调试版本/测试运行中并行运行的自动参考代码,而不是最简单情况的单元测试.
设计影响.有时候设计在开始时并不清晰,随着你的进展而发展 - 这将迫使你重做你的测试,这将导致大量的时间损失.在这种情况下,我建议推迟单元测试,直到你对设计有所了解为止.
连续调整.对于数据结构和黑盒子算法,单元测试将是完美的,但对于倾向于改变,调整或微调的算法,这可能导致人们可能声称不合理的大量时间投资.因此,当您认为它实际适合系统并且不强制设计适合TDD时使用它.
当您达到大量测试的程度时,更改系统可能需要重新编写部分或全部测试,具体取决于哪些测试因更改而失效.这可能会将相对快速的修改变成非常耗时的修改.
此外,您可能更多地基于TDD而不是实际上优秀的设计原则开始做出设计决策.虽然您可能有一个非常简单易用的解决方案,无法测试TDD需求的方式,但您现在拥有一个更复杂的系统,实际上更容易出错.
我认为对我来说最大的问题是"进入它"需要大量的时间.我仍然处于TDD之旅的开始阶段(如果您感兴趣的话,请参阅我的博客了解我的测试冒险的更新)并且我花了几个小时才开始.
让你的大脑进入"测试模式"需要很长时间,编写"可测试代码"本身就是一项技能.
TBH,我恭敬地不同意Jason Cohen关于公开私人方法的评论,这不是它的意思.在我的新工作方式中,我没有比以前更多的公共方法了.但它确实涉及架构更改,允许您"热插拔"代码模块,使其他所有内容更容易测试.您不应该使代码的内部更容易访问.否则我们会回到原点,一切都是公开的,那里的封装在哪里?
所以,(IMO)简而言之:
思考所需的时间(即实际进行测试).
知道如何编写可测试代码所需的新知识.
了解使代码可测试所需的体系结构更改.
提高你的"TDD-Coder"技能,同时努力提高我们光荣的编程工艺所需的所有其他技能:)
组织代码库以包含测试代码,而无需拧紧生产代码.
PS:如果你想要积极的链接,我已经询问并回答了几个问题,请查看我的个人资料.
在我练习测试驱动开发的几年里,我不得不说最大的缺点是:
TDD最好成对完成.首先,当您知道如何编写if/else语句时,很难抵制编写实现的冲动.但是一对会让你完成任务,因为你让他完成任务.可悲的是,许多公司/经理并不认为这是对资源的良好利用.当我有两个需要同时完成的功能时,为什么要为两个人写一个功能付费呢?
有些人对编写单元测试没有耐心.有些人为他们的工作感到自豪.或者,有些就像看到复杂的方法/功能从屏幕末端流出.TDD并不适合每个人,但我真的希望如此.这将使那些继承代码的可怜灵魂更容易维护东西.
理想情况下,只有在做出错误的代码决策时,您的测试才会中断.也就是说,你认为系统是单向运行的,结果却没有.通过打破测试或一小组测试,这实际上是个好消息.您确切知道新代码将如何影响系统.但是,如果您的测试写得不好,紧密耦合,或者更糟糕的是,生成(咳嗽 VS测试),那么维持测试可以很快成为合唱团.并且,经过足够的测试开始导致他们正在创建的感知价值的更多工作,那么当计划被压缩时,测试将是第一个被删除的东西(例如,它变得紧缩时)
理想情况下,如果您遵循该方法,您的代码将默认100%经过测试.通常情况下,我认为最终代码覆盖率高达90%.这通常发生在我有一些模板样式架构,并且测试基础时,我试图偷工减料而不测试模板自定义.此外,我发现当我遇到一个以前没有遇到的新障碍时,我有一个学习曲线来测试它.我承认用旧的skool方式编写一些代码行,但我真的很想拥有100%的代码.(我想我在学校里是一个成功者,呃skool).
然而,有了这个,我会说TDD的好处远远超过了简单的想法的负面影响,如果你能够实现一套覆盖你的应用程序的一套好的测试,但是不是那么脆弱,以至于一次改变就会破坏它们,你会能够像第1天那样在项目的第300天继续添加新功能.所有尝试TDD的人都认为这是他们所有错误代码的神奇子弹,所以他们认为它可以工作,期间.
我个人发现,使用TDD,我编写了更简单的代码,如果某个特定的代码解决方案能够正常工作,我花费更少的时间进行辩论,并且我不必担心更改任何不符合条件的代码行.团队.
TDD是一门难以掌握的学科,我已经学习了几年,而且我一直都在学习新的测试技术.这是一项巨大的时间投资,但从长远来看,您的可持续性将远远超过您没有自动化单元测试.现在,如果只有我的老板可以解决这个问题.
在您的第一个TDD项目中,有两大损失,时间和个人自由
你失去了时间因为:
创建一个全面的,重构的,可维护的单元和验收测试套件,为项目的第一次迭代增加了大部分时间.从长远来看,这可能会节省时间,但同样可以节省您的时间.
您需要选择并成为核心工具集的专家.单元测试工具需要通过某种模拟框架进行补充,并且都需要成为自动构建系统的一部分.您还希望选择并生成适当的指标.
你失去了个人自由,因为:
TDD是一种非常规范的编写代码的方式,它往往会与技能范围的顶部和底部的代码产生共同作用.始终以某种方式编写生产代码并使您的工作不断进行同行评审可能会让您的最差和最好的开发人员感到不安,甚至会导致员工流失.
嵌入TDD的大多数敏捷方法都要求您不断地与客户讨论您打算完成的事情(在这个故事/日/其他事情中)以及权衡取舍.再一次,这不是每个人的一杯茶,无论是在围栏的开发者方面还是客户.
希望这可以帮助
在编写代码以通过这些测试之前,TDD要求您规划类的操作方式.这是加号和减号.
我发现很难在"真空"中编写测试 - 在编写任何代码之前.根据我的经验,每当我在编写我的初级测试时忘记编写课程时不可避免地想到某些东西时,我倾向于绊倒我的测试.那么现在是时候不仅重构我的课程,而且还是我的测试.重复三到四次,这会令人沮丧.
我更喜欢先编写课程草稿,然后编写(并维护)一系列单元测试.我有一个草案后,TDD对我来说很好.例如,如果报告了错误,我将编写一个测试来利用该错误,然后修复代码以便测试通过.
使用TDD进行原型设计可能非常困难 - 当您不确定要采取哪种解决方案时,预先编写测试可能很困难(除了非常广泛的测试之外).这可能是一种痛苦.
老实说,我不认为绝大多数项目的"核心发展"都有任何真正的缺点; 通常情况下,那些认为自己的代码足够好而且不需要测试的人(从来没有这样),而且那些平凡的人不会费心去编写它们.
好吧,这种拉伸,你需要调试你的测试.此外,编写测试还有一定的成本,尽管大多数人都认为这是一项前期投资,可以在节省时间的调试和稳定性方面为应用程序的生命周期带来回报.
不过,我个人最大的问题就是开始实际编写测试的规则.在一个团队中,特别是一个成熟的团队,很难说服他们花费的时间是值得的.
如果你的测试不是很彻底,你可能会因为测试通过而陷入"一切正常"的错误感觉.从理论上讲,如果您的测试通过,代码就可以了; 但如果我们第一次不需要测试就可以完美地编写代码.这里的道德是确保在调用完整的东西之前自己做一个完整性检查,不要只依赖于测试.
在这方面,如果您的健全性检查发现未经测试的内容,请务必返回并为其编写测试.
TDD的缺点是它通常与"敏捷"方法密切相关,它不重视系统的文档,而是理解为什么测试'应该'返回一个特定值而不是任何其他只存在于开发人员中的方法.头.
一旦开发人员离开或忘记测试返回一个特定值而不是其他一些值的原因,你就会被搞砸.TDD很好,如果它有充分的文档,并且由人类可读(即尖头发的经理)文档包围,可以在世界变化和您的应用程序需要的5年内引用.
当我谈到文档时,这不是代码中的模糊,这是存在于应用程序外部的官方文字,例如管理人员,律师和必须更新的可怜的人员可以提及的用例和背景信息你的代码在2011年.
我遇到过TDD让我发疯的几种情况.举几个:
测试用例可维护性:
如果您是一家大型企业,很多机会是您不必自己编写测试用例,或者至少大多数是在您进入公司时由其他人编写的.应用程序的功能会不时更改,如果您没有适当的系统(如HP Quality Center)来跟踪它们,您很快就会变得疯狂.
这也意味着新团队成员需要花费大量时间来掌握测试用例的情况.反过来,这可以转化为更多的钱.
测试自动化复杂性
如果将部分或全部测试用例自动化为机器可运行的测试脚本,则必须确保这些测试脚本与其相应的手动测试用例同步并符合应用程序更改.
此外,您将花时间调试可帮助您捕获错误的代码.在我看来,大多数这些错误来自测试团队未能反映自动化测试脚本中的应用程序更改.业务逻辑,GUI和其他内部资源的更改可能会使您的脚本停止运行或运行不可靠.有时,这些变化非常微妙,难以察觉.一旦我的所有脚本报告失败,因为它们基于表1中的信息计算,而表1现在是表2(因为有人在应用程序代码中交换了表对象的名称).
最大的问题是那些不知道如何编写适当的单元测试的人.他们编写的测试依赖于彼此(并且它们与Ant一起运行很好,但是当我从Eclipse运行它们时突然失败,只是因为它们以不同的顺序运行).他们编写的测试不会特别测试任何东西 - 他们只是调试代码,检查结果,然后将其更改为test,称之为"test1".它们扩展了类和方法的范围,只是因为为它们编写单元测试会更容易.单元测试的代码很糟糕,所有经典的编程问题(重耦合,500行长的方法,硬编码值,代码重复)并且是一个难以维护的地狱.出于某些奇怪的原因,人们将单元测试视为不如"真实"代码的东西,他们不会 至关重他们的品质.:-(
在测试所有代码之前,你失去了说"完成"的能力.
在运行之前,您将失去编写数百或数千行代码的能力.
你失去了通过调试学习的机会.
您无法灵活地发布您不确定的代码.
你失去了紧密结合模块的自由.
您将失去跳过编写低级设计文档的选项.
你失去了每个人都害怕改变的代码所带来的稳定性.
你失去了"黑客"的称号.