作者:Daniel Lebrero
原文地址:http://labs.ig.com/code-coverage-100-percent-tragedy
人世间的事总不乏喜剧的反转。14年来我一直布道TDD(测试驱动开发,或者更早的名字:测试先行方法),或者至少劝说开发者去写单元测试。然而,最近我却发现自己越来越多的说:“你为啥写这个测试?”,而非:“你应该写个测试呀。”
到底发生了什么?
当我在办公室溜达时,一个开发者叫我去帮他写一些单元测试。看起来他正苦于用Mockito来测试下面这段代码:
当我说:“你不用测它”,我觉得他吃了一惊。
“不,我必须要测!”他说,“不然我怎么知道这段代码正常工作?!”
“这段代码很明了。没条件语句,没循环,没数据转换,啥也没。这段代码就是一点点老套普通的胶水代码而已。”
“但是如果没用测试,阿猫阿狗都可以来改点东西,结果代码坏了!”
“你想想看,如果真有个邪恶或者无脑的程序猿把这么简单的代码搞砸了。你觉得他看到个单元测试失败了会怎么办?当然是把单元测试删了。”
“那要是你必须得测试它,你会怎么写?”
“如果这样的话,我会写成这样:”
“但是你没用Mockito呀!”
“没用又怎么样呢?Mockito在这儿帮不上啥忙。相反还会让你的测试更加复杂和难懂。”
“不过我们已经决定所有的测试都要用Mockito!"
我:“……”
第二次我再碰到他的时候,他骄傲的告诉我他已经搞定怎么用Mockito写那个测试了。
尽管我可以理解搞定问题带来的精神满足,但是这仍让我悲哀。
第二个例子
我被一个程序员拽去,他正兴奋于新程序的高覆盖率,以及新的最爱,BDD(行为驱动设计)。翻了翻代码,我们看到了下面的Cucumber测试:
如果你曾经用过Cucumber,你一定不会惊讶于这个测试需要下面这些支持代码:
而所有这一堆就是为了测这段代码:
你没看错,一个简单的map lookup。我和这程序员很熟了,所以坦率的说:“这完全是浪费时间。”
“但是我老板期望所有的类都有测试”,他回答。
“以什么样的代价呢?”
“代价?”
“好吧,但是这些测试跟BDD一毛钱关系也没有。”
“我知道,但是我们决定了,要用Cucumber写所有的测试。”
我:“……”
尽管我可以理解贯彻中意的工具带来的精神满足,但是这仍让我悲哀。
悲剧在哪?
悲剧的是他们两个都是很聪明的开发者(聪明到我愿意跟他们上团队访谈节目),却都把时间浪费在写这种测试,这种毫无意义而且还会继续浪费新一批程序员时间去维护的测试。
悲剧的是我们不是为任务选择合适的工具,而是坚持不懈的使用错误的工具,却没用什么实际的原因。
悲剧的是一旦某个“最佳实践”成了主流,我们就忘了它的初衷,采用它为了什么收益,更重要的是,使用它的代价。
相反,我们只是不假思索的应用这些“最佳实践”,这意味着最终我们只能得到非常平庸的效果,大部分的收益没有得到,却付出了所有的甚至更高的代价。以我的经验写出好的单元测试是相当困难的工作。
100%覆盖率是个值得追求的目标么?
是的,每个人都应该达到这个目标 …… 但是一个项目就足够了。我的意思是你需要做到极致去知道它的局限是什么。
我们对于一个极端已经很有经验了:完全没有单元测试的项目,所以我们知道这样做的痛苦。而我们往往缺乏另一极端的经验:100%覆盖率的项目,并且强制所有的事都必须TDD。
单元测试,尤其是测试先行方式,是非常好的实践。但是我们需要学会哪些测试是有用的,哪些是适得其反的。
请记得没用什么是免费的,没有银弹。停下来,想一想。
关于作者:
Daniel Lebrero 在IG大数据团队担任技术架构师。作为一个超过15年JAVA经验和4年经验的Clojure程序员,他现在是函数式编程的忠实拥护者。你可以通过 Twitter, LinkedIn, 或他的个人 blog 找到他.
译者感言:
自发采用TDD是令人振奋的,被强制推广TDD却可能是非常让人沮丧的。
我的一点感觉,很多问题出在决策者对问题本身毫无兴趣,问题只是引出解决方案的短暂前奏而已。
就像郭德纲的相声:你有病啊? 你有药么!?
重要的是吃下某个灵丹妙药,没有药,病就好像不存在一样。
然而察觉问题,才是解决的开端。