点击链接阅读原文,获取更多技术内容:

本文作者结合我们日常的工作,讨论如何使用好单元测试这把武器。

作者 | 胡春望(秋朔)


(资料图片仅供参考)

来源 | 阿里开发者公众号、

前言

如《Unit Testing》书里提到,

学习单元测试不应该仅仅停留在技术层面,比如你喜欢的测试框架,mocking 库等等,单元测试远远不止「写测试」这件事,你需要一直努力在单元测试中投入的时间回报最大化,尽量减少你在测试中投入的精力,并最大化测试提供的好处,实现这两点并不容易。

和我们在日常开发中遇到的问题一样,学会一门语言,掌握一种方法并不困难,困难的是把投入的时间回报最大化。unit test有很多基础知识和框架,在google上一搜就一大堆,最佳实践的方法论也非常多,本文不准备讨论这些问题,而是结合在我们日常的工作,讨论如何使用好单元测试这把武器。

单元测试的定义

什么是单元测试?来自百度

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。至于【单元】的含义,一般来说,要根据实际情况判定具体含义,如Java里单元指一个类等。

讲人话,单元测试就是为了验证一个类的准确性的测试。区别于集成测试和系统测试。他是 前置 的,由开发人员主导的 最小规模 的测试。

一些学者们经过统计,还绘制出了下图:

85%的缺陷都在代码设计阶段产生; 发现bug的阶段越靠后,耗费成本就越高,呈指数级别的增长。

由此看来,单测代码的编写对于交付质量以及人工耗费成本都有极其重要的影响

常见的误区

浪费时间,影响开发速度

不同项目的开发测试时间曲线不同,你要综合考虑你的代码的生命周期,你debug的能力,你平时花多少时间review有问题的代码。随着项目的进行,这些时间会递增,如果你想你所写的代码能够一直用下去,不让后来人吐槽这写的什么玩意,单元测试非常有必要。

测试应该是测试的工作

开发是代码的第一责任人,最熟悉代码的人,在设计阶段编辑单元测试,不但可以让你更自信的交付,还可以减少测试问题的产生。同时你自己的全栈能力也有所提升。

代码不是我写的,我不懂

我们经常抱怨老代码有坑难懂,或者是缺乏CR。其实在编写单元测试的过程中,也是CR和学习的一个过程,对于代码的主流程,边界,异常等有了深入的理解。同时也是自我审视代码规范、逻辑、设计的过程。我建议在重构中写单测,在写单测中重构,相辅相成。

如何写出好的单测

方法论上,有 AIR 原则 ,像空气一样不会被感受到即 Automatic (自动化)、 Independent (独立性)、 Repeatable (可重复)。

个人的理解 就是

1、自动运行,通过CI集成的方式,保证单测能够自动运行,通过assert保证单元测试的验证结果,而不是print输出。确保单元测试能够自动化运行,不需要人工介入测试。

2、单元测试必须独立,不能互相调用,也不能有依赖的顺序。每个测试用例之间包保证独立。

3、不可以受运行的环境、数据库、中间件等影响。在编写单测的时候,需要把外部的依赖mock掉。

从覆盖率的规范上来讲,不管是阿里内部还是业界,都有很多标准。

语句覆盖率达到70%;核心模块的语句覆盖率和分支覆盖率都要达到100%。 --- 《阿里巴巴Java开发手册》

单测覆盖度分级参考

Level1:正常流程可用,即一个函数在输入正确的参数时,会有正确的输出

Level2:异常流程可抛出逻辑异常,即输入参数有误时,不能抛出系统异常,而是用自己定义的逻辑异常通知上层调用代码其错误之处

Level3:极端情况和边界数据可用,对输入参数的边界情况也要单独测试,确保输出是正确有效的

Level4:所有分支、循环的逻辑走通,不能有任何流程是测试不到的

Level5:输出数据的所有字段验证,对有复杂数据结构的输出,确保每个字段都是正确的

从上面的摘录看,语句覆盖率和分支覆盖率都有数值上和方法论上的要求,那在实际工作中,实践情况如何呢?

笔者曾在一个季度,工作中提交的代码综合增量覆盖率几乎达到了100%。我可以谈谈我的经验和实践。

60%左右的单测覆盖率可以非常轻松达到,但达到95%以上的覆盖率,需要覆盖各种代码分支和异常情况等,甚至是配置和bean的初始化方法,所投入的时间非常巨大,但边际效应递减。我想测试toString, getter/setter这样的方法也没有意义。多少合适,我认为没有一个固定的标准。高代码覆盖率百分比不表示成功,也不意味着高代码质量。该舍弃测试的部分就大胆的ignore掉。

剩余60%,完整内容请点击下方链接查看:

阿里云开发者社区,千万开发者的选择。百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,尽在:

推荐内容