原文:http://coolshell.cn/articles/3649.html

春节前的一篇那些炒作过度的技术和概念中对敏捷和中国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的解决,千万不要因为有问题就要马上换一种新的开发方法”。相信我,大多数的问题是人和管理者的问题,不是方法的问题。

    虚拟座谈会:TDD有多美?
    http://www.infoq.com/cn/articles/virtual-panel-tdd

    TDD并不是看上去的那么美的更多相关文章

    1. 让Python带你看一场唯美的横飘雪!

      “北国风光,千里冰封,万里雪飘”,这句诗描写了一句美丽肃静的风光图,恰逢昨天笔者这边也下了一场比较大的雪,要不今天就用Python带大家也来领略一次美丽的雪景? 开发环境 版本:Python3.6 系 ...

    2. BBC这10部国宝级纪录片,让孩子看遍世间最美的地方

      https://weibo.com/ttarticle/p/show?id=2309404382383649486138#related

    3. 看美剧英文字幕学英语的利器——“深蓝英文字幕助手”简介

      我从初中开始基本上就是一个英语很烂的人,数理化再好有什么用,工作了,结果发现数理化都没啥用,最有用的还是当年学的最烂的英语.于是在2011年年底开始了学习英语的课程,在学习的过程中,外教经常会放英剧美 ...

    4. 测试驱动开发(TDD)及测试框架Mocha.js入门学习

      组里马上要转变开发模式,由传统的开发模式(Developer开发,QA测试),转变为尝试TDD(Test-driven development,测试驱动开发)的开发模型.由此将不存在QA的角色,或者仅 ...

    5. 【学英语~磨耳朵】2013年以来看过的所有美剧&电影&纪录片等等

      我看美剧看太多了,而且同一部剧刷很多遍.这种coach potato的做法其实一点也不好,英文会好可能只是意外收获.下面是单子: 美剧: 老友记-情景喜剧-10季全看.至今还在网易云音乐循环10季音频 ...

    6. Win10看图总有遮挡?如何找回好用的照片查看器

      来,大家日常在电脑上查看图片是用什么软件?老牌的ACDSee.XXX看图王.美图看看还是Win系统自带的呢?反正小编在没什么特殊需要的时候,只用系统自带,免除安装.功能够用,想要进行处理也能用Win自 ...

    7. IDDD 实现领域驱动设计-一个简单业务用例的回顾和理解

      上一篇:<IDDD 实现领域驱动设计-由贫血导致的失忆症> 这篇博文是对<实现领域驱动设计>第一章后半部分内容的理解. Domain Experts-领域专家 这节点内容是昨天 ...

    8. 对HTML+CSS+JavaScript的个人理解

      HTML就像人的骨头架子,是人的根基,要有个人样呀,一个网站,一个WebApp要是缺根儿骨头,那就像人少个胳膊少个腿儿的,行动不方便啊:CSS就像人穿得衣服.鞋子,男人的纹身,女人擦得粉儿,好看呀,一 ...

    9. Apache Spark源码走读之14 -- Graphx实现剖析

      欢迎转载,转载请注明出处,徽沪一郎. 概要 图的并行化处理一直是一个非常热门的话题,这里头的重点有两个,一是如何将图的算法并行化,二是找到一个合适的并行化处理框架.Spark作为一个非常优秀的并行处理 ...

    随机推荐

    1. Ubuntu 12.10 安装VirtualBox增强功能

      原文链接:http://fengbaoxp.iteye.com/blog/1871825 Ubuntu 12.10 Desktop         首先,通过VirtualBox菜单(设备->安 ...

    2. Oracle 数据库 dbConsole 配置笔记

      Oracle安装后,DBConsole有时安装会失败,这时可以通过以下命令恢复: set oracle_sid=orclemca -repos recreateemca -config dbcontr ...

    3. Ng第一课:引言(Introduction)

      Machine Learning(机器学习)是研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能. 它是人工智能的核心,是使计算机具有智能的根本 ...

    4. (转)C# 中使用分布式缓存系统Memcached

      转自:http://blog.csdn.net/devgis/article/details/8212917 缘起: 在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了 ...

    5. 微软新一代Surface发布,参数曝光

      在沉寂许久之后,Surface 2及Surface Pro 2又有猛料爆出,这一次不单单是新品展示,伴随的还有更多的详细的参数和全新配件. 从外观来看,新一代的Surface外形上沿袭了上一代,但颜色 ...

    6. libtool 创建库的工具

      libtool 创建库的工具 1. 背景 在不同的系统中建立动态链接库的方法有很大的差别,这主要是因为每个系统对动态链接库的用法和实现并不相同,以及编译器对动态链接库支持的选项也不太一样. 对于开发人 ...

    7. 《mysql必知必会》学习_第10章_20180731_欢

      第10章,计算字段. P64 select concat (vend_name,'(',vend_country,')') from vendors order by vend_name; # 拼接, ...

    8. [mysql] mysql 查询语句收集

      // 1. 筛选出当天的中奖名单            $where = " date_format(f_ctime,'%Y-%m-%d') = current_date()"; ...

    9. Android-ANR异常

      A N R : Application Not Responding : 应用没有响应异常 引发ANR的原因是,组件的主线程去执行了耗时操作多少秒,就会引发ARN异常: 引发ANR异常的案例: pac ...

    10. 受邀与微软售前技术团队参与TFS技术沟通会议

      微软正式发布Azure DevOps Server 2019的第一个版本,作为Team Foundation Server (TFS)2018的升级版本和替代产品. 作为开发运维一体化平台的主打产品, ...