您的TDD体验的不利方面是什么?您是否发现婴儿脚步(使测试呈绿色的最简单方法)烦人且无用?您是否发现无价值测试(当测试最初具有意义但最终实现时会检查与其他测试相同的逻辑)维护的重要性吗?等等
上面的问题是我在TDD体验期间感到不舒服的事情。因此,我很感兴趣其他开发人员是否有类似的感受,以及他们如何看待它们。
感谢描述TDD负面方面的文章链接(Google充斥着正面且经常是狂热的文章)。
您的TDD体验的不利方面是什么?您是否发现婴儿脚步(使测试呈绿色的最简单方法)烦人且无用?您是否发现无价值测试(当测试最初具有意义但最终实现时会检查与其他测试相同的逻辑)维护的重要性吗?等等
上面的问题是我在TDD体验期间感到不舒服的事情。因此,我很感兴趣其他开发人员是否有类似的感受,以及他们如何看待它们。
感谢描述TDD负面方面的文章链接(Google充斥着正面且经常是狂热的文章)。
Answers:
就像在“敏捷”旗帜下的一切事物一样,TDD在理论上听起来不错,但实际上并不清楚它有多好(而且像大多数“敏捷”事物一样,您会被告知如果不这样做,喜欢它,您做错了)。
TDD的定义并非一成不变:像肯特·贝克(Kent Beck)这样的人要求必须在单行代码之前编写非编译测试,并且应编写每行代码以通过失败的测试。前端设计极少,一切都被驱动通过测试。就是行不通。我已经看到使用这种方法开发的大型企业应用程序,我希望这是我职业生涯中看到的最糟糕的代码(不会太遥远;尽管有一些有才华的开发人员正在研究它)。从我所看到的结果来看,它导致了大量的考虑不周的测试,这些测试主要验证是否发生了函数调用,当变量为null且模拟框架进行了全面的练习时,抛出了异常(whoop-de-whoop);您的生产代码与这些测试紧密相关,并且没有出现恒定且易于重构的梦想-实际上,由于所有测试都会破坏,人们更不可能修复错误的代码。
反过来说,我听说人们认为TDD意味着在设计阶段(包括建筑设计),首先要在高层进行测试。随着更多信息的出现,这些测试可能会在开发过程中发生变化,但已经对其进行了仔细的考虑,并为代码的实际操作提供了很好的指南。对我来说,这很有意义。
这篇(Clojure作者)Rich Hickey的访谈包含以下内容。我感到100%同情:
生命短暂,一天中只有有限的几个小时。因此,我们必须选择如何度过自己的时间。如果我们花时间编写测试,那我们该花时间做其他事情了。我们每个人都需要评估如何最好地度过时间,以便在数量和质量上最大化我们的结果。如果人们认为花费50%的时间来编写测试可以最大程度地提高测试结果-对他们来说还可以。我确定这对我来说不是真的-我宁愿花时间去思考自己的问题。我敢肯定,对于我来说,这将产生比其他任何时间都更好的解决方案,缺陷更少。具有完整测试套件的不良设计仍然是不良设计。
Donald Knuth在《Workers》采访中的《Coders》中的另一份类似声明,复制自此处:
Seibel:说到实际工作,在从事计算机编程艺术的过程中,您花了十年的时间来编写排版系统TeX。我了解您完全不在计算机上编写了TeX的第一个版本。
克努斯(Knuth):当我最初在1977年和'78年写过TeX时,我当然没有识字编程,但是我确实有结构化编程。我用铅笔写在一个大笔记本上。六个月后,当我浏览完整个项目后,我开始在计算机上打字。在我于77年10月开始编写程序时,于78年3月进行了调试。斯坦福档案库中的代码(全都用铅笔写了),当然,当我了解了它应该是什么之后,我当然会回来修改一个子例程。这是第一代系统,因此可能有许多不同的体系结构,必须丢弃它们,直到我忍受了一段时间之后才知道那里有什么。这是一个鸡与蛋的问题-在拥有字体之前无法排版,但是在可以排版之前无法字体。但是结构化编程给了我不变性的想法,并且知道如何制作我能理解的黑匣子。因此,我有信心在我最终调试它时该代码将起作用。我觉得如果我等六个月再进行任何测试,将会节省很多时间。我有足够的信心,该代码是正确的。
Seibel:节省时间是因为您不会花费时间建造脚手架和存根来测试不完整的代码吗?
克努斯:对。
我对TDD的负面经历是我的第一个经历。TDD对我来说听起来很棒,我已经进行了质量检查多年,但仍然让我感到恐惧。我想压扁每个错误,然后再构建它。不幸的是,使用TDD并不能保证您会编写出良好的测试。实际上,我最初的倾向是编写能够生成简单代码的简单测试。真正的简单代码,几乎没有抽象。与类内部结构交织在一起的真正简单的测试。一旦完成了数千个小小的测试,当您必须更改其中的一百个重构代码以使用非常重要的领域概念X进行重构时,您肯定不会感觉自己的移动速度更快。
灯亮了我-TDD不是测试技能。这是一种设计技巧。它只会使您在实践中获得良好,简单,可行的代码,并不断意识到它所带给您的设计指导。如果您出于代码覆盖率而编写测试,则将创建脆弱的测试。如果您正在编写测试来帮助您设计抽象,那么这只是编写自上而下代码的一种更严格的方法。您首先要从调用者的角度看代码,这鼓励您使他的生活更轻松,而不是将类的内部镜像到外部。
我认为TDD很有用,但我并不教条。如果这些“无价值的测试”使维护变得困难,请删除它们!我将测试与其余代码一样对待。如果可以将其重构并简化工作,那就去做吧!
我还没有亲自看过它,但是我听说有些地方跟踪代码覆盖率和测试计数。因此,如果指标收集是TDD的副作用,那么我也可以认为这是负面的。我会热情地删除1000行代码,如果这会使20个测试过时并降低我的代码覆盖率%,那么很好。
我将在这里走出去,以残酷的诚实宣布这实际上是在仪式上浪费时间。(在大多数情况下。)
我买了一本关于单元测试的书,其中也讨论了TDD,虽然我同意UT的好处,但是在尝试了TDD约100小时之后,出于种种原因我放弃了它。我有点交叉张贴,但TDD:
另一个令人担忧的问题是人们对完美度的争论,人们必须对其进行TDD才能成功做到这一点。有人坚持认为,如果从项目开始时团队中的每个人都没有坚持不懈地进行TDD,那您将蒙受痛苦。其他人则坚称,没有人会按此书进行TDD。如果这两者都是正确的,那么无论他们是否意识到,TDD从业者都在遭受痛苦。
当然,如果有人争辩说,通过以类似TDD的方式进行处理,您将可以轻松地与TDD一起使用设计,那么,有更快的方法可以实现这一目标-即,通过实际研究TDD的概念可组合性。那里有很多资源,甚至还有很多严格的数学理论(主要在函数式编程中,而且在其他领域中)。为什么不花所有的TDD时间来学习?
在文化上,TDD表现出是一种礼仪习惯的症状。它感到内s;它鼓励程序而不是理解;它有大量的教义和口号(“如果您客观地看待它,直到它被制造出来,真是令人震惊”)。维基百科对“仪式”一词的定义实际上是很恰当的:
在心理学中,“ 礼节”一词有时在技术上用于人为系统地用来消除或预防焦虑的重复行为。它是强迫症的症状。
另外,我注意到TDD的另一个问题是:
TDD导致开发团队的关注重点从质量代码转移到了测试用例和代码覆盖率!我个人不喜欢TDD,因为它会降低我的创造力,并使软件开发成为乏味的机械过程!单元测试用例在谨慎使用时很有用,但在处理软件开发目标时成为负担。
我知道一个是经理的人,一旦沉迷于TDD,在技术上就会变得呆板。对他来说,这是一件神奇的事情,他相信他可以用最少的可维护代码为他架构不良的软件带来神奇的解决方案。更不用说那个项目发生了什么-它在他的手中惨败,而他所有的测试用例都是绿色的。我想TDD帮助他获得了一些统计信息,例如“我的案例中有99/100是绿色的”等,这就是他痴迷的原因,因为他永远无法评估质量或提出改进设计的建议。
我主要的负面经验是尝试使用TDD来编辑另一个没有测试或非常非常基本的集成测试的程序员的代码。当我添加功能或解决上述代码问题时;我希望先编写一个测试(TDD方式)。不幸的是,代码紧密耦合,如果不进行大量重构,我将无法测试任何东西。
无论如何,重构都是一个很好的练习,但是需要使代码进入可测试状态。在执行此步骤之后,我没有任何制衡手段来查看我的更改是否破坏了任何内容;缺少运行应用程序并检查每个用例的时间。
另一方面,向TDD项目添加功能/修复错误变得非常简单。从本质上讲,用TDD编写的代码通常与一些小片段分离开来。
无论如何,TDD是一个准则。遵循它,直到发现最大的效率。体面的测试范围和解耦的代码,编写良好的代码。
我的经验是,在设计系统时,有时会过分依赖我的测试。从根本上讲,我对具体实施细节的了解太低,无法退后一步来查看大图。这通常导致不必要的复杂设计。我知道,我应该重构代码,但有时我会觉得,通过更频繁地退后一步,可以节省很多时间。
话虽这么说,如果您有一个像Rails这样的框架,而您的架构决策非常有限,那么这些问题基本上是不存在的。
另一个问题是您盲目地信任测试。事实是-与其他任何代码一样,您的测试也可能存在错误。因此,对测试和对实施一样重要。
作为TDD的忠实拥护者,我有时会看到这些缺点
相似测试的测试代码维护成本仅略有不同(通过代码复制(又名复制粘贴继承)创建)。如果您已经拥有一个,则很容易创建一个类似的对象。但是,如果您不重构测试代码,则通过消除代码重复到辅助方法中,如果业务代码的实现细节发生更改,则可能需要一些时间来修复测试。
如果您承受时间压力,您可能会想要消除损坏的测试(或将其注释掉)而不是进行修复。这样,您会损失测试的投资
作为TDD值得的游戏开发者,我还没有遇到多个场景。而且它所处的实例是一段本质上纯粹是数学的代码,需要一种可靠的方法来同时测试大量的边缘情况-很少需要。
也许有一天,有一天会改变我的想法,但是在XP实践中,无情地重构以及不断发展自己的形式的代码的想法更为重要,这对我来说是最大的生产力。詹姆斯·纽基克(James Newkirk)的论文引述:
简单性-“最简单的方法可能会起作用?”
信息很清楚。考虑到当今的需求,设计并编写软件。不要试图预见未来,而要展现未来。它通常会以非常难以预测的方式做到这一点,从而使预期的成本通常太高了,无法承受。”
在我看来,他提到的勇气和强化反馈循环的概念也是提高生产力的关键。
我对TDD的负面体验(可能有限)只是知道从哪里开始!例如,我将尝试做TDD,而又不知道从哪里开始测试琐碎的事情(我可以新建一个Foo
对象,可以将a传递Quux
给Baz
,等等)。测试不测试任何东西),或者如果我试图在现有项目中实现它,那么我发现我必须重写各种类才能在TDD中使用它们。最终结果是我很快就完全放弃了这个概念。
通常我是整个公司中唯一知道什么单元测试(TDD或其他方式)以及为什么这是一件好事的人,这可能并没有帮助。
Foo
与模拟对象,而不是Quux
和Baz
直接,那么你可以打电话给你要测试,然后检查嘲笑,被称为与你所期望的功能的功能。模拟对象是使单元解耦并使单元可测试的使能技术。这就是单身人士之所以邪恶的原因,因为您常常不能仅仅嘲笑他们。* 8')
对我来说,它们只是敲响我宗教大门的一长串宗教借口之一,试图证明我的做事方式无法挽回地破裂,而拯救的唯一途径就是耶稣,肯特·巴克或单位测试。
IMO,他们最大的谎言是TDD将引导您拯救更好的算法设计。请参阅用TDD编写的著名的Soduku求解器:这里,这里,这里,这里和这里
并比较一下不是使用TDD而是使用老式工程的Peter Norvig sudoku求解器:http : //norvig.com/sudoku.html
TDD有一些好处:
TDD是关于长期投资的。当您达到应用程序的维护模式时,付出的努力就会得到回报,并且如果应用程序没有计划达到该目标,则您可能永远无法收回投资。
我认为TDD的红绿色循环的婴儿步骤类似于飞机的检查清单。起飞前检查飞机上的每件事都是烦人而又乏味的,特别是如果它很简单(TDD婴儿步),但是发现它增加了安全性。除了验证一切正常之外,它实际上还可以重置飞机。换句话说,飞机在每次起飞前都要重新启动。
我对TDD的负面经验是我对许多新颖和炒作的感觉。实际上,我之所以喜欢TDD,是因为它确保了我的代码的有效性,甚至更重要:在添加新代码或进行任何类型的重构后,我可以识别失败的测试。
关于TDD的问题让我感到烦恼的是,有很多有关它的规则或指南。因为它仍然很新,所以我们大多数人都有成为TDD初学者的经验。因此,对于我们中的某些人而言,行之有效的方法可能对其他人而言则行不通。我想说的是,没有真正的“错误或正确”方式来执行TDD:这对我和我的团队(如果有的话)都有效。
因此,只要您编写测试-在生产代码之前或之后都没关系,恕我直言-我不确定测试驱动是否真的意味着您必须遵循现在陈述的所有准则,因为尚未证明它们是日常工作的理想解决方案。如果您发现编写测试的更好方法,则应将其发布在博客中,在此处进行讨论或撰写有关该文章的文章。因此,在大约十年的时间里,我们可能已经分享了足够的经验,可以判断在特定情况下哪种TDD规则可以被认为是好的还是不好的。
我已经编写了不止一次的代码,因为它太笨拙,第二天就将其丢弃。我使用TDD重新启动,解决方案更好。因此,我对TDD的负面经验还不多。但是,话虽如此,我还是花了一些时间思考问题,并在TDD空间之外提出了一个更好的解决方案。
我发现当涉及到紧急系统时,TDD的性能很差。我是一名视频游戏开发人员,最近使用TDD来创建一个系统,该系统使用多个简单行为来为实体创建逼真的动作。
例如,有些行为负责将您从不同类型的危险区域移开,而某些行为则负责将您移至不同类型的有趣区域。将每种行为的输出融合在一起将产生最终动作。
系统的内胆很容易实现,而TDD在这里对于指定每个子系统应该负责的内容很有用。
但是,在指定行为如何相互作用以及更重要的是随着时间的推移如何相互作用时,我遇到了问题。通常没有正确的答案,尽管我的初步测试通过了,但是QA可以继续查找系统无法正常工作的极端情况。为了找到正确的解决方案,我不得不反复考虑几种不同的行为,如果我在检查游戏中的新行为之前每次都更新测试以反映新行为,那么我可能最终会一次又一次地抛出测试。所以我删除了那些测试。
我本应该进行更强的测试,以捕获QA发现的边缘情况,但是当您拥有一个位于许多物理和游戏系统之上的类似系统,并且随着时间的推移处理行为时,它就变得有些困难了。梦specify以求确切地说明正在发生的事情。
我几乎肯定在我的方法中犯了错误,就像我说过的那样,TDD的系统非常出色,甚至支持一些优化的重构。