惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
Y
Y Combinator Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Security Blog
Microsoft Security Blog
aimingoo的专栏
aimingoo的专栏
I
InfoQ
C
Check Point Blog
IT之家
IT之家
MyScale Blog
MyScale Blog
Apple Machine Learning Research
Apple Machine Learning Research
Vercel News
Vercel News
Last Week in AI
Last Week in AI
GbyAI
GbyAI
P
Proofpoint News Feed
量子位
Stack Overflow Blog
Stack Overflow Blog
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
人人都是产品经理
人人都是产品经理
B
Blog
T
The Blog of Author Tim Ferriss
H
Help Net Security
云风的 BLOG
云风的 BLOG

agile | 酷 壳 - CoolShell

“单元测试要做多细?” | 酷 壳 - CoolShell 持续部署,并不简单! | 酷 壳 - CoolShell Test-Driven Development?别逗了 | 酷 壳 - CoolShell “品质在于构建过程”吗? | 酷 壳 - CoolShell 在新浪微博上关于敏捷的一些讨论 | 酷 壳 - CoolShell 为什么Scrum不行? | 酷 壳 - CoolShell Bob大叔和Jim Coplien对TDD的论战 | 酷 壳 - CoolShell 敏捷水管工 | 酷 壳 - CoolShell 再谈敏捷和ThoughtWorks中国咨询师 | 酷 壳 - CoolShell [转]TDD到底美还是不美? | 酷 壳 - CoolShell 那些炒作过度的技术和概念 | 酷 壳 - CoolShell 一些杂项资源 | 酷 壳 - CoolShell 为什么敏捷方法能在软件开发中行之有效? | 酷 壳 - CoolShell 结对编程的利与弊 | 酷 壳 - CoolShell
TDD并不是看上去的那么美 | 酷 壳 - CoolShell
陈皓 · 2011-02-12 · via agile | 酷 壳 - CoolShell

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

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

Loading...