10-11月份,我花了很多时间在项目D的代码重构和单元测试上,这期间重读了《重构》、《单元测试的艺术》和《Mastering Unit Testing Using Mockito and JUnit》等基本关于单元测试的书。一点感受,记录于此。
关于为什么需要单元测试,以及测试驱动开发的原理,已经有大量的文章书籍讲述,我就不再浪费时间发表陈词滥调了。
我的一个基本观点是:单元测试是程序员的一个基本修养
虽然说测试驱动开发(TDD)的口号已经吹响了很多年,而实际项目中,单元测试代码因为不产生直接经济效益,常常被人轻视甚至忽视,当然这是非常错误的。
我们先来一个逻辑推理的小游戏。见过很多同行并不是很关心单元测试,很多人都标榜自己关注细节,因为细节决定成败。
而我们知道单元测试里边全是代码的细节,而这些细节决定项目的成败。那么你有什么理由忽视单元测试呢?
所以,如果你的同事或你的经理不重视单元测试,你就可以用 “成败” -> “细节” -> “单元测试”的反向推导来说服TA。
单元测试是安全网
单元测试是代码正确性的第一道防线,因为它是白盒测试,所以能够覆盖到功能实现部分很多不同的分支情况,以及异常的处理。
单元测试和重构
我的一句口头禅是:没有单元测试做保证,谈重构就是耍流氓。在过去的工作中,常常遇到同事说我要重构代码,而对应代码的单元测试都没有呢。没有足够单元测试的情况下,重构是凶险而且低效的。反之,如果有足够好的单元测试,我们可以大胆地重构而且往往不需要太多时间。
下面吐槽一下身边的实际情况。
不堪的现状:单元测试不被重视
某项目M本身是个几年没人维护的项目,去年老板一声令下要跟另外的产品集成添加某个功能,三个同事(两个开发一个测试)开始上手项目写了一堆代码,今年上线之后问题不断鸡飞狗跳,好几次半夜起床修复问题。出现这些问题,总结原因有好几个。其中很重要的几点就是跟测试相关的。这里说的不是QA的问题,而是开发出现的问题。
首先,单元测试的覆盖率很低。项目的整体覆盖率是30%,可以说是很低了。而且单元测试代码本身质量不高,覆盖的多是一些零散的容易做单元测试的模块和类。项目是用python语言开发的,对于python这类的弱类型语言写的代码,没有足够的覆盖率连代码是否有语法错误都不清楚。(说句题外话,这个项目连pylint都没有使用,由此可见工程水平多么令人担心。)
单元测试是白盒测试,必须保证每个被测试的函数里边很高的路径覆盖,这样方能保证程序在各种情况下都是按照预期运行的。为什么单元测试应该由程序员来写呢?很显然的一个原因是程序员写测试的过程也是验证自己思路的一个过程。因为程序员知道自己对输入输出的期望,并且知道错误情况出现时代码的行为。
其次,单元测试的质量不高。跟很多程序员一样,m项目中的两个开发对于单元测试认识不够,他们仅仅是让代码能够运行起来。核心代码都是满目疮痍,遑论单元测试?所有的单元测试都是挑选了最容易做到的、无关痛痒的部分来测试。这样的工作方式(态度)其实就是面向经理编程。
第三,单元测试和集成测试混为一谈。m项目中有一部分功能是通过REST API暴露出来的,一个开发的同事觉得单元测试难做,就直接将怎个REST服务运行起来(该服务还是用了mysql数据库和几个依赖的外部服务),然后写客户代码调用REST API,这怎么就能叫单元测试呢?这已经是集成测试甚至变成系统测试了。分析一下,为什么会这样做呢?是因为开发不理解什么叫做单元测试吗?显然不是的。而是因为代码写的耦合度太高无法做单元测试,只好偷懒做“整体测试”啦。
出现上面的情况,怎么做才是正确的解决办法呢?只能重构代码,提高代码可测性才是正道。不可测试的代码,一定遍布着“坏味道”。
实际开发中常常遇到的问题以及对策:
1. 代码和单元测试的不是同一个人写
我就做过这个错误事情。07年的时候,我在开发某个SDK。刚好公司来了几个实习生,为了让他们快速增长经验上手项目,也让我有更多精力放在功能开发商,我的老板让我写核心代码,实习生写单元测试。虽然这个事情并没有出什么乱子(因为那个SDK的功能以及很稳定了),但是后来回想起来,当时的做法是不好的。因为老板的目的,一个都没有达成。先说实习生上手这方面,他们回去读核心代码,理解之后去写测试,因为缺少经验,他们写出的测试并不能很好地覆盖各个分支。一些我写的代码并不好,因为他们缺乏自信心,所以也不敢跟我提出来。他们只会问我一些皮毛的小问题,所以他们的成长速度并不理想。再说我的时间精力方面,因为当时的几个实习生每天要找我问各种小问题的情况很多,我很少有连续的大片时间,所以并不能很专注地开发新功能。那么,怎么样才是理想的方案呢?我想,如果再来一次,我应该会采取结对编程的方式。结对编程应该是带人入门的最好方式,没有之一。
2. 先写代码,再补单元测试
这个现象,我经历过很多次。你们有没有经历过呢?每次都是相似的:时间紧,任务急,先撸了代码再说。然后也许是高层领导要检查某些指标了,或者是团队自发地觉得需要还技术债了,才开始正视这个问题。这个情况在我所经历的公司和项目中,屡见不鲜。
那么,问题来了。该怎么破?
我觉得首先要在团队中建立一个“测试头等重要”的价值观,有了共同的价值观,大家才可能步调一致。分三个方面来讲,第一个是在团队中要反复强调单元测试和核心代码同等重要,要求每个人都必须写测试。而且要尽最大可能赢得老板们的支持,向上管理当然不容易,可能需要进行很多次的布道和说服。项目期限比较紧张的时候,也不能够放弃任何努力。说服领导,做对的事情,而不是容易的事。第二个是要尽最大努力让单元测试简单快速,千万不要成为开发工作的负担。比如用最简洁的单元测试框架,并且持续集成时一定运行单页测试。第三,对单元测试的质量要求决不妥协,不能因为是测试代码,所以要随意写写。好的团队和个人,都是追求极致的。核心代码和单元测试都是一个妈生的,不可以厚此薄彼。具体的做法,个人认为应该在code review的时候做充分的检查,不合格的代码或测试用例一定要改掉或重写。
3. 程序员缺少足够的技能或缺少合适的工具
在我服务过的公司中,从来没有听说有正式的单元测试的培训。原因很简单,因为单元测试也仅仅是一项编程活动。大家平时看过太多的《xxx编程》《xxx高级编程》之类的书(或者受到相似的培训),却很少有人专门讲单元测试的。相对来说,单元测试在技术上并没有什么特别需要学习。但是有一点很重要,就是单元测试的一些最佳实践,是非常适合在团队里边分享学习和践行的。
我入行的时候,TDD运动还没有兴起,尤其是在C++社区。记得2004年加入第一家公司,写单元测试的时候只有CppUnit可以选择,而且没有听说过mock,很大程度上这跟C++语言相关。(现在的C++社区有没有发明出合适的轮子呢?有知道的同行可以给我留言)其他的语言社区像JAVA,.NET,Python等等,情况好很多。例如前段时间我做的一个项目就是用了老牌的JUnit和Mockito,用起来非常舒服。
以上是我对于如何解决单元测试相关的问题,从技术、流程和管理几个反面的感想,欢迎程序员同学们交流指正。
【2018,12.29 - 12.30】
---------------------
作者:痴痴笑笑
来源:CSDN
原文:https://blog.csdn.net/IDisposable/article/details/85345769
版权声明:本文为博主原创文章,转载请附上博文链接!
网友评论