浅谈Jacoco原理
在上一篇文章中(戳我阅读)我们已经探讨了Jacoco与SonarQube结合后单元测试覆盖率的计算方式,那么这次我们来聊一聊Jacoco获取单元测试覆盖率的原理。 01 单元测试覆盖率工具 目前对于Java来说,主要有Jacoco、JCov、Clover、Cobertura、Serenity、EMMA等工具来获取单元覆盖率,简单介绍如下:- Jacoco:目前使用最广泛的开源代码覆盖率工具,它最大的特性是支持通过Java Agent的方式在线对字节码进行插桩,也支持通过离线的方式进行插桩。目前与SonarQube、Jenkins、IDEA、Gradle等工具都有插件集成。
- JCov:最早的代码覆盖率工具,从Sun JDK的Java 1.1版本就开始使用,到目前也支持离线插桩与通过Java Agent形式的在线插桩。
- Clover:是Atlassian(Jira与Confluence的那家公司)推出的商业版工具,与免费开源的Jacoco相比,功能自然更多。详细的功能介绍可查看参考文章。此外有开源的OpenClover供大众使用。
- Cobertura:也是一款使用者较多的覆盖率工具,与Jacoco相比它只有离线插桩模式,且扫描时间比Jacoco要慢一些。
- EMMA:Jacoco是其替代者,且不支持JDK8,目前使用者逐渐减少。


上图展示了代码覆盖率获取的方式,主要可以分为两种:第一种是运行时性能分析(Runtime Profiling),它又可以分为JVMPI(Java Virtual Machine Profiling Interface,JVM性能分析接口)与JVMTI(Java Virtual Machine Tool Interface,JVM工具接口,在JDK6中取代JVMPI与JVMDI,是Java平台调试器架构中最低的层级);第二种是插桩(Instrumentation),其中以插桩为主流。
在插桩方式中又可以分为源码插桩与字节码插桩。源码插桩会在编译前修改代码,在实践过程中就会有这样的问题——流水线编译报错的行数与开发人员看到的行数不一致。而在字节码插桩中,又可以分为On-The-Fly(在线)与Offline(离线)两种模式。在线模式可细分为类加载与代理,而离线模式可细分为替换与注入。在字节码插桩的模式中,Jacoco除了不支持离线替换外,其他都支持。
Jacoco与Cobertura都通过ASM框架来修改字节码,不仅可以修改class文件,还可以修改jar文件。

ASM是一个通用的Java字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。ASM提供了一些常见的字节码转换和分析算法,可以从中构建自定义复杂转换和代码分析工具。
03 插桩模式 这里详细介绍下On-The-Fly插桩与Offline插桩:- On-The-Fly
- Java Agent:通过在JVM中-javaagent参数指定特定的jar文件启动插桩代理程序,并在加载class时对其进行插桩。
- Class Loader:通过自定义类加载器在类加载前对class进行插桩。
- Offline:在编译时对文件进行插桩,生成插过桩的class文件或jar包。其中替代的方式是指插桩会生成新的class,注入的方式是指在原有class文件上进行修改。
- 不支持Java Agent的运行时环境;
- 无法配置JVM选项的部署;
- 字节码需要转换为另一个VM,如Android Dalvik VM;
- 修改字节码过程中与其他Agent冲突;

...
org.jacoco
jacoco-maven-plugin
0.8.4
default-instrument
instrument
default-restore-instrumented-classes
restore-instrumented-classes
report
prepare-package
report
target/coverage-reports/jacoco-unit.exec
org.apache.maven.plugins
maven-surefire-plugin
3.0.0-M3
target/coverage-reports/jacoco-unit.exec
...
0 5总结 这回主要和大家简单介绍了主流的代码覆盖率工具以及基本原理,然后针对在实践中出现的代码覆盖率为0的情况,介绍了如何修改Jacoco的插桩模式。那么相信在各种工具的辅助下,我们能够提升代码质量,不断减少缺陷的产生。
