写有价值的单元测试

发表于:2017-9-22 17:04

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:ali家鸽    来源:51Testing软件测试网采编

  这是写给开发同学系列文档中的一篇,主要讲单元测试
  写这个系列的原因是发现开发同学,尤其是偏业务的开发同学对于软件开发中的很多实践和理论理解的不够清楚。比如设计文档,代码评审,单元测试,集成测试和自动化测试,持续集成和持续发布这样一些耳熟能详的概念,说起来每个开发同学都听过,但很多人并没有深入考虑过为什么要引入这些实践,实践需要哪些手段,要达到什么目的,要坚持什么原则?所以这些实践落地的过程也是千差万别,效果往往也不甚理想。
  通过这一系列文档,我会把我所了解的每个实践的来源、适用范围和价值用最简明的方式写出来,并结合具体的开发环境提供一些具体的操作手段,帮助同学们按照正确的路径快速了解和上手。
  理论基础
  大数据就像青少年谈性,每个人都在说,但不知道谁做了。每个人认为另外人在做,所以每个人都声称自己在做。
  这是大数据火的时候调侃大数据的段子,套在单元测试上也一样适用。说起来单元测试是开发应该了解的基础概念,然而实际并非如此。
  我在若干团队(不限于淘宝)作过以下的调查,结果基本类似:
  1. 有多少人写过单元测试? 60% ~ 80%,视团队而定
  2. 每个单元测试里面都有至少一个assert语句,而非使用System.out.println? 20% ~ 30%
  3. 单元测试的行覆盖率超过50%? < 10%
  4. 每个单元测试平均每天会被执行10次以上? < 5%
  5. 所有单元测试一定不会因为断网失败? < 1%
  大多数开发都认为自己写了单元测试,但大家写的"单元测试"都不相同。实际上,能达到最后一点要求的单元测试才是有价值的单元测试,其它所谓的"单元测试"最多只能算是"测试代码"甚至"代码片段"。
  "单元测试"和"测试代码"的区别就像钻石和煤炭,虽然都由碳元素(程序代码)构成,但前者价值远远大于后者。随着时间的流逝,"钻石恒久远,一颗永流传",而煤炭只会被烧掉,变成二氧化碳;同样,随着项目的不断演进,"单元测试"能一直存在并发挥价值,而"测试代码"只会被一行行注释掉,直到某个接手的同学把一个全被注释掉的测试文件从项目中删除。
  要理解这一切发生的原因,首先我们需要了解软件开发中的几个定律:
  错误率恒定定律
  程序员的错误产出比是个常数
  对某一个程序员来说,实现相同功能会犯的错误(BUG)是固定的,不受程序员自身意愿影响,不受绩效影响,也不受项目紧急程度影响。不考虑程序员水平的成长,错误产出比在很长一段时间(每个项目的间隔)内可以认为是个常数。
  这个定律告诉我们,如果一个程序员感觉今天状态很好,比昨天多写了一倍代码并向QA声称bug数肯定比昨天还少,而QA测试的结果居然真的如此,那很大可能是QA测的不够仔细,而不是程序员的代码水平一夜之间突飞猛进。
  这个定律还告诉我们,TL开会时要求程序员写代码尽量不要犯错没有任何意义,这种要求就像要求程序员明天长高5公分一样,不具有可操作性。
  错误率恒定定律决定了错误数是一定的,但并不意味着这些错误产生的影响是一定的。恰恰相反,不同开发方式中,这些错误带来的影响差别非常巨大。菜鸟和老手完成相同功能发生的错误可能只差5倍,在具体项目实践中带来的影响却可能差了20倍、50倍甚至更多。原因就是以下的规模代价平方律。
  规模代价平方定律
  定位并修复一个BUG所需的代价正比于目标代码规模的平方
  如果一个20行的函数刚写完时作者就能发现有BUG,找到并修复这个BUG可能只需要1分钟,并且不担心影响其它使用者。如果是在200行的一个类中,别人调用时发现有BUG,阅读代码并定位问题可能就需要一个小时,对这个问题的修复重新代码评审又要花一个小时。如果在系统和系统联调的时候才发现这个问题,前面扯皮就要半天,改完了重新回归又是半天。如果改这个BUG的人已经不是原作者的话,往往担心改了这个BUG又引入其它问题,于是修改方案就要拖一群人讨论半天,最终改完了要求QA作大范围的回归,结果还是还不放心。
  规模代价平方律是很多软件工程实践的核心思想。根据平方律,为了减少错误修复的成本,要尽可能早的发现错误,在尽量小的范围内定位并修复错误。由于这是一个平方律而非线性率,所有这方面的努力都是非常划算的。比如以下实践很大程度上就是为了尽早发现错误,以后有机会我再逐一介绍这些实践。
  1. 设计评审
  2. 代码评审
  3. 单元测试
  4. 自动化测试
  5. 结对编程
  6. (Scrum)小迭代,迭代后期的成果演示
  规模代价平方律对程序员的重要性可以和牛顿三定律在初等物理中的地位媲美。遗憾的是很多程序员写了很多年都不知道这个定律律,往往低估了错误修复的时间。所以业界对程序员的自我评估有如下经典的吐槽
  当一个程序员宣称他已经完成了90%的工作时,他的意思是还需要相同的时间来完成剩下10%的工作
  单元测试的目的
  错误率恒定律告诉我们错误是不可避免的,而规模代价平方律告诉我们要尽早发现错误。单元测试作为一个行之有效的工程实践,目的只有一个
  单元测试的目的是尽早在尽量小的范围内暴露错误
  不同项目的单元测试方案各有不同,各种方案的选型往往也会有争议。这时候一定要记住单元测试的目的。凡是利于此目的,即使复杂一些的方案或有一定学习成本也可以采用,凡是不利于此目的的方案,即使看起来很美也没有采用的必要(本文最后有几个单测的误区具体说明这点)
  单元测试的要求
  为了达到尽早和尽量小的范围以及暴露错误,对单测有以下要求。 实践证明,只有满足这些要求的单测才能实现单测的目的。
  单测要能报错
  有些同学不喜欢用Assert,而喜欢在test case中写个System.out.println,人肉观察一下结果,确定结果是否正确。这种写法根本不是单测,原因是即使当时被测试代码是正确的,后续这些代码还有可能被修改,而一旦这些代码被改错了。println根本不会报错,测试正常通过只会带来虚假的自信心,这种所谓的"单测"连暴露错误的作用都起不到,根本就不应该存在。

21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号