Hudson 支持的 JUnit XML 格式规范是什么?

211

我使用Hudson作为持续集成服务器,想要使用“发布JUnit测试结果报告”的选项。但是我没有使用xUnit工具进行测试,而是使用运行测试并以简单格式返回结果的shell脚本。我正在考虑编写一个将这些结果转换为JUnit格式的脚本。因此,我想知道JUnit文件应该是什么样子?


有什么理由不使用JUnit?这些测试可以通过多种工具进行自动化,例如命令行、UI等。 - Aaron McIver
6
Shell脚本非常适合在非Java语言上运行测试。您如何使用JUnit进行这种操作? - Ben Voigt
1
@BenVoigt 我最初认为 OP(原帖作者)涉及到 Java,并且试图绕过 JUnit 作为测试工具。但在查看问题后,这很可能不是情况。经过第二次查看,似乎 http://code.google.com/p/shell2junit/ 可以为 OP 提供一些用处。 - Aaron McIver
1
类似于shell2unit,这是我创建的一个JAXB类,可以解析/输出JUnit XML:https://gist.github.com/agentgt/8583649 - Adam Gent
8个回答

142

我几个月前也做了类似的事情,结果发现这种简单的格式足以让Hudson接受它作为测试协议:

<testsuite tests="3">
    <testcase classname="foo1" name="ASuccessfulTest"/>
    <testcase classname="foo2" name="AnotherSuccessfulTest"/>
    <testcase classname="foo3" name="AFailingTest">
        <failure type="NotEnoughFoo"> details about failure </failure>
    </testcase>
</testsuite>

这个问题有更详细的答案:JUnit XML 输出规范


12
我遇到了相反的问题。class被拒绝了,只有classname可用。 - ryanbrainard
1
当我将xUnit插件升级到1.60时,这个开始出现问题。我发现验证器变得更加严格了,我不得不添加<testsuite tests="(number of tests)">,例如<testsuite tests="10"> - Kevin Brotcke
2
谢谢@KevinBrotcke,我会更新答案以包括该属性。 - Anders Lindahl
3
请注意,要让Hudson按包/套件组织您的测试,您必须在classname属性中指定一个包。例如:<testcase classname="foo.bar" name="ATest" /> 这将在Jenkins上将bar类放入foo包中,使您的测试集合更加有组织。 - jluzwick
这个问题是关于Hudson/Jenkins理解的标准测试报告解析器。这个解析器是为JUnit设计的。有一个单独的插件用于解析xUnit格式的报告。xUnit和JUnit是两种不同的格式。 - Alex Skrypnyk
显示剩余4条评论

102

我刚刚下载了其他人提供的junit-4.xsd文件,并使用名为XMLSpear的工具将模式转换成带有下面选项的空白XML文件。这是(稍微清理过的)结果:

<?xml version="1.0" encoding="UTF-8"?>
<testsuites disabled="" errors="" failures="" name="" tests="" time="">
    <testsuite disabled="" errors="" failures="" hostname="" id=""
               name="" package="" skipped="" tests="" time="" timestamp="">
        <properties>
            <property name="" value=""/>
        </properties>
        <testcase assertions="" classname="" name="" status="" time="">
            <skipped/>
            <error message="" type=""/>
            <failure message="" type=""/>
            <system-out/>
            <system-err/>
        </testcase>
        <system-out/>
        <system-err/>
    </testsuite>
</testsuites>

其中一些项目可能会出现多次:

  • testsuites元素只能有一个,因为这是XML的工作原理,但是在testsuites元素内可以有多个testsuite元素。
  • 每个properties元素可以有多个property子元素。
  • 每个testsuite元素可以有多个testcase子元素。
  • 每个testcase元素可以有多个errorfailuresystem-outsystem-err子元素。

XMLSpear options


1
是否有一份文档描述了某些属性的有效值,例如测试用例的状态或错误类型? - Eric Cope
1
@EricCope 我可以建议你查看源代码http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java?view=markup。基本上它只是一个字符串。 - Sulthan
4
为什么标签会出现重复? - Nakilon
设置镜像:http://imgur.com/quneFJf,备选项:“Rootelement”:“testsuites”,“最大递归深度”:“2”,“最大重复因子”:“2”,“包括可选元素”:(是=打勾),“包括可选属性”:(是=打勾) - n611x007
1
@Nakilon,虽然晚了2.5年,但我已经修复了它。 - bdesham
显示剩余3条评论

67

这个问题的Anders Lindahl最佳答案提到了一个xsd文件

我个人认为这个xsd文件也很有用(我不记得是怎么找到这个的了)。它看起来比较简单,而且就我使用的情况来看,Jenkins(v1.451)识别了所有元素和属性。

但有一点需要注意:添加多个<failure ...元素时,Jenkins只会保留一个。创建xml文件时,现在我将所有故障信息都合并成了一个。


更新 2016-11:链接已经失效。更好的选择是 cubic.org 的这个页面:JUnit XML reporting file format,他们花费了不少心思提供了一个合理的、有文档说明的示例。示例和xsd文件都已复制在下面,但他们的页面看起来更好看。


示例JUnit XML文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- a description of the JUnit XML format and how Jenkins parses it. See also junit.xsd -->

<!-- if only a single testsuite element is present, the testsuites
     element can be omitted. All attributes are optional. -->
<testsuites disabled="" <!-- total number of disabled tests from all testsuites. -->
            errors=""   <!-- total number of tests with error result from all testsuites. -->
            failures="" <!-- total number of failed tests from all testsuites. -->
            name=""
            tests=""    <!-- total number of successful tests from all testsuites. -->
            time=""     <!-- time in seconds to execute all test suites. -->
        >

  <!-- testsuite can appear multiple times, if contained in a testsuites element.
       It can also be the root element. -->
  <testsuite name=""      <!-- Full (class) name of the test for non-aggregated testsuite documents.
                               Class name without the package for aggregated testsuites documents. Required -->
         tests=""     <!-- The total number of tests in the suite, required. -->
         disabled=""  <!-- the total number of disabled tests in the suite. optional -->
             errors=""    <!-- The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem,
                               for example an unchecked throwable; or a problem with the implementation of the test. optional -->
             failures=""  <!-- The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed
                               by using the mechanisms for that purpose. e.g., via an assertEquals. optional -->
             hostname=""  <!-- Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. optional -->
         id=""        <!-- Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite -->
         package=""   <!-- Derived from testsuite/@name in the non-aggregated documents. optional -->
         skipped=""   <!-- The total number of skipped tests. optional -->
         time=""      <!-- Time taken (in seconds) to execute the tests in the suite. optional -->
         timestamp="" <!-- when the test was executed in ISO 8601 format (2014-01-21T16:17:18). Timezone may not be specified. optional -->
         >

    <!-- Properties (e.g., environment settings) set during test
     execution. The properties element can appear 0 or once. -->
    <properties>
      <!-- property can appear multiple times. The name and value attributres are required. -->
      <property name="" value=""/>
    </properties>

    <!-- testcase can appear multiple times, see /testsuites/testsuite@tests -->
    <testcase name=""       <!-- Name of the test method, required. -->
          assertions="" <!-- number of assertions in the test case. optional -->
          classname=""  <!-- Full class name for the class the test method is in. required -->
          status=""
          time=""       <!-- Time taken (in seconds) to execute the test. optional -->
          >

      <!-- If the test was not executed or failed, you can specify one
           the skipped, error or failure elements. -->

      <!-- skipped can appear 0 or once. optional -->
      <skipped/>

      <!-- Indicates that the test errored. An errored test is one
           that had an unanticipated problem. For example an unchecked
           throwable or a problem with the implementation of the
           test. Contains as a text node relevant data for the error,
           for example a stack trace. optional -->
      <error message="" <!-- The error message. e.g., if a java exception is thrown, the return value of getMessage() -->
         type=""    <!-- The type of error that occured. e.g., if a java execption is thrown the full class name of the exception. -->
         ></error>

      <!-- Indicates that the test failed. A failure is a test which
       the code has explicitly failed by using the mechanisms for
       that purpose. For example via an assertEquals. Contains as
       a text node relevant data for the failure, e.g., a stack
       trace. optional -->
      <failure message="" <!-- The message specified in the assert. -->
           type=""    <!-- The type of the assert. -->
           ></failure>

      <!-- Data that was written to standard out while the test was executed. optional -->
      <system-out></system-out>

      <!-- Data that was written to standard error while the test was executed. optional -->
      <system-err></system-err>
    </testcase>

    <!-- Data that was written to standard out while the test suite was executed. optional -->
    <system-out></system-out>
    <!-- Data that was written to standard error while the test suite was executed. optional -->
    <system-err></system-err>
  </testsuite>
</testsuites>

JUnit XSD文件

<?xml version="1.0" encoding="UTF-8" ?>
<!-- from https://svn.jenkins-ci.org/trunk/hudson/dtkit/dtkit-format/dtkit-junit-model/src/main/resources/com/thalesgroup/dtkit/junit/model/xsd/junit-4.xsd -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:element name="failure">
        <xs:complexType mixed="true">
            <xs:attribute name="type" type="xs:string" use="optional"/>
            <xs:attribute name="message" type="xs:string" use="optional"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="error">
        <xs:complexType mixed="true">
            <xs:attribute name="type" type="xs:string" use="optional"/>
            <xs:attribute name="message" type="xs:string" use="optional"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="properties">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="property" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="property">
        <xs:complexType>
            <xs:attribute name="name" type="xs:string" use="required"/>
            <xs:attribute name="value" type="xs:string" use="required"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="skipped" type="xs:string"/>
    <xs:element name="system-err" type="xs:string"/>
    <xs:element name="system-out" type="xs:string"/>

    <xs:element name="testcase">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="skipped" minOccurs="0" maxOccurs="1"/>
                <xs:element ref="error" minOccurs="0" maxOccurs="unbounded"/>
                <xs:element ref="failure" minOccurs="0" maxOccurs="unbounded"/>
                <xs:element ref="system-out" minOccurs="0" maxOccurs="unbounded"/>
                <xs:element ref="system-err" minOccurs="0" maxOccurs="unbounded"/>
            </xs:sequence>
            <xs:attribute name="name" type="xs:string" use="required"/>
            <xs:attribute name="assertions" type="xs:string" use="optional"/>
            <xs:attribute name="time" type="xs:string" use="optional"/>
            <xs:attribute name="classname" type="xs:string" use="optional"/>
            <xs:attribute name="status" type="xs:string" use="optional"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="testsuite">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="properties" minOccurs="0" maxOccurs="1"/>
                <xs:element ref="testcase" minOccurs="0" maxOccurs="unbounded"/>
                <xs:element ref="system-out" minOccurs="0" maxOccurs="1"/>
                <xs:element ref="system-err" minOccurs="0" maxOccurs="1"/>
            </xs:sequence>
            <xs:attribute name="name" type="xs:string" use="required"/>
            <xs:attribute name="tests" type="xs:string" use="required"/>
            <xs:attribute name="failures" type="xs:string" use="optional"/>
            <xs:attribute name="errors" type="xs:string" use="optional"/>
            <xs:attribute name="time" type="xs:string" use="optional"/>
            <xs:attribute name="disabled" type="xs:string" use="optional"/>
            <xs:attribute name="skipped" type="xs:string" use="optional"/>
            <xs:attribute name="timestamp" type="xs:string" use="optional"/>
            <xs:attribute name="hostname" type="xs:string" use="optional"/>
            <xs:attribute name="id" type="xs:string" use="optional"/>
            <xs:attribute name="package" type="xs:string" use="optional"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="testsuites">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="testsuite" minOccurs="0" maxOccurs="unbounded"/>
            </xs:sequence>
            <xs:attribute name="name" type="xs:string" use="optional"/>
            <xs:attribute name="time" type="xs:string" use="optional"/>
            <xs:attribute name="tests" type="xs:string" use="optional"/>
            <xs:attribute name="failures" type="xs:string" use="optional"/>
            <xs:attribute name="disabled" type="xs:string" use="optional"/>
            <xs:attribute name="errors" type="xs:string" use="optional"/>
        </xs:complexType>
    </xs:element>

</xs:schema>

你怎么让失败信息显示得更好看呢?我想手动添加换行符,但在 Jenkins 中它们并不显示。 - rationalcoder
这是我方法的一个缺点。我记得我也曾经为此苦苦挣扎。尝试添加类似<br/>的东西-我忘记了这是如何解决的(而且我们现在不再使用它),但这似乎值得一试。 - parvus
1
我找到了一个解决办法。由于我们正在使用C++,我只是在失败消息中报告失败的数量,并使用“堆栈跟踪”来报告实际的失败情况。由于堆栈跟踪是从失败元素的正文中报告的,所以换行符被正确支持。 - rationalcoder

29

我在这方面没有找到任何好的信息,所以进行了一些试验和错误。Jenkins(v1.585)只识别以下属性和字段(仅限以下内容)。

<?xml version="1.0" encoding="UTF-8"?>
<testsuite>

  <!-- if your classname does not include a dot, the package defaults to "(root)" -->
  <testcase name="my testcase" classname="my package.my classname" time="29">

    <!-- If the test didn't pass, specify ONE of the following 3 cases -->

    <!-- option 1 --> <skipped />
    <!-- option 2 --> <failure message="my failure message">my stack trace</failure>
    <!-- option 3 --> <error message="my error message">my crash report</error>

    <system-out>my STDOUT dump</system-out>

    <system-err>my STDERR dump</system-err>

  </testcase>

</testsuite>

我从这个示例XML文档开始,然后向后推导。

Jenkins插件的较新版本可以处理jUnit.XML结果规范中的更多字段。 - MarkHu
你能详细说明一下吗? - Ian

9
我决定发表一个新答案,因为一些现有的答案已经过时或不完整。
首先:不存在“JUnit XML格式规范”,因为JUnit不会生成任何XML或HTML报告。
XML报告生成是来自Ant JUnit任务/Maven Surefire插件/Gradle(无论您用哪个来运行测试)。XML报告格式最初由Ant引入,后来被Maven(和Gradle)改编。
如果有人只需要官方的XML格式,则:
  1. 存在一个用于maven surefire生成的XML报告的模式,可以在此处找到:surefire-test-report.xsd
  2. 对于由Ant生成的XML,这里有一个第三方模式可用here (但可能略有过时)。
希望对大家有所帮助。

感谢您的澄清。我正在尝试将旧的Jenkins 1.6实例中的JUnit测试摘要发送到Slack - 或许您可以帮忙?我应该把这样的XML文件放在哪里? - JJD
@JJD 抱歉,我不明白你的意思。你所说的“such XML file”是指什么?但我猜想你已经使用ant/maven/gradle运行了你的JUnit测试,是吗?如果是的话,这些工具在测试执行后会生成漂亮的摘要报告。Jenkins的版本在这里并不重要。 - G. Demecki
是的,我的构建通过Gradle运行。我想在使用Jenkins 1.6时将JUnit测试摘要发送到Slack频道。阅读GitHub讨论后,我认为我需要将配置XML文件放在某个地方,以便Slack插件获取测试摘要。也许我误解了。 - JJD
1
请在 Gradle 完成启动 JUnit 测试后检查是否存在正确生成的测试报告。之后,Slack 插件应该能够使用这些报告。 - G. Demecki
1
最后,您的建议将我推向了正确的方向:我必须配置正确的路径以查找XML文件。对于我的具有多个Gradle产品风格Android项目,以下内容有效:**/build/test-results/**/TEST-*.xml。非常感谢!!! - JJD

8

基本结构 下面是一个JUnit输出文件的示例,展示了跳过和失败的结果,以及一个通过的结果。

<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
   <testsuite name="JUnitXmlReporter" errors="0" tests="0" failures="0" time="0" timestamp="2013-05-24T10:23:58" />
   <testsuite name="JUnitXmlReporter.constructor" errors="0" skipped="1" tests="3" failures="1" time="0.006" timestamp="2013-05-24T10:23:58">
      <properties>
         <property name="java.vendor" value="Sun Microsystems Inc." />
         <property name="compiler.debug" value="on" />
         <property name="project.jdk.classpath" value="jdk.classpath.1.6" />
      </properties>
      <testcase classname="JUnitXmlReporter.constructor" name="should default path to an empty string" time="0.006">
         <failure message="test failure">Assertion failed</failure>
      </testcase>
      <testcase classname="JUnitXmlReporter.constructor" name="should default consolidate to true" time="0">
         <skipped />
      </testcase>
      <testcase classname="JUnitXmlReporter.constructor" name="should default useDotNotation to true" time="0" />
   </testsuite>
</testsuites>

以下是典型的JUnit XML报告结构。请注意,报告可以包含一个或多个测试套件。每个测试套件都有一组属性(记录环境信息)。每个测试套件还包含一个或多个测试用例,如果测试未通过,则每个测试用例将包含一个跳过、失败或错误节点。如果测试用例已通过,则不会包含任何节点。有关每个节点有效的属性的详细信息,请参阅以下“模式”部分。
<testsuites>        => the aggregated result of all junit testfiles
  <testsuite>       => the output from a single TestSuite
    <properties>    => the defined properties at test execution
      <property>    => name/value pair for a single property
      ...
    </properties>
    <error></error> => optional information, in place of a test case - normally if the tests in the suite could not be found etc.
    <testcase>      => the results from executing a test method
      <system-out>  => data written to System.out during the test run
      <system-err>  => data written to System.err during the test run
      <skipped/>    => test was skipped
      <failure>     => test failed
      <error>       => test encountered an error
    </testcase>
    ...
  </testsuite>
  ...
</testsuites>

4

“JUnit”和“xUnit”的结果有多个模式。

请注意,Jenkins xunit-plugin使用几个版本的模式(当前最新版本是junit-10.xsd,添加了对Erlang/OTP Junit格式的支持)。

一些测试框架以及“xUnit”风格的报告插件也使用自己的方法来生成“xUnit”风格的报告,这些可能不使用特定的模式(请注意:他们尝试使用但工具可能无法验证任何一个模式)。Python unittests in Jenkins?提供了几个库之间的快速比较以及生成的xml报告之间的轻微差异。


3
在使用Python方面有很多种方法,以下是一些有用的答案:Python unittests in Jenkins? 在我看来,最好的方法是编写Python单元测试并安装pytest(类似于“yum install pytest”)以获取py.test的安装。然后像这样运行测试:“py.test --junitxml results.xml test.py”。您可以运行任何unittest Python脚本并获得jUnit xml结果。 https://docs.python.org/2.7/library/unittest.html 在Jenkins构建配置中,添加一个“发布JUnit测试结果报告”的操作,并添加result.xml和任何其他测试结果文件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接