The tragedy of 100% code coverage这篇文章前两天被人推荐到hacknews以后引起热烈讨论, Uncle Bob也转发了。我有幸恰好看到,尝试翻译了一下。在TDD社群里也引发了一些讨论。
这里把我看到的意见罗列一下,供大家参考。
- 很多人认为这涉及到为什么写单元测试。@虎头锤推荐了sandi metz关于有效的单元测试的视频。
这个讲座里把测试按照
a,被测方法的发起方和接收发
b,被测方法是无状态的还是改变状态的
两个维度对测试进行分类,并分别给出建议,非常值得一看。
- @李小波 从组织层面对这个问题进行了解读。
我认为决策者是站在组织层面思考,如果每个人都像文章作者一样对测试的理解这么深的话,是不用强制的。
回头看一下大部分不强制的企业,测试覆盖率趋近于 0。而强制 100% 一段时间后,大家养成了写测试的习惯,也尝试到了测试不合理的痛苦,就会慢慢学会哪些是有价值的测试。
所以我认为,强制 100% 不是一个终极状态,而是一个过程。
- 无独有偶 @袁英杰 也从另一个层面给出了很有启发的观点。
我是觉得,一个程序员愿意事无巨细不厌其烦的进行100%覆盖的tdd,并自得其乐,作为教练,这是绝对不应该打击的事。让他通过坚持这样的活动去深刻理解tdd, 然后自己找到平衡的边界。“守,破,离”,这是有效学习的规律,没有“守”,直接奔向“破”和“离”,结果大概率会是“没搞懂”。
另外,同一件事,对于不同的人,投入产出比是不一样的。对于教练,会觉得那样做是浪费(投入大,收益小)。但对于初学者,其收益却要大的多,因为其学习所带来的理解和掌握,这对他个人而言,就是一个巨大的收益。
看到这个意见,我意识到自己的翻译有误导的地方。 原文并没有强制的字眼,是我加进去的。 大概这几天自己想推动TDD比较多就带入了。 从这篇文章内容而言, 主要还是程序的自发技术热情在起作用。
- @91分享了他在事务操作中关于覆盖率的准则。专治遗留代码,简单实用。
我對團隊中legacy code 測試覆蓋率的部分,如果真要給個標準,基本只有兩個:
1. 有 CI, coverage > 0%
也就是有就好,這代表後面的人都只需要加測試代碼 不用一堆設定。
2. 每次 build coverage 只能往上,不能往下。這代表 你只需要為自己這次異動到的代碼負責。沒要他幫別人擦屁股,擦自己屁股合情合理。
滿足了這兩點之後,鼓勵團隊頻繁 commit。
記得設定coverage 往下掉就會通知團隊。
- @张刚 提到要功利的写测试。
我经常说自己喜欢功利的写测试和做TDD,但是这样说并不利于教会别人怎么做。这篇文章给出了一个更清晰的视野,值得一读。当然 我觉得仍然有一些未提及但是重要的,值得强调的原则:对我而言 是由外而内、价值导向、风险导向。
国外网友的一些讨论
- 更悲剧的是,就算费了这么大力气做到100%,也不代表提供了足够的保护。因为同一段代码可能有很多不同的输入组合,单单覆盖了一遍并不能保证程序的正确性。
回复:应该更重视代码分支覆盖,而非代码行覆盖。
回复:如果真的要狠狠的测代码,应该考虑变异测试。
回复的回复:变异测试的两个难点。一是很多变异让整个系统无法工作了,二是如何判断变异造成死循环了。
回复:有些工具 PEX 和 IntelliTest可以自动产生输入去覆盖不同分支。 - mock太多引起的问题。
a. 整个流程分散到多个类分别测试后。对业务最重要的主流程有可能被覆盖的更少了
b. mock把过多首次写代码时的假设固化在了测试中,对重构造成阻碍
c. mock使测试脆弱,实现修改往往不必要的破坏了测试 - 程序语言应该把测试作为一等公民提供内建支持。
回复:D,Eiffel,Rust,粉丝纷纷举手。
Ruby粉:Ruby虽然没有语言层面的支持,但测试已经是社区文化的核心。
回复:动态语言如Ruby和JS更容易对付测试中的依赖问题。
对JS代码而言,如果没有达到非常接近100%是一件奇怪的事。
回复: Haskell弄个mock简直跟玩一样。 - 我只有一段程序可以有信心说100%覆盖了。 是一个处理 HTML/CSS 颜色值的库, 测试产生了所有16,777,216个16进制值和rgb值,逐个进行测试。
- 100%覆盖的悲剧在于投入产出比太低。IBM的一项研究表明70%就差不多最高了。
回复:对初创公司而言,应该找到最重要的10%~20%的功能,对它们进行测试覆盖。
回复的回复:但是怎么找那10%~20%,到处可见的TDD教程都是关于严格的100%覆盖的方式。应该怎么学习高投入产出的写测试呢? - 越差的程序员,写的测试越多。他们不去写良好清晰的代码,而是写些无意义的,缺乏抽象,冗长,易错的测试,得到只是虚假的信心。
回复:我看到过这种争论很多次了。糟糕的代码结构导致了糟糕的测试。但是常常有人认为糟糕的测试就是问题本身。它仅仅是症状。
回复:我尝试从正面来理解这个评论。良好的代码结构需要的测试较少,测试的代码也较短。
回复:我曾和一些100%教徒工作过,看起来他们关注测试更胜过功能。最可笑的是当我改好了一些bug后,他们因为代码变得不太容易测试了而怪我。
回复:我见过的差的程序员都是不写测试的。事实上他们写负的测试,当他们发现自己的代码通不过测试的时候,他们把我写的测试删掉了。
回复:我宁愿接手过度测试的代码,而非缺乏测试的。
回复:我工作过的最好的代码库有30年历史了,设计非常好,没有测试。
回复的回复:幸存者偏差。是因为它的设计特别好所以能撑30年,很多没有测试的烂代码早就死了。