Maven项目多线程单元测试下Cobertura覆盖率数据异常问题
Maven多线程测试下Cobertura覆盖率不准的问题解决
问题根源
Cobertura 2.7本身不支持多线程/多进程测试场景的覆盖率数据合并。当你用Surefire开启多线程并行测试时,多个测试进程会同时往同一个cobertura.ser数据文件写数据,直接导致文件写入冲突、数据互相覆盖,所以每次跑出来的覆盖率都不准,结果还不一致。
为啥简易项目复现不了?因为测试用例少,执行速度快,进程间写文件的冲突概率几乎为0,自然看不出问题;单线程跑的时候只有一个进程写文件,数据自然准确一致。
解决办法
方法1:给每个测试进程分配独立的覆盖率文件,事后合并
修改Surefire和Cobertura的配置,让每个fork出来的测试进程生成单独的覆盖率文件,最后再合并这些文件:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M5</version> <configuration> <forkCount>4</forkCount> <!-- 直接指定固定数量,比1C的动态适配更可控 --> <reuseForks>false</reuseForks> <!-- 必须关闭进程复用,每个测试启动新进程 --> <parallel>classesAndMethods</parallel> <threadCount>4</threadCount> <trimStackTrace>true</trimStackTrace> <!-- 给每个fork进程指定唯一的覆盖率数据文件路径 --> <argLine>-Dfile.encoding=UTF-8 -Dnet.sourceforge.cobertura.datafile=${project.build.directory}/cobertura-${surefire.forkNumber}.ser</argLine> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.7</version> <configuration> <formats> <format>xml</format> </formats> <outputDirectory>${project.reporting.outputDirectory}/cobertura</outputDirectory> <encoding>UTF-8</encoding> <aggregate>true</aggregate> <!-- 指定要合并的所有覆盖率文件 --> <dataFiles> <dataFile>${project.build.directory}/cobertura-*.ser</dataFile> </dataFiles> </configuration> <!-- 把merge目标绑定到测试阶段之后,自动合并文件 --> <executions> <execution> <phase>test</phase> <goals> <goal>merge</goal> </goals> </execution> </executions> </plugin>
方法2:换用JaCoCo(推荐)
Cobertura 2.x已经停更很久了,JaCoCo是现在主流的覆盖率工具,原生支持多线程/多进程的覆盖率收集,配置也更简单:
<!-- 删掉原来的cobertura插件,换成JaCoCo --> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.11</version> <executions> <execution> <id>jacoco-init</id> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>jacoco-report</id> <phase>test</phase> <goals> <goal>report</goal> </goals> <configuration> <outputDirectory>${project.reporting.outputDirectory}/jacoco</outputDirectory> <formats> <format>xml</format> </formats> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M5</version> <configuration> <forkCount>1C</forkCount> <reuseForks>true</reuseForks> <parallel>classesAndMethods</parallel> <threadCount>4</threadCount> <trimStackTrace>true</trimStackTrace> <!-- 把JaCoCo的代理参数加到argLine里 --> <argLine>-Dfile.encoding=UTF-8 ${jacoco.agent.argLine}</argLine> </configuration> </plugin>
方法3:临时单线程执行(不推荐)
如果暂时不想改工具或配置,只能关掉多线程,牺牲测试速度换准确率:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M5</version> <configuration> <forkCount>1</forkCount> <reuseForks>true</reuseForks> <!-- 删掉parallel和threadCount这两个多线程配置 --> <trimStackTrace>true</trimStackTrace> <argLine>-Dfile.encoding=UTF-8</argLine> </configuration> </plugin>
补充说明
业务项目测试用例多、执行时间长,多进程写文件的冲突概率比简易项目高太多,所以只有业务项目会出问题。而且Cobertura 2.7的官方文档里明确说了不支持多进程并行测试的覆盖率收集,这是工具本身的硬伤。
内容的提问来源于stack exchange,提问作者Mitch Leo
相关产品推荐
相关产品推荐

