我們的項目是一個J2EE的項目,涉及了spring,hibernate,struts,EJB。部署在websphere上,本地的開發環境是IBM RAD。項目組自己開發了一個基於JUnit的測試框架,可以通過JSP調用測試類得出測試結果。美中不足的是該框架得不到代碼覆蓋率。於是我們決定引入cobertura.
分析cobertura自帶的example的bulid.xml我們可以將其分解成幾個步驟:
1.編譯源代碼,該步驟其實IDE已經替我們完成了,不需要通過ant去編譯,所以省去.
Java代碼
<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信息文件,該文件很重要,包含了需要被測試的類的信息。
Java代碼
<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。
Java代碼
<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.生成代碼覆蓋率報告。
Java代碼
<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
Java代碼
<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.