关于cobertura的介绍可参考我转载的几篇文章,这里就不做介绍了.这里我想介绍一下cobertura和中间件的结合使用。
我们的项目是一个J2EE的项目,涉及了spring,hibernate,struts,EJB。部署在websphere上,本地的开发环境是IBM RAD。项目组自己开发了一个基于JUnit的测试框架,可以通过JSP调用测试类得出测试结果。美中不足的是该框架得不到代码覆盖率。于是我们决定引入cobertura.
分析cobertura自带的example的bulid.xml我们可以将其分解成几个步骤:
1.编译源代码,该步骤其实IDE已经替我们完成了,不需要通过ant去编译,所以省去.
<target name="compile" depends="init"> <javac srcdir="${src.dir}" destdir="${classes.dir}" debug="yes"> <classpath refid="cobertura.classpath" /> </javac> </target>
2.instrument,在这里cobertura生成了instrument后的源代码。并生成cobertura.ser信息文件,该文件很重要,包含了需要被测试的类的信息。
<target name="instrument" depends="init,compile"> <!-- Remove the coverage data file and any old instrumentation. --> <delete file="cobertura.ser"/> <!--<delete dir="${instrumented.dir}" /> --> <!-- Instrument the application classes, writing the instrumented classes into ${build.instrumented.dir}. --> <cobertura-instrument todir="${instrumented.dir}"> <!-- The following line causes instrument to ignore any source line containing a reference to log4j, for the purposes of coverage reporting. --> <ignore regex="org.apache.log4j.*" /> <fileset dir="${classes.dir}"> <!-- Instrument all the application classes, but don't instrument the test classes. --> <include name="**/*.class" /> <exclude name="**/*Test.class" /> </fileset> </cobertura-instrument> </target>
3.test ,这步是生成详细代码覆盖信息的步骤,并生成JUnit的测试结果。这里需要用instrument后的*.class替代原来的*.class,当instrument后的*.class被执行时,它们会纪录被调用的信息,并写入之前生成的信息文件cobertura.ser。
<target name="test" depends="init,compile"> <junit fork="yes" dir="${basedir}" failureProperty="test.failed"> <!-- Note the classpath order: instrumented classes are before the original (uninstrumented) classes. This is important. --> <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" /> <formatter type="xml" /> <test name="${testcase}" todir="${reports.xml.dir}" if="testcase" /> <batchtest todir="${reports.xml.dir}" unless="testcase"> <fileset dir="${src.dir}"> <include name="**/*Test.java" /> </fileset> </batchtest> </junit> <junitreport todir="${reports.xml.dir}"> <fileset dir="${reports.xml.dir}"> <include name="TEST-*.xml" /> </fileset> <report format="frames" todir="${reports.html.dir}" /> </junitreport> </target>
4.生成代码覆盖率报告。
<target name="coverage-check"> <cobertura-check branchrate="34" totallinerate="100" /> </target> <target name="coverage-report"> <!-- Generate an XML file containing the coverage data using the "srcdir" attribute. --> <cobertura-report srcdir="${src.dir}" destdir="${coverage.xml.dir}" format="xml" /> </target> <target name="alternate-coverage-report"> <!-- Generate a series of HTML files containing the coverage data in a user-readable form using nested source filesets. --> <cobertura-report destdir="${coverage.html.dir}"> <fileset dir="${src.dir}"> <include name="**/*.java"/> </fileset> </cobertura-report> </target>
经过分析,这些步骤中只有第三步test部分执行有问题,因为没有websphere的环境,所以我决定将第三步分离,通过项目自带的JUnit框架来调用测试类。首先我执行instrument步骤,然后启动websphere服务器。在这里,正式服务器和RAD自带的websphere有所区别。正式的服务器需要用instrument后的*.class替换原来的,打包后重新发布。测试服务器上我采用先启动,然后替换发布后的*.class。然后我通过测试框架在jsp上执行测试类,此时,所有的信息并没有写入到相应的cobertura.ser文件中,只有当server停止时,系统才会将信息写入文件。因为在测试框架执行instrument后的class时,我们无法指定信息文件的位置,所以会在C:\RAD7\SDP70\runtimes\base_v61\profiles\serverName 文件夹下生成新的cobertura.ser文件,serverName为配置的server名称。然后再利用cobertura自带的merge功能将这两个cobertura.ser文件合并.
5.merge
<target name="merge" description="merge files"> <cobertura-merge> <fileset dir="${basedir}"> <include name="**/cobertura1.ser"/> <include name="**/cobertura22.ser"/> </fileset> </cobertura-merge> </target>
最后,我们再通过ant生成相应的代码覆盖率的报告.这样,我们就完成了cobertura和中间件的结合.
PS:各种中间件生成cobertura.ser的位置有所不同.但都可以通过这种方式使用cobertura.