Cobertura 统计单元测试覆盖率的机制:运行类,并在一个log文件中记录哪一行被执行,然后将源代码和log文件进行比对。
1. 简单的情况:单模块maven项目
项目结构
源代码 src/main/java/se/sigma/calculator/Calculator.java
package se.sigma.calculator;public class Calculator { public int nextFibonacci(int a, int b) { return a + b; }}
单元测试src/test/java/se/sigma/calculator/CalculatorTest.java
package se.sigma.calculator;import org.junit.Test;import static org.hamcrest.core.Is.is;import static org.junit.Assert.assertThat;public class CalculatorTest { @Test public void shouldCalculateFibonacci() { Calculator calculator = new Calculator(); int expected = 13; int actual = calculator.nextFibonacci(5, 8); assertThat(actual, is(expected)); }}
我们添加maven cobetura插件
pom.xml
<?xml version="1.0" encoding="UTF-8"?><project> <modelVersion>4.0.0</modelVersion> <groupId>se.thinkcode</groupId> <artifactId>one-module-example</artifactId> <version>1.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.5.1</version> <executions> <execution> <phase>process-classes</phase> <goals> <goal>cobertura</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies></project>
关键部分是cobertura-maven-plugin,它在项目的process-classes阶段被执行,生成的报告如下所示
上图显示覆盖率是100%,两行代码都被执行到。代码没有分支,复杂度是1。
点击进入package se.sigma.calculator里面会得到如下结果图:
点击进入 Calculator 这个类会得到:
cobetura不仅会记录每行是否被执行到,还会记录每行被执行的次数(hits)。
2. 复杂的情况:多模块maven项目
在多模块项目中,我们往往会把源代码放在一个模块,测试代码放在另一个模块(出于项目分解管理或配置文件的原因)。而maven的模块是一个执行完再执行另一个,导致cobetura的代码覆盖率统计出现问题。
现有多模块项目结构如下:
功能源代码放在product模块,单元测试代码放在test模块。java文件的内容和前面的一样。test模块添加了product模块的依赖,这样就可以import Calculator这个类。
根项目的pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?><project> <modelVersion>4.0.0</modelVersion> <groupId>se.thinkcode</groupId> <artifactId>multi-module-failing-example</artifactId> <version>1.0</version> <packaging>pom</packaging> <modules> <module>product</module> <module>test</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.5.1</version> <executions> <execution> <phase>post-integration-test</phase> <goals> <goal>cobertura</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies></project>
<modules>标签里包含它的两个模块 product 和test。同样加上了 cobertura-maven-plugin来生成测试覆盖率报告.
product模块的pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?><project> <modelVersion>4.0.0</modelVersion> <parent> <groupId>se.thinkcode</groupId> <artifactId>multi-module-failing-example</artifactId> <version>1.0</version> </parent> <groupId>se.thinkcode</groupId> <artifactId>calculator</artifactId> <version>1.0</version></project>
test模块的pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?><project> <modelVersion>4.0.0</modelVersion> <parent> <groupId>se.thinkcode</groupId> <artifactId>multi-module-failing-example</artifactId> <version>1.0</version> </parent> <groupId>se.thinkcode</groupId> <artifactId>calculator-test</artifactId> <version>1.0</version> <dependencies> <dependency> <groupId>se.thinkcode</groupId> <artifactId>calculator</artifactId> <version>1.0</version> </dependency> </dependencies></project>
依赖 product 的源代码,才能引用 Calculator并调用它的方法。
测试结果如下:
可以看到覆盖率是0,而我们知道正确的结果应该是100%。
点击进入package里面,显示Calculator.java没有被执行
点击进入Calculator.java里面,显示方法代码没有被执行
maven的项目结构是固定的,一个java模块生成一个target文件,各模块是隔离的,因此无法跨模块比对运行的测试类和源代码文件。这时我们想到另一种项目管理工具Ant。Ant与maven相比,它的灵活在于可以让用户自己指定源码的位置和编译后字节码的存放位置,如此又可以达到单模块maven项目的效果。
maven+Ant
在前面工程结构的基础上,我们在根目录加上Ant的 build.xml 文件。
multi-module-example|-- build.xml|-- pom.xml|-- product| |-- pom.xml| `-- src| `-- main| `-- java| `-- se| `-- sigma| `-- calculator| `-- Calculator.java`-- test |-- pom.xml `-- src `-- test `-- java `-- se `-- sigma `-- calculator `-- CalculatorTest.java
父项目根目录pom.xml:
<?xml version="1.0" encoding="UTF-8"?><project> <modelVersion>4.0.0</modelVersion> <groupId>se.thinkcode</groupId> <artifactId>multi-module-example</artifactId> <version>1.1</version> <packaging>pom</packaging> <modules> <module>product</module> <module>test</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-clean-plugin</artifactId> <version>2.4.1</version> <configuration> <filesets> <fileset> <directory>.</directory> <includes> <include>**/*.ser</include> </includes> </fileset> </filesets> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies></project>
maven-clean-plugin是用来清楚之前生成的target文件(.ser结尾的是cobetura生成的报告)。这里不用再添加maven-cobetura插件,cobertura需要单独下载,在Ant 脚本中引用。
Cobertura.下载地址 http://cobertura.sourceforge.net/download.html 。我这里用的是Cobertura 1.9.4.1下载后解压到 /Users/tsu/java/cobertura-1.9.4.1
,你可以修改成其他目录,但是要注意和 build.xml
中指定的路径一致。
Ant构建脚本build.xml:
<project> <target name="instrument"> <!-- Add all modules that should be included below --> <!-- <antcall target="instrumentAModule"> <param name="module" value="MODULE_NAME_TO_REPLACE"/> </antcall> --> <antcall target="instrumentAModule"> <param name="module" value="product"/> </antcall> </target> <target name="report" depends="merge"> <property name="src.dir" value="src/main/java/"/> <cobertura-report datafile="sum.ser" format="html" destdir="./target/report"> <!-- Add all modules that should be included below --> <!-- fileset dir="./MODULE_NAME_TO_REPLACE/${src.dir}"/ --> <fileset dir="./product/${src.dir}"/> </cobertura-report> </target> <target name="merge"> <cobertura-merge datafile="sum.ser"> <fileset dir="."> <include name="**/cobertura.ser"/> </fileset> </cobertura-merge> </target> <target name="instrumentAModule"> <property name="classes.dir" value="target/classes"/> <cobertura-instrument todir="./${module}/${classes.dir}"> <fileset dir="./${module}/target/classes"> <include name="**/*.class"/> </fileset> </cobertura-instrument> </target> <property environment="env"/> <property name="COBERTURA_HOME" value="/Users/tsu/java/cobertura-1.9.4.1"/> <property name="cobertura.dir" value="${COBERTURA_HOME}"/> <path id="cobertura.classpath"> <fileset dir="${cobertura.dir}"> <include name="cobertura.jar"/> <include name="lib/**/*.jar"/> </fileset> </path> <taskdef classpathref="cobertura.classpath" resource="tasks.properties"/></project>
两个重要的<targets>是:
instrument
用Cobertura instrumentation来执行编译类文件。
report
将执行的测试类和源代码组合起来,生成覆盖率报告。
product模块的pom.xml和之前一致 :
<?xml version="1.0" encoding="UTF-8"?><project> <modelVersion>4.0.0</modelVersion> <parent> <groupId>se.thinkcode</groupId> <artifactId>multi-module-example</artifactId> <version>1.1</version> </parent> <groupId>se.thinkcode</groupId> <artifactId>calculator</artifactId> <version>1.1</version></project>
test模块的pom.xml:
<?xml version="1.0" encoding="UTF-8"?><project> <modelVersion>4.0.0</modelVersion> <parent> <groupId>se.thinkcode</groupId> <artifactId>multi-module-example</artifactId> <version>1.1</version> </parent> <groupId>se.thinkcode</groupId> <artifactId>calculator-test</artifactId> <version>1.1</version> <dependencies> <dependency> <groupId>se.thinkcode</groupId> <artifactId>calculator</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>net.sourceforge.cobertura</groupId> <artifactId>cobertura</artifactId> <version>1.9.4.1</version> </dependency> </dependencies></project>
test 模块需要添加 Cobertura依赖,这样在test phase才能记录执行的踪迹。
项目的执行有以下四步:
- Compile all code
- Instrument the code
- Execute all tests
- Consolidate and build the report
即顺序执行下面的四条命令:
mvn clean compileant instrumentmvn testant report
生成的报告:
0 0