最近,身边的一位朋友因为需要在其单位与同事分享单元测试(Unit Test,UT)方面的知识,邀我对他所准备的PPT进行审阅。在审阅的过程中我发现,他在PPT中指出:“实际工作中,写好程序后对程序功能的调试就是一种单元测试”。由于我知道这位朋友并没有运用单元测试的经验,所以我问到:“你的这一认识是从哪里获得的?”,朋友答曰:“从网上搜来的”。无独有偶,这两天我在微博上看到了对单元测试相似的理解:“写好程序,编译完,跑一跑,看看写得对不对,这就是最简单的UT啊!”
对于我这混迹于软件行业超十载,且在工作中能经常娴熟运用单元测试的“老鸟”来说,看到这样的理解实在是hold不住了,觉得很有必要写一篇文章以正视听(希望后面的人能“搜”到这篇文章
)。
![](https://i-blog.csdnimg.cn/blog_migrate/6691b329171dd67238906f44888d4216.png)
之所以要做“以正视听”这件事,是因为我担心上面的解释让新人或没有单元测试经验的人产生一种幻觉 ——“哇,好赞诶,原来我每天都在做单元测试!”这种幻觉带的后果,是这些人不去做真正的单元测试,且别人在说单元测试如何如何时,他却不知所云。
如果要用不会产生争议的文字解释单元测试,我想我不一定能写得比维基百科上的好(点击这里,或许参考我的《专业嵌入式软件开发》一书更好),那请允许我从特点的角度对之加以解释。首先,单元测试一定要写测试代码。测试代码并不会最终成为软件产品的一部分,测试代码通过实现各种不同测试用例的方式,检查被测代码(最终成为软件产品的一部分)的行为是否如程序员所希望的那样。读者不难想象,测试用例中需要构建大量被测代码所需的参数、环境等(体力活
)。
![](https://i-blog.csdnimg.cn/blog_migrate/c8cb980b6922e32de0344b325f8163e9.png)
其次,为了简化单元测试程序中测试用例的编写,单元测试一定需要使用一定的测试框架,比如cxxtest、CppUnit、JUnit等等。当然,使用自己设计的测试框架也是一种选择。
再次,单元测试通常(我本来想写成“一定”,但又怕被指太绝对)需要通过获取代码覆盖报告这一形式以了解测试效果,来自开源社区的gcov和lcov相信被不少人所知。“代码覆盖”是一个不小的“炸弹”,乃至有的人会选择性地将其看成是“形式主义”。对于有这样反应的人,我相信是因为他们吃过了“代码覆盖”这“鸟”的苦头,因为那帮丫领导将百分百的代码覆盖率当成了考核目标,结果可想而知。
代码覆盖报告的真正作用,是帮助程序员了解被测代码的哪些“角落”没有“扫过”,从而指导测试用例的编写。如果一味地以百分百的代码覆盖率作为目标,这会让程序员承担巨大的工作压力。Parasoft(C++ Test工具的开发商)的一份研究报告中指出,覆盖率超过大约80%时,团队所承担的工作压力就很重了,这一报告可作为制定具体覆盖率的参考。
另外,即使以百分百的代码覆盖率为实施目标,很遗憾,还是达不到完全保证代码质量的目的,因为测试用例是可以“伪造”的。在软件行业,不论有多么好的软件开发方法,都依赖于程序员的专业精神,否则其效果将大打折扣,这一点对于单元测试也不例外。与其追求百分百的覆盖率,强调测试用例的质量更具意义(OMG,如何保证测试用例的质量?)。还有,千万不要将单元测试当作是“银弹”。
抱有“我每天都在做单元测试”幻想的程序员在幻想破灭以后,又会产生另一种错觉 —— “单元测试是测试人员的事”。我得对这类程序员说:“你不负责任”。程序员编写程序后验证其中没有任何缺陷是理所当然的事。什么?你没有遗留缺陷?怎么证明?单元测试!
当幻觉与错觉都远离我们以后,或许有的同仁会想:“单元测试就是一种测试!管它什么测试,目的都是为了保证软件质量的,那么在意其具体含义干吗呢?”不,精确掌握各词的具体含义是为了我们能更高效地沟通!想一想,我们还有集成测试、系统测试,为什么要造出这些新词?