您的位置:首页 > 编程语言 > Java开发

使用 Java Annotation 定制 Ant Junit Report

2011-02-15 19:29 393 查看
JUnit 和 Ant

JUnit 是一个开源的 Java 测试框架,可以帮助开发人员简化测试案例的编写,已成为 Java 社区中知名度很高的单元测试工具。

Apache Ant 是一个基于 Java 的构建工具,它凭借出色的易用性、平台无关性以及对项目自动测试和自动部署的支持,成为众多项目构建过程中不可或缺的工具,并已经成为事实上的标准。大多数现代 Java IDE(包括 Eclipse)都支持 Ant 构建文件的开发,也都支持在 IDE 内部运行这些文件;而 Ant 也可以独立于任何 IDE 运行。

Ant 内置了对 JUnit 的支持,它提供了两个 Task:junit 和 junitreport,分别用于执行 JUnit 单元测试和生成测试结果报告。测试人员使用生成的测试结果报告,可以很方便地查看并分析测试结果。下图为 Ant 生成的 HTML 格式的测试结果报告。

图 1. Ant 生成的 HTML 格式的测试结果报告



(查看图 1 的 清晰版本。)

回页首

在测试结果报告中缺了什么?

在生成的测试结果报告中,可以看到每个测试套件的软件包和类名、测试案例的故障和错误数,以及测试套件的执行时间。对于每个测试套件,可以看到如下信息:

测试案例的名称

测试结果

故障或错误的类型(如果适用)

任何故障或错误的详细信息

执行的持续时间

但是,我们无法在上述测试结果报告中看到测试案例相对应的 Defect(缺陷)信息。这个信息在用户查看并分析测试结果报告的时候至关重要:

对于成功的测试案例,用户可以看到它的 defect 历史信息;

对于失败的测试案例,用户可以很快知道这个失败的测试案例有没有对应的 defect;

对于新失败的测试案例,用户可以方便知道是不是产生了 regression defect;

回页首

扩展 Ant JUnit Report

为了在测试结果报告中添加测试案例相对应的 defect 信息,下面将介绍如何利用 Java Annotation,并扩展 Ant 来实现这个目标。

使用 Annotation 定义 defect 信息

首先,需要定义一个 Annotation 表示 defect,并给测试案例加上这个 annotation。

清单 1. 定义一个 annotation: Defect

import java.lang.annotation.Target;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @Interface Defect {
public String value();
}

定义好了 Defect annotation 后,用 defect annotation 给测试案例加上相对应的 defect(如果有的话)。

清单 2. 给测试案例加上 defect annotation

@Defect(“DDDL7Z76CX”)
public void testRenameDocumentChangingExtension() throws Exception
{
//test logic here
}

扩展 XML 结果格式化器

Ant 的最大优势之一是它的可扩展性。对于使用带有 XML 格式化器( <formatter type="xml"/> )的 <junit> 任务运行的每个测试类,都创建了一个 XML 文件。

为了捕获测试案例对应的用 annotation 定义的 defect 信息,我们需要扩展 Ant 的 JUnit 相关类中的 XMLJUnitResultFormatter 来添加 defect 信息。 XMLJUnitResultFormatter 类中添加的部分用 /*-- ADDED --*/ 标明。

清单 3. XMLJUnitResultFormatter 中的修改部分

/**
* Interface TestListener.
*
* A Test is finished.
* @param test the test.
*/
public void endTest(Test test) {
if (!testStarts.containsKey(test)) {
startTest(test);
}

Element currentTest = null;
if (!failedTests.containsKey(test)) {
currentTest = doc.createElement(TESTCASE);
String n = JUnitVersionHelper.getTestCaseName(test);
currentTest.setAttribute(ATTR_NAME,
n == null ? UNKNOWN : n);
// a TestSuite can contain Tests from multiple classes,
// even tests with the same name - disambiguate them.
currentTest.setAttribute(ATTR_CLASSNAME,
JUnitVersionHelper.getTestCaseClassName(test));
/*-- ADDED --*/
// 获取defect信息
Method method = null;
try
{
method = test.getClass().getMethod(n, new Class [0]);
}
catch (SecurityException e)
{
e.printStackTrace();
}
catch (NoSuchMethodException e)
{
e.printStackTrace();
}

boolean hasDefect = method.isAnnotationPresent(DefectAnnotation.Defect.class);
//若有defect信息,添加到生成的XML文件
if (hasDefect)
{
Defect defectAnnotation = (Defect)method.
getAnnotation(DefectAnnotation.Defect.class);
String defectNumbers = defectAnnotation.value();
currentTest.setAttribute("defects",defectNumbers);
}
/*-- END ADDED --*/
rootElement.appendChild(currentTest);
testElements.put(test, currentTest);

} else {
currentTest = (Element) testElements.get(test);
}

Long l = (Long) testStarts.get(test);
currentTest.setAttribute(ATTR_TIME,
"" + ((System.currentTimeMillis()
- l.longValue()) / ONE_SECOND));

}

作为附带的优点,当扩展捕获的数据时,最终捕获的不仅是在测试套件运行时特定状态的信息,而且还包括了用户自定义的属性。这样,在生成的 XML 文件中就会包含测试案例对应的 defect 信息。

使用自定义的 XSLT 在生成的测试结果报告中显示 defect

junitreport task 使用 XSLT 把 junit task 生成的 xml 文件生成 HTML 格式的测试结果报告。<junitreport> 可被轻松扩展,允许用户自定义的 XSLT 文件用作报告生成。我们可以在 <junitreport> 中嵌套的 <report> 标签中用“styledir”属性指定用户自定义的 XLST 文件所在的目录。

<!-- 使用 reportstyle/junit-frames.xsl 生成测试报告 -->
<report styledir="reportstyle" format="frames" todir="testreport"/>

定制测试报告,有两种方法:

XSLT 文件内建于 Ant 的 optional.jar 文件中。将它解压缩到本地目录,然后修改 junit-frames.xsl 文件以便将 defect 信息引入报告。

创建一个自定义的 XSLT 文件,然后在 <junitreport> 中嵌套的 <report> 标签中用“styledir”属性指定用户自定义的 XLST 文件所在的目录。对于一些小的修改,创建一个自定义的 XSLT 文件,引入默认的 XSLT, 并且覆盖需要定制的 templates 是一个不错的方法。例如,在每一个测试案例中加一列(defect 信息), 我们就只需要重载产生表头和表行的 template 就可以了。

在下面的清单中,是重载产生表头和表行来加 defect 的例子(添加的部分用 <!-- ADDED --> 标明)。

清单 4. 自定义的 XSLT

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!-- import the default stylesheet -->
<xsl:import href="jar:file:lib/ant-junit.jar!/org/apache/tools/ant/
taskdefs/optional/junit/xsl/junit-frames.xsl"/>
<!-- override the template producing the test table header -->
<!-- method header -->
<xsl:template name="testcase.test.header">
<xsl:param name="show.class" select="''"/>
<tr valign="top">
<xsl:if test="boolean($show.class)">
<th>Class</th>
</xsl:if>
<th>Name</th>
<th>Status</th>
<th width="80%">Type</th>
<!-- ADDED -->
<th width="80%">Defects</th>
<th nowrap="nowrap">Time(s)</th>
</tr>
</xsl:template>
<!-- override the template producing a test table row -->
<xsl:template match="testcase" mode="print.test">
<xsl:param name="show.class" select="''"/>
<tr valign="top">
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="error">Error</xsl:when>
<xsl:when test="failure">Failure</xsl:when>
<xsl:otherwise>TableRowColor</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:variable name="class.href">
<xsl:value-of select="concat(translate(../@package,'.','/'),
'/', ../@id, '_', ../@name, '.html')"/>
</xsl:variable>
<xsl:if test="boolean($show.class)">
<td>
<a href="{$class.href}">
<xsl:value-of select="../@name"/>
</a>
</td>
</xsl:if>
<td>
<a name="{@name}"/>
<xsl:choose>
<xsl:when test="boolean($show.class)">
<a href="{concat($class.href, '#', @name)}">
<xsl:value-of select="@name"/>
</a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@name"/>
</xsl:otherwise>
</xsl:choose>
</td>
<xsl:choose>
<xsl:when test="failure">
<td>Failure</td>
<td>
<xsl:apply-templates select="failure"/>
</td>
</xsl:when>
<xsl:when test="error">
<td>Error</td>
<td>
<xsl:apply-templates select="error"/>
</td>
</xsl:when>
<xsl:otherwise>
<td>Success</td>
<td></td>
</xsl:otherwise>
</xsl:choose>
<td>
<!-- ADDED -->
<xsl:call-template name="display-defects">
</xsl:call-template>
</td>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="@time"/>
</xsl:call-template>

<!-- ADDED -->
<xsl:template name="display-defects">
<xsl:choose>
<xsl:when test="not(@defects)">N/A</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@defects"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

部署

以上整个工作只涉及到修改一个现有的类 (XMLJunitResultFormatter.java),添加一个新的类 (DefectAnnotation.java) 并创建一个自定义的 XSLT 文件,之后就可以开始部署的工作(这些文件可以在下载的 zip 包中看到)。将修改之后的类文件编译好,连同自定义的 XSLT 文件打包到 Ant-Junit.jar 中去替代原有的 class 就可以了。

为了使用这个新的 Ant-Junit.jar,可以拷贝新的 Ant-Junit.jar 到 Ant\lib 目录中,也可以在运行 Ant 的时候,用 -lib 参数来指定你的新 Ant-Junit.jar 。这样,就可以在生成的测试结果报告中看到测试案例对应的 defect 信息了。

包含 defect 信息的测试结果报告如下。

图 2. 包含 defect 信息的 Ant JUnit Report



(查看图 2 的 清晰版本。)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: