TDD并不是看上去的那么美

TDD并不是看上去的那么美

春节前的一篇那些炒作过度的技术和概念中对敏捷和中国ThoughtWorks的微辞引发了很多争议,也惊动了中国ThoughtWorks公司给我发来了邮件想来找我当面聊聊。对于Agile的Fans们,意料之中地也对我进行了很多质疑和批评。我也回复了许多评论。不过,我的那些回复都是关于中国ThoughtWorks咨询师以及其咨询的方法的。我对Agile方法论中的具体内容评价的不是很多,所以,我想不妨讨论一下Agile方法论中的具体的实践(以前本站也讨论过结对编程的利与弊)。

那么,这次就说说TDD吧,这是ThoughtWorks中国和Agile的Fans们最喜欢的东西了。我在原来的那篇文章中,我把TDD从过度炒作的技术剔除了出去,因为我还是觉得TDD有些道理的,不过,回顾我的经验,我也并不是很喜欢TDD。我这篇文章是想告诉大家,TDD并没有看上去的那么美,而且非常难以掌控,并且,这个方法是有悖论之处的

TDD简介

TDD全称Test Driven Development,是一种软件开发的流程,其由敏捷的“极限编程”引入。其开发过程是从功能需求的test case开始,先添加一个test case,然后运行所有的test case看看有没有问题,再实现test case所要测试的功能,然后再运行test case,查看是否有case失败,然后重构代码,再重复以上步骤。其理念主要是确保两件事:

  • 确保所有的需求都能被照顾到。
  • 在代码不断增加和重构的过程中,可以检查所有的功能是否正确。

我不否认TDD的一些有用的地方,如果我们以Test Case 开始,那么,我们就可以立刻知道我们的代码运行的情况是什么样的,这样可以让我们更早地得到我们实现思路的反馈,于是我们更会有信心去重构,去重新设计,从而可以让我们的代码更为正确。

不过,我想提醒的是,TDD和Unit Test是两码子事儿。有很多人可能混淆了自动化的Unit Test(如:XUnit系例)和TDD的软件开发过程。另外,可能还会有人向鼓吹“TDD让你进行自顶向下的设计方式”,对此,请参阅本站的《Richard Feynman, 挑战者号, 软件工程》——NASA的挑战者号告诉你自顶向下设计的危险性。

TDD的困难之处

下面是几个我认为TDD不容易掌控的地方,甚至就有些不可能(如果有某某TDD的Fans或是ThoughtWorks的咨询师和你鼓吹TDD,你可以问问他们下面这些问题)

  • 测试范围的确定。TDD开发流程,一般是先写Test Case。Test Case有很多种,有Functional的,有Unit的,有Integration的……,最难的是Test Case要写成什么样的程度呢。

    • 如果写的太过High Level,那么,当你的Test Case 失败的时候,你不知道哪里出问题了,你得要花很多精力去debug代码。而我们希望的是其能够告诉我是哪个模块出的问题。只有High Level的Test Case,岂不就是Waterfall中的Test环节?
    • 如果写的太过Low Level,那么,带来的问题是,你需要花两倍的时间来维护你的代码,一份给test case,一份给实现的功能代码。
    • 另外,如果写得太Low Level,根据Agile的迭代开发来说,你的需求是易变的,很多时候,我们的需求都是开发人员自己做的Assumption。所以,你把Test Case 写得越细,将来,一旦需求或Assumption发生变化,你的维护成本也是成级数增加的。
    • 当然,如果我把一个功能或模块实现好了,我当然知道Test 的Scope在哪里,我也知道我的Test Case需要写成什么样的程度。但是,TDD的悖论就在于,你在实现之前先把Test Case就写出来,所以,你怎么能保证你一开始的Test Case是适合于你后面的代码的?不要忘了,程序员也是在开发的过程中逐渐了解需求和系统的。如果边实现边调整Test Case,为什么不在实现完后再写Test Case呢?如果是这样的话,那就不是TDD了。
  • 关注测试而不是设计。这可能是TDD的一个弊端,就像《十条不错的编程观点》中所说的一样——“Unit Test won’t help you write the good code”,在实际的操作过程中,我看到很多程序员为了赶工或是应付工作,导致其写的代码是为了满足测试的,而忽略了代码质量和实际需求。有时候,当我们重构代码或是fix bug的时候,甚至导致程序员认为只要所有的Test Case都通过了,代码就是正确的。当然,TDD的粉丝们一定会有下面的辩解:

    • 可以通过结对编程来保证代码质量。
    • 代码一开始就是需要满足功能正确,后面才是重构和调优,而TDD正好让你的重构和优化不会以牺牲功能为代价。

说的没错,但仅在理论上。操作起来可能会并不会得到期望的结果。1)“结对编程”其并不能保证结对的两个人都不会以满足测试为目的,因为重构或是优化的过程中,一旦程序员看到N多的test cases 都failed了,人是会紧张的,你会不自然地去fix你的代码以让所有的test case都通过。2)另外,我不知道大家怎么编程,我一般的做法是从大局思考一下各种可行的实现方案,对于一些难点需要实际地去编程试试,最后权衡比较,挑选一个最好的方案去实现。而往往着急着去实现某一功能,通常在会导致的是返工,而后面的重构基本上因为前期考虑不足和成为了重写。所以,在实际操作过程中,你会发现,很多时候的重构通常意味着重写,因为那些”非功能性”的需求,你不得不re-design。而re-design往往意味着,你要重写很多Low-Level的Test Cases,搞得你只敢写High Level的Test Case。

  • TDD导致大量的Mock和Stub。相信我,Test Case并不一定是那么容易的。比如,和其它团队或是系统的接口的对接,或是对实现还不是很清楚的模块,等等。于是你需要在你的代码中做很多的Mock和Stub,甚至fake一些函数来做模拟,很明显,你需要作大量的 assumption。于是,你发现管理和维护这些Mock和Stub也成了一种负担,最要命的是,那不是真正的集成测试,你的Test Case中的Mock很可能是错的,你需要重写他们。

也许,你会说,就算是不用TDD,在正常的开发过程中,我们的确需要使用Mock和Stub。没错!的确是这样的,不过,记住,我们是在实现代码后来决定什么地方放一个Mock或Stub,而不是在代码实现前干这个事的。

  • Test Case并没有想像中的那么简单。和Waterfall一样,Waterfall的每一个环节都依赖于前面那个环节的正确性,如果我们没有正确的理解需求,那么对于TDD,Test Case和我们的Code都会的错的。所以,TDD中,Test Case是开发中最重要的环节,Test Case的质量的问题会直接导致软件开发的正确和效率。而TW的咨询师和Agile的Fans们似乎天生就认为,TDD比Waterfall更能准确地了解需求。如果真是这样,用TDD进行需求分析,后面直接Waterfall就OK了

另外,某些Test Case并不一定那么好写,你可能80%的编程时间需要花在某个Test Case的设计和实现上(比如:测试并发),然后,需求一变,你又得重写Test Case。有时候,你会发现写Test Case其实和做实际设计没有差别,你同样要考虑你Test Case的正确性,扩展性,易读性,易维护性,甚至重用性。如果说我们开发的Test Case是用来保证我们代码实现的正确性,那么,谁又来保证我们的Test Case的正确性呢?编写Test Case也需要结对或是Code review吗?软件开发有点像长跑,如果把能量花在了前半程,后半程在发力就能难了。

也许,TDD真是过度炒作的,不过,我还真是见过使用TDD开发的不错的项目,只不过那个项目比较简单了。更多的情况下,我看到的是教条式的生硬的TDD,所以,不奇怪地听到了程序员们的抱怨——“自从用了TDD,工作量更大了”。当然,这也不能怪他们,TDD本来就是很难把控的方法。这里送给软件开发管理者们一句话——“当你的软件开发出现问题的时候,就像bug-fix一样,首要的事是找到root cause,然后再case by case的解决,千万不要因为有问题就要马上换一种新的开发方法”。相信我,大多数的问题是人和管理者的问题,不是方法的问题。

全文完,转载请注明作者和出处,请勿用于商业用途

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

好烂啊有点差凑合看看还不错很精彩 (34 人打了分,平均分: 4.15 )
Loading...

TDD并不是看上去的那么美》的相关评论

  1. 一直在想一个问题。我们对所有的方法进行了单元测试,这样就会出现了一大堆的mock和stub,有没有一种机制,能够替换了这个mock而直接用真实的数据。这样就可以直接用单元测试的代码来做集成测试了?

  2. @陈皓 TDD不是为了提高外部质量,它有助于提高内部质量。TDD是提高coder设计能力的有效途径。其实很像我们以前说自底向上设计和自顶向下设计,TDD可以帮助你做自顶向下设计,这其实是很多coder缺失这个能力。就好比说写代码前先高明白需求,或者验收条件,TDD可以让你接近这样的工作流。
    还有,TDD的测试不限定于一个测试层(UT IT FT AT),它就是整理api设计的过程,测试可以后面补充,QA也会加入这个流程。
    最后,mock和stub的问题很多时候是测试强迫症造成的。TDD过程中一样要避免过度的mock和stub,这些东西是用来隔离外部系统依赖的,而不是用来掩盖未实现的内容。TDD最难的其实是一开始的测试,它是系统高层次api设计,这个地方的设计对后面奠定了基础,好的设计就可以减少对后面mock的依赖,而直接产生后面逻辑的api骨架,真实领域模型的命名加空方法就好了,不需要stub的。我觉得如果写个java,mvc部分controller没什么东西的情况下tdd完全不需要测试前面的部分,直接渗透到肥厚的领域模型的设计部分就好了。这样就进入了BDD的过程,一样是强调从外向里逐渐揭开领域模型协作的方式。
    一边设计,一边还积累了一些关键协作部分的测试,何乐而不为呢?

    1. 对于下面这几个观点,个人以为仅在忽悠层面上成立,实践起来可能恰好相反。(对此,我的文章中已说得够多了)

      “TDD不是为了提高外部质量,它有助于提高内部质量”
      “TDD是提高coder设计能力的有效途径”
      “TDD…是系统高层次api设计,这个地方的设计对后面奠定了基础”

      自顶向下的设计请参阅历史的教训“挑战者号”:http://coolshell.cn/articles/1654.html

  3. >>相信我,大多数的问题是人和管理者的问题,不是方法的问题。
    非常认同。撇开非程序员,就程序员而言,我想,软件工程到今天,一个优秀的程序员和一个糟糕的程序员对工程的影响依然差别巨大。程序员是软件工程里最重要的元素,也是讨论最少的部分。而且,很多时候,公司的文化(不是口头说的那种,是在实际中形成的)、规模和类型对流程的影响是很大的。
    >>Tin 它就是整理api设计的过程,测试可以后面补充,QA也会加入这个流程
    不错,但我觉得这不是TDD的目的或主要的优点。就我的认识而言,我觉得核心不是设计也不是测试,而是需求。我觉得不管是敏捷还是TDD,吸引我的地方都在,这些方法提供了一些参考,如何让需求明明白白贯穿整个工程流程。就设计而言,TDD最多算是一种思路,属于TDD的副产品,就测试而言,我也觉得必要的只是UT。

  4. 我没有系统地看过TDD,不过我自己的实践是这样的:
    1.先根据假设去考虑一个小模块
    2.根据假设设计其与外部交互的协议以及接口(不会开始实际的编码)
    3.根据假设设计单元测试(会立刻开始实际的编码)
    4.反复修改第二步产生的模块设计以及第三步产生的单元测试,以单元测试编写过程来修正模块设计,在这个过程中把模块原设计中完全可以独立出去的功能剔除出去,把原设计中缺失的部分补充进来,最终目标是整理出难以进一步削减的最小需求,通过模块的接口和协议设计来支持最小需求,以单元测试代码来模拟实现最小需求。
    5.放心地轻松地随意地实现模块的设计,只要实现设计,那么模块的编码本身很难犯什么大错。

    所以我的感觉是,单元测试不是用来保障代码质量或模块实现质量的,因为测试的是模块的外部表现和行为,如果要测试代码质量,那么还是要靠传统的lint工具和人肉检查。单元测试测试的是模块的设计质量以及其外部表现与行为是否符合设计。所以我把单元测试的编写过程看作需求分析和模块设计的过程,这一过程做完做好之后,单元测试的主要意义就完成了——单元测试最有价值的在于它的编写过程,而之后产生的自动化测试功能其实是相对次要的。我觉得如果是一个无法自动化测试的模块,只要用其他方式(例如在纸上画示意图)把步骤1到4做好,那也算测试驱动了。只不过我的概念里,测试驱动的是设计而不是实现。当然整个过程中都少不了程序员的思考和对需求的收集猜测分析,但我认为这是这个方法的优点:它帮助程序员更轻松地做人脑更适合的工作,而不是尝试用机器抢程序员的饭碗 ;)
    如果有人对我的回复有兴趣欢迎发邮件给我(sjinny1985囧gmail点com),这一段算是我对这一阶段试验的总结。

  5. 导致其写的代码是为了满足测试的,而忽略了代码质量和实际需求
    ==========
    我还真从未见过能完整通过单元测试集成测试的代码会是低质量的代码

    相反,现实中充斥了把所有测试交给客户,所谓测试局限于鼠标点点点或者干脆是页面能正常200
    没有人确切知道某个类在干什么,甚至都没有人确切知道程序正确表现应该是什么的垃圾代码

  6. 太多的mock和太多的sub本身就说明代码需要重构

    需要重构到适合单元测试

  7. 骂敏捷也许是今年的新浪潮?

    不过最好先分清楚“咨询师”口中的敏捷和敏捷宣言的敏捷

    最好也区分一下敏捷宣言和敏捷开发实践/最佳实践

  8. 还有,没有银弹,如果一种方法论宣布它能优雅高效平滑解决所有问题
    那么可以毫不犹豫的把它踢飞

    但是如果因为一个方法不能优雅高效平滑解决所有问题就指责其是骗子
    也不过是哗众取宠而已

  9. >>相信我,大多数的问题是人和管理者的问题,不是方法的问题。
    这句话是经典,,,

  10. TDD確實不如宣傳的那麼容易理解和實踐。
    個人不是TDD粉,但是裡面那些問題個人認為不難解決。
    首先如果是習慣性無視設計階段和測試階段的話,那麼建議表用TDD,用了只有束縛自己,包括那些沒時間寫測試代碼的情況。
    測試代碼的測試範圍(層次?)。如果有前期設計,那麼按照前期設計編寫代碼,這麼說好像和TD相悖,但是你寫測試代碼也有參照,參照設計而來的。如果你的需求或者設計變了,自然需要先修改測試代碼,而不是先修改實現代碼。這樣做的好處在於,你一不會有一種“懸浮感”,二重構代碼比較“安全”。
    關注測試而不是設計,這條的主旨和上面一條是一樣的,TDD如其名是以測試為關注點,而不是設計。個人認為就像“單元測試不會幫你改進代碼“一樣,在一線程序員的角度,測試就是設計的體現,測試用來驗證代碼的正確性就足夠了,其他的額外功能,比如消除代碼副作用,重構代碼只是“噱頭”而已。
    Mock和Stub問題,這個是實踐問題,一些Mock框架可以動態生成那些Mock對象,如果你經常修改Mock對象,就說明你的代碼有問題,一般來說,Mock良好設計的Interface是很不錯的選擇。
    TDD最大的好處在於:對於程序員來說,修改代碼之後,rerun tests,可以保證你的代碼的正確性。特別是在你準備發布的時候(一些工具比如Maven默認會在發布前進行測試)。

  11. 我觉得关键还是跟Bottom-up的方式有矛盾,很多开发活动并不仅仅只是“实现”,实际上还包含了设计的过程、了解需求甚至发现需求的过程,就好像敏捷方法本身是不信任不依赖需求文档的,教条式的要求“先写”或是“必写”测试用例,对效率和质量都未必有帮助(甚至起反作用,比如文中所说的只管通过测试不管最终体验)

    P.S. 我更信仰“自动化的云测试”XD, 类似Given enough eyeballs, all bugs are shallow

  12. @Tin

    Tin,不好意思,误删了你的一条评论。因为你的那条评论被识别为Spam,而本站每天的Spam有4000条左右,有非常少量的是误报,需要人工检查,所以,我检查起得很费劲。当我看到你的评论时,我点获准,然后就清空垃圾评论,也许是获准的Ajax还没有执行完成,我就清空了。所以,连着你的评论一起被清空了。Sorry!

    关于你的回复,大致意思是关于“自顶向下”那一段的,你觉得我很极端,而且拿着一些缺点在否认整个东西,当然也骂了我什么也不懂,在这里哗众取宠。大概就是这些意思吧。

    我虽然不赞同,但是我捍卫你表达你观点的权利。我只想说,

    > 如果你觉得我是一棍子打死Agile,打死TDD,那只能证明1)你的理解能力很极端,或是2)你过于敏感了,没有认真读我的文章。

    > 对于你说我什么也不懂,嗯,我的确是不懂。TW的一个20出头的咨询师和随便的一个Agile的Fans都比我懂。

    > 不能接受批评的东西一定不是什么好东西。另外,我质疑的是敏捷和TDD,不是你个人。请你不要像教徒一样把我当成异教徒

  13. 首先我想说的是不管什么技术或者流程,有好处也会有缺点,Agile,TDD都是如此。就如您文中所说,TDD存在诸多困难,是的,使用TDD对开发人员的要求高了。但我不觉得使用困难,要求高了就可以用来反驳TDD。
    仔细阅读本文后,我发现作者一直是在举TDD难,或者没有成功应用TDD作为反驳TDD的论据,那么给我的印象就是:我做不好TDD,所以我认为TDD很差劲。显然,这不是理由。
    期望作者能写出一篇真正反驳TDD的文章,或者可以用示例来说明更好,不然真的如Tin所说,有点哗众取宠的味道。

    PS:我不知道你和TW结了什么梁子,呵呵。一直穷追猛打。作为国内践行敏捷的先行者,我觉得TW对国内敏捷的推动还是起到一定的作用的。但是作为一个以咨询为生的商业公司来说,有的时候是要考虑一下自己的盈利的,所以推广的手法是有点过,但哪个公司又不是呢。

    20出头的咨询师又怎样,我也是20几,20几就不能评论,发表自己的看法了么。忘记了曾经的那句话具体是什么了:有的人做了七年,年年是重复。那么年龄大又能说明什么,只是增加了几道皱纹而已。

  14. 结对编程是在所谓XP中被频频提到的好的实践,但实际情况是,大大小小的公司经历的不少,还没有看到任何公司有所谓结对编程,虚无的实践为何会成为好的实践?结对编程首先会导致人力成本的增加,就算老板大方,人力 不是问题,又怎能保证结对的人就一定能默契工作,磨合出一对双打球员都需要机缘和长时间的训练,何况是变化频繁的计算机软件行业。

  15. @syw
    这正是我一直都在说的,软件开发中人和管理的因素相当的重要,所以,不考虑实际情况,不关注人和团队的风格而只谈方法或流程的东西只能在炒作或是理论层面存活。

    @This is
    我非常同意你说的——“是不管什么技术或者流程,有好处也会有缺点”,这正是我的初衷,只有知道了Agile的光明面和阴暗面,才能扬长避短。有人说过——“没有批评,赞美也没有意义”。当某公司仅鼓吹Agile的光明面时候,我向大家揭开一些Agile阴暗面时。另,TDD不是做得好做不好的问题,TDD本身就有悖论存在。如果你想讨论,请对事讨论,而不要阴暗地认为我有什么企图。谢谢。

  16. 1)、我在很多团队中推广敏捷,发现很多团队都是用复制&粘贴,或者hack的方式在编程。这样导致的项目,最后就是到处都是耦合、重复和混乱。这样的系统之上,再TDD,基本上很困难。因为这样的系统,架构本身就是高耦合,很难去独立的TDD
    2)、TDD的测试有很多类别。我们常见的是:单元测试、功能测试、验收测试。我在以前的项目和产品中都做过。我见过很多的团队的TDD只是狭义的单元测试
    3)、TDD ≠ 好的设计。它是一种设计方法,目前来说有效提高代码内部质量最有效的方法之一。

  17. 我倒是想把作者的最后一段话回赠给作者:“当你使用TDD出现问题的时候,就像bug-fix一样,首要的事是找到root cause,然后再case by case的解决,千万不要因为有问题就要马上换一种新的开发方法”

  18. 我本来以为可以在回复中看到一些关于我所说的那几个难点的讨论。而实际上,我只看到了很多“结论性”的回复。

    @小刀
    对于你对我文章的引用,我想对你说的是——我不会以TDD为中心来开发软件,我会以用户,项目,软件过程中的人为中心来改善过程。而你和大多数Agile的倡导者都犯了同一个错误——以Agile或TDD为中心来改变项目以及项目中的人。这正是我们的差别。

    @Eric
    你说的——“TDD并不适用于菜鸟程序员”。 你完全错了!!过程和方法,正是用来帮助那些对软件开发并不是管得好的人的。另外,请你也不要这样说,这些会给人一种对“种族歧视”的感觉。Agile和TDD不是用来歧视水平一般的程序员的

  19. 我只看到满篇的经典废话和两拨没有交流诚意的人在这里自说自话。难道骂两句有利于解决问题?或是自我提升?那好,我也来提升下。

    @小刀
    人家没说换一种开发方法,过度的轻视别人难怪招来骂,Thoughtworks有自己需要反思的地方。还没交流呢先给人安上个标签。不要在确定问题之前下结论不一直是Thoughtworks在咨询时强调的吗?一味追求自己牛B之余也要对Thoughtworks之外的人(比如博主)的智商给点起码的尊重。

    @陈皓
    “我不会以TDD为中心来开发软件,我会以用户,项目,软件过程中的人为中心来改善过程。”
    你到底是想说啥?开发软件还是改善过程?
    我猜你还是想强调改进以人为本,你调查过没有就说跟人家的差别,能力提升是Thoughworks那些你看不起的20多岁的咨询师一直在做的。
    满嘴以人为本我也没见你怎么提人的问题,讲了一整篇的TDD,你就是这么以人为夲的?
    “我本来以为可以在回复中看到一些关于我所说的那几个难点的讨论。而实际上,我只看到了很多“结论性”的回复。”
    讨论为啥一定要在你这里呢?你有讨论的诚意么?你骂完了别人还指望别人给你点干货?真不知道Thoughtworks到底哪惹着博主了,客观的讲,国内有多少本经典是由Thoughtworks中国的员工翻译的?Thoughtworks为中国的程序员做的事情还不够多么?如果真的想讨论,那就摆出讨论的姿态来。

  20. @飘过说两句
    这样的文章就算是骂啦?!我说我对TW咨询师,Agile,TDD的观点的同时也给了一些建议。没说你耶~~,大可不必那么敏感,也不必那么沉不住气。 只是提出不同的见解嘛, 何必那么激动嘛。

    敏捷不是敏感,敏捷的信仰者们不必太敏感了。莫不成真被我说到痛处啦?

  21. @小刀

    你送给我的话,我理解成——用TDD用不好,原因不在TDD,原因在于使用的人。

    而我送给大家的话的意思是—— 流程或方法是项目组需要改善而自发出来的,并不是被人鼓吹出来。

    咨询师就应该像裁缝一样,做的应该是量体裁衣的活儿,而不是说——1) 你穿这件衣服不好看是你自己的问题。这么说就是独裁者了。2)我们这有一件衣服,这也好,那也好,你一定要试试。这么做就是推销员了。

  22. @Eric
    我原文提到过,我见过用TDD用得很成功的例子,只不过项目比较简单罢了。我做的项目都是比较“低层”的,系统层面的,这类的系统,一开始的时候,别说Test Case了,测试你都不知道怎么做。比如OS和DBMS相关的,集群相关的,高可用性相关的,高并发性相关的,高性能相关的。比如需求是,10ms的latency,1GBps的吞吐量,系统部署、升级,打补丁、回滚、修改配置时全部自动化而且在些过程中系统不能停止服务,系统的可用性是99.999%,成百上千台机器的性能监控和报警问题……,我很想知道你用TDD怎么个关注这方面的设计?一开始怎么个先整出个Test Case来?

    很感谢你留下的两个InfoQ的文章,不过,引用过来引用过去,至头来还是引用的人的话。建议你引用几个项目看看,无论你是引用的是诸如像LAMP这样开源杀手级的解决方案,或是像iPhone这样成功的商业的产品,或是像那些银行金融业里用的比较大的金融系统,都比你引用别人的话要有说服力得多。

    让我们看看TDD已经干出多大的事,而不是有多少人在说TDD的好! 这是鉴别某东西是否在炒作的标准之一。

  23. 我没说清楚,这里面特指是骂Thoughtworks,比如很朝鲜,也许您觉得这不算骂,但我觉得是。咱们这谁以很朝鲜为荣?之所以会单独说这事呢,是因为我发现留言的有Thoutworks员工甚至前员工的ID,他们还算比较压得住火在跟博主交流,我猜您也看出来了,所以我只能猜你想指望指责Thoughtworks后换来Thoughtworks的人把自己做过的项目经验白白给你秀秀。要是这样,我觉得这事挺搞笑。要我我是不会秀给你看。本来被骂就被恶心了,还无私的把项目经验贡献出来。自认没这个度量。总觉得那以后别人不用找Thoughtworks咨询了,找个有名的网站骂Thoughtworks去好了。这个省钱。

    不是Thoughtworks的人呢,更不可能了。就您这个态度,躺着都中枪,说了更惹一身骚,除非顺着你说。

    您呢,说别人不阴暗的说你有什么企图,那您也别老阴暗的以为别人有什么企图。这种隔空交流不得不揣测,我揣测你的时候用了“猜”指字,我的意思是,如果猜错,后面的说法都可以无视。所以你呢,也不要随便揣测我,我没有敏感,我猜您看到我用了很多反问觉得我很敏感,我只不过觉得值得反问的地方太多了,没时间组织语句照顾您的感受。也就是最近比较无聊才会发这么多字,不要把对自己的标准和对别人的标准总是双轨制。我发现您特别爱这么干。

    我很高兴看到您终于提到了具体的问题,我 猜 其实绕了这么多您就是想说,底层的项目无法使用TDD。

    咱不是这方面的的专家,随便参与两句,仅供参考。仅从您描述的不够全面的场景来分析,我依然不知道理论上,您这个问题怎么就没法TDD,我猜无非是TDD的成本太高了呗,世界上一切的方法都在成本大于收益时不可行。这种经典废话我真的不想说。但是我知道有专门搞了百台机器去查Bug的案例,人家拿得出来,而且觉得有必要,咱没做,那肯定是收益不够诱人。

    另外,我对简单这个词跟您理解有歧义,简单的反义词是复杂,不是困难。容易的反义词才是困难。那么我个人体验,面对复杂业务逻辑的商用系统,敏捷方法的价值会更大。或者说采用敏捷的收益在较多情况下会远大于成本。至于怎么裁剪,到具体问题时还要具体分析。

    这么说话太累了,我也懒得检查上面说的文字有没有歧义了。我想我如果在现实中遇到博主,绝对不会说话这么追求精确。保不齐哪说漏了也被博主揪到博文里批判一番。看到前面给我的回复我好怕怕哦。得了。发帖没有钱拿,伤了心情就不值得了。最后一贴了。

    一个人形成自己的观点和思维习惯有自己的环境,改变别人的观点比较难。所以这里总是两拨人在自说自话,谁对谁错。我们还是交给历史去评价吧。10年后,敏捷20年的时候我们再看。

  24. @飘过说两句
    哇!回复这么多,看得出来你回复时心情不是很好。所以我说你们很敏感嘛,所以我说你们信仰很深嘛。关于实质性的问题,好像我说的比大家说的都多,你的回复中我只看到两方面的东西:1)你的主观情绪,2)你的各种与讨论无关的道理。(经验保守,企图,猜测,成本,简单,歧义,见面,等等)。呵呵,看起来你比较情绪化,只希望你的这么做可以舒缓你的心情。

  25. TDD 这东西嘛,我想说 TDD 不等于 ATDD。也就是说 test-driven-development 并不意味着这个 test 一定就是 auto-test。如果能够自动测试自然更好,但测试驱动开发并不意味着你必须使用自动测试。

    从概念上讲,一群具有非常丰富的业务经验的测试人员进行的人工test,也可以用于driven-development,而这个模式恰恰证明了 TDD 的高度可行性。

    我们这个靠软件主业支撑的公司年产值几亿,十多年来一直全面使用TDD开发,如果TDD不行,我们早就倒闭了。

    auto-test 自动测试这个东西,不适合没有经验的新程序员,只能给非常资深的老程序员使用。例如编写文件系统我们会使用资深程序员让他自己去做auto-test。而编写文件系统这种活也不可能给新程序员去干的。

    但是编写终端用户界面就不一样了,那我们会考虑使用专业的测试员去测试,但不论是自动测试,还是专业测试员手工测试,这个测试都是足以驱动开发的,并且是可以经常回归的。

    假定你要做一个 POS 机的软件,他有些什么功能你知道么?划分成模块之后,具体的程序员会知道整个机器是干什么的么?显然,多数程序员根本不知道整体情况也不需要知道,最了解这个终端如何使用的是测试员而不是程序员。因此,由测试经理从初期开始就同软件开发经理一起调研需求非常必要,而测试案例也是有测试经理组织编写,这在我看来才是 TDD 的核心,也就是说软件的需求规格由更懂得这个软件功能的人去设计,并且由这个人写出的测试方案来驱动整个开发。测试可以不是自动的,测试员可以甚至必须是跟程序员不同的。这在面向最终用户的应用中是一个更靠谱的 TDD 模式。

    测试驱动开发的关键在于谁在前,谁在后,谁主导开发的进度与重点,至于用什么方式测试反而是次要的,人工测试在某种情况下可能更好,因为这意味着测试经理在主导开发,而这往往意味着敏捷,因为给测试经理提一个新需求比向程序员提需求往往要敏捷得多。

  26. 写的不错,总结一下:使用剪子剪头发不一定能获得理想的发型。有些理发师过度喜欢剪子,忘记了还有推子和梳子的作用。也许,剪子真是过度炒作的,不过,我还真是见过使用剪子剪出的不错的发型,只不过那个发型比较简单了。更多的情况下,我看到的是教条式的生硬的剪,所以,不奇怪地听到了理发师们的抱怨——“自从用了剪刀,工作量更大了”。当然,这也不能怪他们,剪刀本来就是很难把控的方法。最后法无四乘。人心自有等差。宇宙流善于取势,秀策流善于取地,但是你不管用什么流都不行。因为你不是高手。

  27. @kingfish 如果你是发型高手,是不是用剪子都不一定了。不过,我想,这里不是讨论用剪子的问题,这里讨论的是,这个发型适不适合你,什么样的发型适合你的问题。并不是看上去很美的发型,适合你的发型,很时髦的发型就适合所有人的。

  28. 看到这么认真愿意回复评论的博主,我也说说我对TDD的感觉(这个词比较恰当一点)吧:
    首先,TDD的这个T,不管是UnitTest,IntegrationTest还是AcceptanceTest都可以,这只是一个形式,看你用在什么级别上了。似乎这个名字并不太恰当,很多人都把重点放在T上,但其实中心词是开发这个D上。TDD是以T的这个形式不断的去演化自己的代码,迫使你去做简单的实现,去写可测试的代码,从这个意义上来说TDD可以帮助你设计。在我TDD的过程中,有很多的Test帮我演化了代码,在演化过程中这些Test也被我删除了,它们只是我实现现有代码过程中的一个工具而已。
    但是如果认为TDD是万能的,既可以设计,又可以保证质量,过分依赖TDD那就非常错了。我曾经试过完全顺着TDD的思路写,发现我是被推着写完代码的,这样写出来的代码是我完全想不到的,就感觉像是不经过大脑写的,完全不存在设计,质量只是能保证那些UT过而已。TDD只是一种手段,还是需要我们思考和经验来设计,可以通过TDD来去实现这个设计。那么,此时TDD可以基本保证是按我们的设计来实现代码的。TDD不会帮你做SOLID的设计,更别说什么高性能,高并发可伸缩的设计。
    最后还是说一句,敏捷不是万能的,它和传统开发最大的不同是没有所谓的最佳实践或者最佳模式,照搬是不会成功的。敏捷推行的实践确实都很好,但是光知道How是没用的,必须理解这些实践背后的Why,才能发现真正对自己有用的How,即使做法可能和这些实践,和别人一样,但得到的结果却完全不同。
    罗嗦了这么多,完全是自己想到哪写到哪,没啥逻辑性,让博主受累了,望海涵海涵,呵呵。

  29. @nEo
    非常非常非常同意你的观点。TDD只是一种手段,在理论上高度,不一定能在实践中达到。软件开发中人是复杂的动物,项目也此复杂。所以,教条和照搬都是不会成功的。走自己的条,而把敏捷真正当成一种参考或建议,千万别把敏捷当成项目的核心。你所有说的,正是我所提倡的。

  30. @陈皓
    这就是我理解的敏捷,敏捷说穿了就是一些原则和思考方法,在推广的过程中,为了让更多的人接触和使用,当然从推广这些实践开始,但是很多人在接受培训的时候,接受到却只有实践,只知道这么做好,不知道为什么这么做,直接就在项目中用了,可能开始尝到甜头了,但是最后该失败的项目还是失败了,没有改变什么。所以现在对于敏捷才会有这么多的争吵,鄙视敏捷的和推崇敏捷的,两边很少不在同一层面上讨论,各说各的,谁也说服不了谁。

  31. 看到测试驱动的导论,有些不成熟的想法。最近接触small talk,作为一门在国内沉积已久的语言,在国外却开始复苏,这门语言的一个显著的特点就是TDD 测试驱动开发方式,为此还专门设计了一个开源的测试框架SUNIT,其IDE叫squeak, 有专门的测试驱动工具,编了一个函数,需要有相应的测试类进行质量测试。以测试为驱动TDD 并不是银弹, 但是针对特定的领域,还是应当实施这种软件工程方法论吧。

  32. “关注测试而不是设计。这可能是TDD的一个弊端”
    相反,单元测试的TDD关注的就是设计,测试是自然而然的副产品。我觉得作者对TDD有误解。

  33. @陈皓
    个人认为所有说关于适用性的问题,基本上是相对无聊的话题。我这些年经历的项目也没多少100%TDD的,大部分的项目30%到5%左右。敏捷也不可能是项目的中心,像ThoughtWorks这样大家都认为是敏捷咨询/交付公司的口号中也没有发现有一句话跟敏捷沾边。谁也不可能硬拿着一些实践不管自己的状态而使用,除非脑袋进水。如果说这篇文章给意识不清醒的人,我认为作者的笔墨还是值得的。

    我从来没认为Test能带来良好的设计,啥都有好有坏。如果说ThoughtWorks的人有些脑袋发热,我也会见一个拍一个。但现在我认识的几十个人里面还没有冲昏头脑的人。

  34. 测试范围的确定:的确如此,kent beck在书里也没有给出定义,通常认为,范围、深度、开始、终结testcase都依赖于开发人员的经验和能力。
    悖论一说:不太苟同。testcase实际上是实现设计的手段和方法,需求变更了,设计自然要变,testcase变更无可厚非。可能的确在效率上有所下降,但Testcase可以保证一份所谓的100%覆盖率的测试程序,更重要的作用是在项目进程中让程序员每时每刻对自己的代码都充满自信,这也是XP强调的一个理念。相信每个程序员都有个越到最后越不敢测试甚至运行自己代码的经历。
    “如果说我们开发的Test Case是用来保证我们代码实现的正确性,那么,谁又来保证我们的Test Case的正确性呢?”
    这个,去了解一下计算机的历史,它不像数学定理,每一个都有严密的推理证明,而计算机科学中很多都没有前因后果。就好比写一些测试程序去测试功能,没有人能保证测试程序没有bug.

  35. 陈皓 :
    我谈的不是理论和理解,我谈的是实际操作过程中的问题。

    @陈皓
    没有理解谈何操作?对一个工具,你自己用错了,然后来批判这个工具?

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注