评论《测试的道理》

sorra 发表于 2016 09/15 16:46 修改于 01/11 21:47 阅读数715

王垠写了一篇《测试的道理》 http://www.yinwang.org/blog-cn/2016/09/14/tests

我在知乎发表了一篇回答,在此贴出:

同意一部分条目。文末提供的案例非常精彩!
你们还是要多学习一下。

然后我对这篇文章来条分缕析一下:

测试的道理:

  1. 不要以为你处处显示出“重视代码质量”的态度,就能提高代码质量。
    +1!有态度无能力 => 教条主义。

  2. 真正的编程高手不会被测试捆住手脚。
    持保留意见。人非圣贤,高手也有糊涂的时候。

  3. 在程序和算法定型之前,不要写测试。
    +1!如果你在写一个要反复推倒重来的程序,先写测试就是浪费。不要拘泥于教条。

  4. 不要为了写测试而改变本来清晰的编程方式。
    结果你就发现这程序里每个类都有一个配套的 interface,还需要写另外一个 mock 类,去实现这个 interface。
    +1!反对每个类都配一个interface,铺张浪费。

  5. 不要测试“实现细节”,因为那等同于把代码写两遍。

测试应该只描述程序需要满足的“基本性质”(比如 sqrt(4) 应该等于 2),而不是去描述“实现细节”(比如具体的开平方算法的步骤)。有些人的测试过于详细,甚至把代码的每个实现步骤都兢兢业业的进行测试:第一步必须做A,第二步必须做B,第三步必须做C…… 还有些人喜欢给 UI 写测试,他们的测试里经常这样写:如果你浏览到这个页面,那么你应该在标题栏看见这行字……

+0.5!90%正确,10% depends。

  1. 并不是每修复一个 bug 都需要写测试。
    +1!不要在技术界搞『两个凡是』。

  2. 避免使用 mock,特别是多层的 mock。
    +1!过多的mock即使在TDD中也属于反模式。

  3. 不要过分重视“测试自动化”,人工测试也是测试。
    持保留意见。自测无所谓,上规模的测试还是要自动化的吧,自动化是大趋势啊。

  4. 避免写太长,太耗时的测试。
    +0.5!若实在无法避免,建议耗时的测试不要频繁运行。

  5. 一个测试只测试一个方面,避免重复测试。
    +1!是这样的。重复的后果是又多了一笔legacy code in test。

  6. 避免通过比较字符串来进行测试。
    持保留意见。比较字符串方便啊,看实际效果决定吧,可不能往反方向搞教条哦。

案例:

case Google

如果我当时按照 Google 队友的要求,采用已有的开源代码,或者过早的写了测试,别说无法在三个月的实习时间之内完成这个东西,就算折腾好几年也没有可能。

能否实现都不知道,就惦记着写测试,岂不是本末倒置。而且我搞过类似东西,确实很难写测试。

case Shape Security

队友们听说我花一下午重写了一个换名器,非常紧张,咋呼地跟我说:“你知道我们的换名器是花了多少个月的时间做出来的吗?你知道我们写了多少测试来保证它的正确性吗?你现在一下午做出来一个新的,你如何能保证它的正确!”

即兴给这种场景起个名字——“人月教条”。

在这个项目中,由于代码的改动幅度很大,在同事和部门领导的理解,信任和支持下,我们决定直接抛弃已有的测试,完全靠严格而及时的 code review,逻辑推理,推敲讨论,手工试验来保证代码的正确。

《代码大全》告诉我们,大多数缺陷发现于code review阶段。这个案例符合软件工程实践。

case Coverity

本来很明显的一个图论算法问题,加一个“&”就修好了,手工试验也发现问题消失了。然而 Coverity 的测试教条主义者们(包括写出这 bug 的那人自己),吵着闹着,严肃命令我必须写出测试,构造出可以造成这种后果的数据结构,确保这个 bug 不会再重新出现。
为一个我根本不会犯的错误写测试,而且它不可能再次发生,这不是很搞笑吗?就算你写了测试,也不能保证同样的事情不再发生。**如果你不小心漏掉“&”,下次同样的问题还会发生,并且发生在另外的地方,而你却没有给那块代码写测试,所以给这个 bug 写测试,并不能防止同样的问题再次发生。**这就像一个技术不过关的赛车手,他在别人不大可能撞车的地方撞了车,然后就要求赛场在那个地方撞上轮胎护栏。可是下一次,这个车手又会在另一个其他人都不会撞车地方撞车……

一针见血,这种情况的正确测试方法应为静态检查,一家生产静态检查工具的公司竟然不懂这个道理,呵呵。

总结:
大量的测试只是从0到N,可谁能完成从0到1的测试呢?
最后,我的理解浓缩成一句话:用心对待你的工作。