这三种代码覆盖分析方法有什么区别?

19

这个声纳页面基本上列出了不同代码覆盖分析工具采用的各种方法:

  1. 源代码插装(由Clover使用)
  2. 离线字节码插装(由Cobertura使用)
  3. 即时字节码插装(由Jacoco使用)

这三种方法是什么,哪一种最有效,为什么?如果效率问题的答案是“取决于”,那么请解释一下为什么?

2个回答

16
源代码仪器化是在编译之前向源代码添加指令。这些指令用于跟踪哪些代码部分已被执行。
离线字节码仪器化是在编译后直接将相同的指令添加到字节码中。
在线字节码仪器化是在运行时,当JVM加载字节码时,动态地向字节码中添加相同的指令。 此页面对这些方法进行了比较。它可能存在偏见,因为它是Clover文档的一部分。
根据您对“高效”的定义,选择您最喜欢的方法。我认为你不会得到很大的差异。它们都能完成工作,无论使用哪种方法,大局将是相同的。

我想你需要一个指向Clover文档的链接? - sharakan
是的,我忘记添加链接了。现在已经加上了。感谢您发现问题。 - JB Nizet
1
目前,Cobertura支持Java 7和Java 8。 - Krzysztof Krasoń
但是使用仪器意味着我们需要建立一个包括仪器打印的构建,并且另外建立一个用于运营的构建(不包括打印)。因此,有人可能会说测试是在不同的构建上进行的,对吧? - ransh

3
一般而言,覆盖率的影响是相同的。
源代码插装可以提供更高级的报告结果,因为字节码插装无法区分源行内的任何结构,因为代码块粒度仅以源行记录。想象一下我有两个嵌套的if语句(或者等效地,在单个行中使用 if(a && b) ... *)。源代码插装程序可以看到这些内容,并为if内的多个分支提供与源行相关的覆盖信息;它可以基于行和列报告块。但是字节码插装器只看到一个包围条件的行。如果条件a执行但为false,它是否将该行报告为“已覆盖”?
您可能会认为这是一个罕见的情况(实际上可能是这样),因此没有太大用处。但是当您在其后遇到错误覆盖并出现场错误时,您可能会改变对其效用的看法。 这里有一个很好的例子和解释,说明了字节码覆盖率如何正确地获得switch语句的覆盖率,这是极其困难的。
源代码插装程序还可以实现更快的测试执行,因为它具有编译器帮助优化插装代码的优势。特别是,二进制插装器在循环内插入探针可能会被JIT编译器编译到循环内部。一个好的Java编译器将看到插装程序产生了一个循环不变结果,并将插装程序提出循环(JIT编译器理论上也可以这样做;问题在于它们是否真的这样做)。

实际上,像Cobertura和JaCoCo这样的工具不显示单行覆盖率信息的原因很简单,就是开发人员选择不实现它。在我自己的即时字节码插装工具(JMockit Coverage)中,这一点得到了实现,并且单独的行段(例如"if (a && b)"中)会在覆盖报告中按照其实际情况显示。 - Rogério
这就是所有工具的方式:“开发人员选择不实现(某些功能)”。很好,你有更多的热情。你如何区分行的部分?我认为类文件没有提供任何更细粒度的信息。 - Ira Baxter
我没有遇到任何困难,因为在字节码级别上没有进行任何优化/简化;Java编译器生成标准化且未经优化的字节码(javac和Eclipse编译器之间有一些细微差别),这就是(加上源代码)所有覆盖工具需要处理的。JIT优化不会干扰。我认为“其他用户”是错误的;如果源代码中有“x==null”的条件,则它将始终存在于字节码中。 - Rogério
但是使用仪器不意味着我们必须构建包括仪器打印的版本和另一个用于操作的版本(不包括打印)。因此,有人可能会说测试是在不同的版本上进行的,对吗? - ransh
@ransh:是的,他们可以“说”那样。精心设计的仪器不会影响程序的功能,除非程序中有明确检查该仪器是否存在以引起某些行为变化的内容,并且在合作工程过程中,人们可以合理地确定这种检查不存在,除非以奇怪的方式存在(例如,检查程序大小,由于仪器添加的代码量而改变)。... - Ira Baxter
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接