使用 Java Annotation 定制 Ant Junit Report

红薯 发布于 2011/02/15 09:18
阅读 1K+
收藏 5

本文介绍了扩展 Ant JUnit Report 的方法,通过扩展 Ant 的 XML 结果格式化器,利用 Java Annotation,在生成的 Ant JUnit Report 中关联测试案例相对应的缺陷(Defect)信息。

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. 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"/> 

 

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

  1. XSLT 文件内建于 Ant 的 optional.jar 文件中。将它解压缩到本地目录,然后修改 junit-frames.xsl 文件以便将 defect 信息引入报告。
  2. 创建一个自定义的 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. 包含 defect 信息的 Ant JUnit Report

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

小结

本文介绍了如何利用自定义的 Java Annotation, 并扩展 Ant 来实现在生成的测试结果报告关联 defect 信息。这是一个示例,读者可以根据本文中介绍的扩展 Ant 的方法,定制扩展 Ant 实现实际项目的需要。

 

本文代码下载

加载中
返回顶部
顶部