公司最近要提高代码质量,让所有的项目从今往后必须要提高Unit Test的覆盖率到二成,测试才给测。这可让兄弟们到吸了一口凉气,我想这玩意还不把人给整死,郁闷了老半天。
还好,偶经高人指点,知道了Cobertura这个开源的工具。“Open source”作为一个程序员来说,,最喜欢听到的英文单词就是这两个了。Cobretura是一个基于JCoverage的分支开源项目,它的主要用途在于进行单元测试代码覆盖率的自动化测试。总的来说通过它,我们可以一定程度来了解我们代码的质量。一般来说单元测试覆盖率越高的代码越可靠。当然这不是绝对的,但是我相信有比没有好,就好比有计划比没计划好,即使计划烂到无法实施。
好了罗嗦了老半天就开始讲正题了。假如现在有个Java项目要进行单元测试代码的覆盖率的计算(由于Cobertura是基于java,所以这里讨论的仅为java项目)。
我们按部就班吧。
1. 下载http://cobertura.sourceforge.net/download.html,最新版是1.9.4.1。下载好了后找到如下几个jar包:cobertura.jar,asm-3.0.jar, asm-tree-3.0.jar, jakarta-oro-2.0.8.jar, log4j-1.2.9.jar,把它们拷贝到Java项目的根目下的lib/cobertura目录(如果没有lib和其子目cobertura就新建它们)。
2. 在项目中添加ant的build_Coverage.xml脚本和所用的属性文件build_Coverage.properties。内容如下:
build_Coverage.properties
build_Coverage.xml
添加好了后只要修改好参数,然后通过eclipse执行ant的脚本,等待其跑完即可。
在这里有几点要注意的:
一)参数的修改
- 在cobertura-instrument进行单元测试代码的指定,如,
<fileset dir="${classes.dir}">
- 在执行Junit的ant脚本部分指定单元测试代码的路径,如,
<batchtest todir="${reports.xml.dir}" unless="testcase">
<fileset dir="${src.test.dir}">
- 在report部分指定要统计覆盖率的java代码的位置,如,
<cobertura-report datafile="${reports.dir}/basic_coverage.ser" srcdir="${src.main.dir}" destdir="${coverage.xml.dir}" format="xml" />
- 指定cobertura计算coverage的时候要用的的ser文件
<sysproperty key="net.sourceforge.cobertura.datafile" file="${reports.dir}/basic_coverage.ser" />
二)如果你想要去掉几个不想进行统计的package的话,你可以如下操作
上面去掉了包名中有mock的和po的所有类,这样的话可以让你仅统计你关心的package。
三)下面的classpath引用的顺序不能乱,不然会出所有的line coverage为0的现象。
<junit fork="yes" failureProperty="test.failed" printsummary="on" haltonfailure="false">
<!--
Specify the name of the coverage data file to use.
-->
<sysproperty key="net.sourceforge.cobertura.datafile" file="${reports.dir}/basic_coverage.ser" />
<!--
Note the classpath order: instrumented classes are before the
original (uninstrumented) classes. This is important.
-->
<classpath location="src/main/resources"/>
<classpath location="${instrumented.dir}" />
<classpath location="${classes.dir}" />
<!--
The instrumented classes reference classes used by the
Cobertura runtime, so Cobertura and its dependencies
must be on your classpath.
-->
<classpath refid="cobertura_classpath" />
<classpath refid="run.classpath" />
四)这里再进一步对instrument进行一个说明,在执行junit test case之前必须生成cobertura的instrument类,这里些类是你要进行统计覆盖率的所有类的class文件,也就是如下这一段ant script
<cobertura-instrument datafile="${reports.dir}/basic_coverage.ser"todir="${instrumented.dir}" >
<!--
Thefollowing line causes instrument to ignore any
sourceline containing a reference to log4j, for the
purposesof coverage reporting.
-->
<ignoreregex="org.apache.log4j.*" />
<filesetdir="${classes.dir}">
<!--
Instrumentall the application classes, but
don'tinstrument the test classes.
-->
<includename="**/*.class" />
<excludename="**/mock/**/*.class"/>
<excludename="**/po/**/*.class"/>
<excludename="**/*Test.class" />
<excludename="**/Js*Run.class"/>
</fileset>
</cobertura-instrument>
这里你应该要明确一点,也就是生成到instrument.dir目录下的类的class文件与classes.dir下同名的class已经不再是原来的class, 实际上经过instrument这个过程后已经打个了cobretura的标记信息,从如下图可以看出(同一个类instrument后的内容与之前做的比较)
在后面真正要执行junit的时候,如果你不想看到你的覆盖率报表显示的是0,所用到的类必须先引用instrument的类。如下所示:
<!--
Note the classpath order: instrumented classes arebefore the
original (uninstrumented) classes. This is important.
-->
<classpath location="src/main/resources"/>
<classpath location="${instrumented.dir}" />
<classpath location="${classes.dir}" />
<!--
The instrumented classes reference classes used bythe
Cobertura runtime, so Cobertura and itsdependencies
must be on your classpath.
-->
<classpath refid="cobertura_classpath" />
<classpath refid="run.classpath" />
虽然后面classes.dir中包含有同名的class,但这个从报表结果上看来,在加载过instrument的类后,其原始类就不再加载了。所以你也完全可以把原始类从classes.dir中去掉再放到junit的classpath,这个过程是没有问题的。除此之外还有一个地方需要注意的是在执行junit的ant script时候,要设置一下fork=”true”,也就是启用一个新的jvm来跑所有的test case。当然你也可以把fork=”true”放在batchtest的属性中。表相上得知如果运行ant的jvm来执行test case的话会产生类的冲突。所以只能启用新的jvm来完成cobertura下的test case运行。