使用PowerMock javaagent结合Jacoco时出现NoClassDefFoundError: MainMockTransformer
问题背景
我按照文章指导配置Jacoco以支持PowerMock类的覆盖率统计,在Eclipse中使用Eclemma时能正常看到正确的代码覆盖率,但团队用Sonar做大规模统计时,通过VM参数引入Jacoco agent、Ant任务跑单元测试生成的报告却无法正确统计这些类的覆盖率,我的VM参数配置如下:-Xmx1048m -Xms1048m -XX:PermSize=512M -XX:MaxPermSize=1024M -javaagent:<somepath>\powermock-module-javaagent-<version>.jar
核心原因
Eclemma本身基于Jacoco,但它的运行方式和Ant+Jacoco agent的方式存在差异:PowerMock通过字节码篡改实现mock,而Jacoco默认的字节码插桩顺序如果不对,就会导致PowerMock修改后的类无法被Jacoco正确统计。
解决方案步骤
调整VM参数的agent顺序
必须把Jacoco agent放在PowerMock agent之前,因为Jacoco需要先对类进行插桩,之后PowerMock再去修改这些类的字节码。正确的VM参数格式应该是:-Xmx1048m -Xms1048m -XX:PermSize=512M -XX:MaxPermSize=1024M -javaagent:<somepath>\jacocoagent.jar=destfile=jacoco.exec,append=false -javaagent:<somepath>\powermock-module-javaagent-<version>.jar注意:要确保Jacoco agent的
destfile参数指定了输出文件路径,Sonar需要读取这个.exec文件来生成报告。配置Ant任务的Jacoco报告生成
在Ant的build.xml中,确保Jacoco的报告任务正确引用测试类、源代码以及生成的jacoco.exec文件,示例配置如下:<taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml"> <classpath path="<somepath>\jacocoant.jar"/> </taskdef> <jacoco:report> <executiondata> <file file="jacoco.exec"/> </executiondata> <structure name="Your Project"> <classfiles> <fileset dir="bin/test-classes"/> <fileset dir="bin/main-classes"/> </classfiles> <sourcefiles encoding="UTF-8"> <fileset dir="src/main/java"/> <fileset dir="src/test/java"/> </sourcefiles> </structure> <html destdir="jacoco-report"/> <xml destfile="jacoco-report/jacoco.xml"/> <!-- Sonar需要这个XML报告 --> </jacoco:report>Sonar配置关联Jacoco报告
在Sonar的项目配置(或sonar-project.properties文件)中,添加以下参数指定Jacoco的报告路径:sonar.java.coveragePlugin=jacoco sonar.jacoco.reportPaths=jacoco-report/jacoco.xml sonar.jacoco.itReportPaths=jacoco-it.exec <!-- 如果有集成测试的话 -->排除PowerMock相关类(可选)
如果某些PowerMock的辅助类被错误统计,可以在Sonar配置中排除它们:sonar.exclusions=**/powermock/**,**/*Test*.class
验证步骤
- 运行Ant任务,确认
jacoco.exec和jacoco.xml文件正常生成 - 启动Sonar扫描,查看对应项目的覆盖率数据是否和Eclemma一致
内容的提问来源于stack exchange,提问作者ashish.g

