代码覆盖率
代码覆盖率指我们运行所有项目中的测试方法后,能够执行的语句和所有语句的比值。更具体一些,我们用行覆盖率举例,行覆盖率应该等于运行所有测试方法能够执行到的行数除以代码所有能够执行的行数。
客观看待代码覆盖率
简单的说,覆盖率是指我们代码在测试中能够被覆盖的程度。所以,覆盖率理论上来说越高越好,代码覆盖率高说明我们的每一段代码都经过了测试,得到了预期的答案。但我们也不应该太纠结于代码覆盖率的高低,客观地说:
-
并不是越高的代码覆盖率表示代码质量越好BUG越少
-
代码覆盖率高只能表示代码都被测试过,但是否可靠并不确定
-
虽然高的覆盖率并不一定是好代码,但覆盖率低很大程度上代码质量会有问题
-
没有覆盖的代码应该引起我们的重视,有存在问题的风险
覆盖率的分类
刚才我们一直笼统的说代码覆盖率,实际上,根据具体情况的不同代码覆盖率有很多不同的分类。
在介绍各类覆盖率之前我们再明确一下概念,覆盖率是我们自己编写的测试方法对自己的代码覆盖的程度,本身的操作者就是开发者自己,而不是其他的CI工具帮我们进行测试,CI工具能做的就是运行我们的测试方法,计算出覆盖率。我们根据他们得到的覆盖率继续查缺补漏,覆盖代码。
-
行覆盖率
-
也叫做:语句覆盖、段覆盖、基本块覆盖
-
行覆盖率是最基本的指标,表示是否代码中的每个可执行语句都被执行过
-
在有些情况下不可能做到行覆盖率100%,90%的覆盖率已经很优秀,一般我们要求80%的行覆盖率
-
-
分支覆盖率
-
也叫做:条件覆盖、判断覆盖、边界覆盖
-
分支覆盖使用一组测试参数来测试是否代码中所有的分支都能被测试到了
-
我们的测试方法要对分支进行不同参数的测试,一般情况下我们要求60%的分支覆盖率
-
-
路径覆盖率
-
也叫做:断言覆盖
-
之前的分支覆盖的问题在于,可能在不同的参数下能够达到不同的分支,但是不通的分支达到情况是否会带来问题这是分支覆盖不能测试出来的,如果有一种测试方法,对包括所有分支在内的所有的路径都能测试一遍就完美了,这就是路径覆盖
-
一般来说路径覆盖对于开发来说负担太大,一旦分支增多路径覆盖所需的测试样例指数增长,所以我们一般不会用路径覆盖做覆盖率的统计
-
-
变更行覆盖率
-
上一次发布代码后更新的代码的行覆盖率,这个数据可以方便的看出你新的代码是否做了测试
-
如何计算覆盖率
上图是一个方法执行的内部情况,这个方法开始时先执行一些代码,把这些执行的代码称作A,之后有一个if-else判断条件BC,如果是if就走B、else就走C,DE也是一样,最后这个方法还会执行一些代码,我们把这些代码当作F。
-
行覆盖率
-
统计行覆盖率的时候不关心是否有条件判断,我们只会跑完所有的测试方法,最后执行了几行除以总行数,得到一个行覆盖率
-
-
分支覆盖率
-
如果我们只是在测试样例中调用了一次这个方法,那么假如这一次方法的调用执行了B下一个条件执行了D,这样我们的分支覆盖率是50%。如果我们想提高分支覆盖了,就要在测试样例中多次调用方法传入不同参数,在多次调用中让代码走其他的分支,比如我们多调用了一次,这次代码走了C和D,这样我们调用两次,一次BD、一次CD,这样分支覆盖率就是75%。如果我们用多次调用把BCDE全部测试到了,那么分支覆盖率就是100%
-
-
路径覆盖率
-
路径覆盖率说起来很麻烦,其实很好理解。我们像这样一个问题,假如现在你在旅游,A到F是6个景点,你一共有多少种走法?所有的走法总和就是路径覆盖率的分母。这个方法分支比较少,我们可以穷举,一共有下面这些走法:ABDF、ABEF、ACDF、ACEF。如果我们在测试样例中走通上面四种中的一种,那么我们的路径覆盖率就是25%,以此类推。可想而知在分支很多的情况下,想做到很高的路径覆盖率是几乎不可能的,所以我们一般不会统计这种覆盖率。
-
代码覆盖率的工具
我们已经了解了什么是代码覆盖率,作为开发怎么样让自己写出高覆盖率的测试。那么现在我们在CI集成人员的角度来看,代码覆盖率是如何统计的,有哪些代码覆盖率的工具。现在比较常用的代码覆盖率工具有Jacoco和Cobertura。Emma也是一个不错的覆盖率统计工具,但是他不支持JDK8,我们暂且先不讨论。
这些代码覆盖率工具的工作流程大概是这样的:
-
在class文件在JVM中的某个瞬间进行插桩
-
执行测试用例的时候,将执行的轨迹Dump到内存中存放
-
数据处理器结合插桩的数据分析代码覆盖率
为什么我们说是在JVM中class文件某个瞬间进行插桩呢?是因为普遍我们有三类插桩模式:
-
On-The-Fly代理模式
-
通过设置JVM参数指定特定的jar执行代理程序,将探针插入class文件
-
Jacoco使用这种方式
-
-
On-The-Fly类加载器模式
-
通过自定义类加载器实现类类装载的时候插入探针
-
Emma使用这种方式
-
-
Offine模式
-
在class文件要加载到JXM之前直接进行插桩插入探针,修改文件
-
Cobertura使用这种方式
-
On-The-Fly和Offine比较:
-
On-The-Fly模式更加方便的获取代码覆盖率,无需提前进行字节码插桩,可以实时获取代码覆盖率信息
-
Offline模式适用于以下场景:
-
运行环境不支持java agent
-
部署环境不允许设置JVM参数
-
字节码需要被转换成其他虚拟机字节码,如Android Dalvik VM
-
动态修改字节码过程中和其他agent冲突
-
无法自定义用户加载类
-