这次还是配置问题,接上上次关于ant脚本模板的详细说明。对于一个完整的项目测试报告,一般来说我们会用JUnit生成的report来分析关于测试用例执行情况,但是,我们怎么样保证我们的测试用例的测试质量呢,我们如何知道我们的测试用例到底覆盖了多少我们的工程代码呢。Cobertura就是一个很好的开源免费插件,他不仅仅支持ant,而且对maven的支持也有很不错的表现。对于Cobertura对Maven的支持我会在下一个专题中专门阐述(官方提供的Maven plug-in有些小bug,别走开,下一专题告诉你,呵呵呵呵),我这篇文章只针对Cobertura在ant中的使用做一个说明。
在上一个关于ant脚本的专题中,我详细说明了如何用ant来完成mail→mkzip→report→junit→build→prepare→clean等一系列的工作,为了保证项目中能够使用Cobertura,我这次对上一个专题的ant模板进行适当的修改:mail→mkzip→coverage-report→cover-test→instrument→report→junit→build→prepare→clean
我增加了coverage-report(生成测试报告)、cover-test(进行覆盖率测试)和instrument(生成打过标签的二进制classes)等三个target。同时对mkzip和mail的target也进行了适当的修改,以便能够把进行覆盖测试的report进行打包发邮件。
首先定义一个顶级 taskdef 元素将 cobertura.jar 文件限定在当前工作目录中:
- <taskdef classpath="cobertura.jar" resource="tasks.properties" />
接下来需要定义一个instrument任务,该任务将在已经编译好的类文件中添加日志代码-打上标签。todir 属性指定将测量类放到什么地方。fileset 子元素指定测量哪些 .class 文件
- <target name="instrument" depends="report">
- <cobertura-instrument todir="${instrumented.dir}">
- <fileset dir="${pro.build.path}">
- <include name="**/*.class" />
- <exclude name="**/*Test.class" />
- </fileset>
- </cobertura-instrument>
- </target>
接下来就可以进行覆盖测试了,定义一个cover-test任务,该任务依赖于instrument任务。要注意的一点就是被测量的类必须在原始类出现在类路径中之前出现在类路径中,而且需要将 Cobertura JAR 文件添加到类路径中。
- <target name="cover-test" depends="instrument">
- <mkdir dir="${testreportdir}" />
- <junit dir="./" failureproperty="test.failure" printSummary="yes" fork="true" haltοnerrοr="true">
- <classpath location="cobertura.jar" />
- <classpath location="instrumented" />
- <classpath>
- <fileset dir="lib">
- <include name="**/*.jar" />
- </fileset>
- <pathelement path="${pro.build.path}" />
- <pathelement path="${pro.build.path}" />
- </classpath>
- <batchtest todir="${pro.build.path}">
- <fileset dir="src/main/test">
- <include name="**/*Test.java" />
- </fileset>
- </batchtest>
- </junit>
- </target>
生成测试报告,为了能够使ant支持cobertura-report,同上个专题说到的一样,我们需要将Cobertura的相关资源文件在${ANT_HOME}/lib下放一份(cobertura-report不是ant内置标签):
- <target name="coverage-report" depends="cover-test">
- <cobertura-report srcdir="src/main/java" destdir="instrumented" />
- </target>
修改打包target
- <target name="mkzip" depends="coverage-report">
- <jar destfile="report/html/test-result${ant.project.name}.zip">
- <fileset dir="report/html">
- <include name="**/*.html" />
- <include name="**/*.css" />
- <include name="**/*.txt" />
- </fileset>
- </jar>
- <jar destfile="report/html/cover-test-result${ant.project.name}.zip">
- <fileset dir="instrumented">
- <include name="**/*.html" />
- <include name="**/*.css" />
- <include name="**/*.txt" />
- <include name="**/*.png" />
- <include name="**/*.js" />
- </fileset>
- </jar>
- </target>
修改发邮件target
- <target name="mail" depends="mkzip">
- <mail mailhost="smtp.126.com" mailport="25" subject="The Build Test" user="用户名" password="邮箱密码">
- <from address="你的发信地址" name="Danlley Wei" />
- <fileset dir="report/html">
- <include name="**/test-result${ant.project.name}.zip" />
- <include name="**/cover-test-result${ant.project.name}.zip" />
- </fileset>
- <to address="收信人地址" name="Danlley Wei" />
- <message>The ${pro.name}--${pro.author} has been tested ! </message>
- </mail>
- </target>
最后执行一下看看结果。
本专题完整模板
- <?xml version="1.0"?>
- <project name="springproj" basedir="." default="mail">
- <!--<property file="build.properties" /> -->
- <property name="pro.name" value="springproj" />
- <property name="pro.author" value="Danlley Wei" />
- <property name="src.dir" value="src/main/java" />
- <property name="pro.web.root" value="war" />
- <property name="pro.web.source" value="${pro.web.root}/WEB-INF" />
- <property name="pro.build.path" value="${pro.web.source}/classes" />
- <property name="user.dir" value="${pro.build.path}" />
- <property name="instrumented.dir" value="instrumented" />
- <taskdef classpathref="master-classpath" resource="tasks.properties" />
- <taskdef classpath="cobertura.jar" resource="tasks.properties" />
- <target name="mail" depends="mkzip">
- <mail mailhost="smtp.126.com" mailport="25" subject="The Build Test" user="邮箱用户名" password="邮箱密码">
- <from address="发信地址" name="Danlley Wei" />
- <fileset dir="report/html">
- <include name="**/test-result${ant.project.name}.zip" />
- <include name="**/cover-test-result${ant.project.name}.zip" />
- </fileset>
- <to address="收信地址" name="Danlley Wei" />
- <message>The ${pro.name}--${pro.author} has been tested ! </message>
- </mail>
- </target>
- <target name="mkzip" depends="coverage-report">
- <jar destfile="report/html/test-result${ant.project.name}.zip">
- <fileset dir="report/html">
- <include name="**/*.html" />
- <include name="**/*.css" />
- <include name="**/*.txt" />
- </fileset>
- </jar>
- <jar destfile="report/html/cover-test-result${ant.project.name}.zip">
- <fileset dir="instrumented">
- <include name="**/*.html" />
- <include name="**/*.css" />
- <include name="**/*.txt" />
- <include name="**/*.png" />
- <include name="**/*.js" />
- </fileset>
- </jar>
- </target>
- <target name="coverage-report" depends="cover-test">
- <cobertura-report srcdir="src/main/java" destdir="instrumented" />
- </target>
- <target name="cover-test" depends="instrument">
- <mkdir dir="${testreportdir}" />
- <junit dir="./" failureproperty="test.failure" printSummary="yes" fork="true" haltοnerrοr="true">
- <classpath location="cobertura.jar" />
- <classpath location="instrumented" />
- <classpath>
- <fileset dir="lib">
- <include name="**/*.jar" />
- </fileset>
- <pathelement path="${pro.build.path}" />
- <pathelement path="${pro.build.path}" />
- </classpath>
- <batchtest todir="${pro.build.path}">
- <fileset dir="src/main/test">
- <include name="**/*Test.java" />
- </fileset>
- </batchtest>
- </junit>
- </target>
- <target name="instrument" depends="report">
- <cobertura-instrument todir="${instrumented.dir}">
- <fileset dir="${pro.build.path}">
- <include name="**/*.class" />
- <exclude name="**/*Test.class" />
- </fileset>
- </cobertura-instrument>
- </target>
- <target name="report" depends="junit">
- <junitreport id="myJUnitReport" taskname="reported" todir="report" description="Junit Report">
- <fileset dir="report">
- <include name="TEST-*.xml" />
- </fileset>
- <report todir="report/html" />
- </junitreport>
- </target>
- <target name="junit" depends="build">
- <mkdir dir="report/html" />
- <junit printsummary="yes" haltοnerrοr="yes" haltonfailure="yes" fork="yes">
- <classpath location="${build.instrumented.dir}" />
- <formatter type="plain" usefile="false" />
- <formatter type="xml" />
- <test name="org.danlley.hibernate.dao.DeptDAOImplTest" todir="report" />
- <classpath refid="master-classpath" />
- </junit>
- </target>
- <target name="build" depends="prepare">
- <javac destdir="${pro.build.path}" target="1.5" debug="true">
- <src path="src/main/java" />
- <classpath refid="master-classpath" />
- </javac>
- <javac destdir="${pro.build.path}" target="1.5">
- <src path="src/main/test" />
- <classpath refid="master-classpath" />
- </javac>
- </target>
- <target name="prepare" depends="clean">
- <copy todir="${pro.build.path}">
- <fileset dir="${src.dir}">
- <include name="**/*.properties" />
- <include name="**/*.xml" />
- </fileset>
- </copy>
- </target>
- <target name="clean">
- <delete>
- <fileset dir="${pro.build.path}">
- <include name="**/*.*" />
- </fileset>
- <fileset dir="report">
- <include name="**/*.*" />
- </fileset>
- <fileset dir="instrumented">
- <include name="**/*.*" />
- </fileset>
- </delete>
- </target>
- <path id="master-classpath">
- <fileset dir="lib">
- <include name="*.jar" />
- </fileset>
- <pathelement path="${pro.build.path}" />
- </path>
- </project>
Cobertura 是敏捷程序员工具箱中新增的一个重要工具。通过生成代码覆盖率的具体数值,Cobertura 将单元测试从一种艺术转变为一门科学。它可以寻找测试覆盖中的空隙,直接找到 bug。测量代码覆盖率使您可以获得寻找并修复 bug 所需的信息,从而开发出对每个人来说都更健壮的软件。
尽管测试先行编程(test-first programming)和单元测试已不能算是新概念,但测试驱动的开发仍然是过去 10 年中最重要的编程创新。最好的一些编程人员在过去半个世纪中一直在使用这些技术,不过,只是在最近几年,这些技术才被广泛地视为在时间及成本预算内开发健壮的无缺陷软件的关键所在。但是,测试驱动的开发不能超过测试所能达到的程度。测试改进了代码质量,但这也只是针对实际测试到的那部分代码而言的。您需要有一个工具告诉您程序的哪些部分没有测试到,这样就可以针对这些部分编写测试代码并找出更多 bug。 Mark Doliner 的 Cobertura (cobertura 在西班牙语是覆盖的意思)是完成这项任务的一个免费 GPL 工具。Cobertura 通过用额外的语句记录在执行测试包时,哪些行被测试到、哪些行没有被测试到,通过这种方式来度量字节码,以便对测试进行监视。然后它生成一个 HTML 或者 XML 格式的报告,指出代码中的哪些包、哪些类、哪些方法和哪些行没有测试到。可以针对这些特定的区域编写更多的测试代码,以发现所有隐藏的 bug。