¿Cuál es la especificación de formato XML JUnit que admite Hudson?

183

Tengo Hudson como servidor de integración continua y quiero usar la opción 'Publicar informe de resultados de prueba JUnit'. Pero no uso las herramientas de xUnit para las pruebas, en lugar de eso tengo scripts de shell que ejecutan pruebas y devuelven resultados en formato simple. Estoy pensando en hacer un script que transforme estos resultados al formato JUnit. Entonces, ¿me interesa cómo debe verse el archivo JUnit?

krvladislav
fuente
¿Alguna razón para no usar JUnit? Estas pruebas pueden automatizarse en una variedad de modas a través de una variedad de herramientas cmd, UI, etc ...
Aaron McIver
66
@AaronMcIver: los scripts de Shell son bastante buenos para ejecutar pruebas en (lenguaje que no es Java). ¿Cómo usarías JUnit para eso?
Ben Voigt
1
@BenVoigt Asumí inicialmente que el OP tenía Java involucrado y estaba buscando eludir JUnit como el arnés de prueba. Es muy probable que este no sea el caso después de revisar la pregunta. Parece que code.google.com/p/shell2junit puede proporcionar algún uso al OP después de una segunda mirada.
Aaron McIver
1
En la línea de shell2unit aquí hay una clase JAXB que creé que puede analizar / generar JUnit XML: gist.github.com/agentgt/8583649
Adam Gent el

Respuestas:

127

Hice algo similar hace unos meses, y resultó que este formato simple era suficiente para que Hudson lo aceptara como un protocolo de prueba:

<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>

Esta pregunta tiene respuestas con más detalles: Spec. para JUnit XML Output

Anders Lindahl
fuente
Corrija esta respuesta, porque el complemento xunit rechaza el atributo 'classname' y acepta solo 'class'
y el
10
Tuve el problema opuesto. classfue rechazado y solo classnamefuncionó.
ryanbrainard
1
Esto comenzó a fallar cuando actualicé el complemento xUnit a 1.60. Encontré que el validador se volvió más estricto y tuve que agregar <testsuite tests="(number of tests)">ex. <testsuite tests="10">.
Kevin Brotcke
2
Gracias @KevinBrotcke, actualizaré la respuesta para incluir ese atributo.
Anders Lindahl
2
También tenga en cuenta que para que Hudson organice sus pruebas por paquete / suite, debe especificar un paquete en el atributo classname. Ej: <testcase classname="foo.bar" name="ATest" /> Esto pondrá la clase de barra en un paquete foo en Jenkins haciendo que su colección de pruebas sea más organizada.
jluzwick
90

Acabo de tomar el junit-4.xsd que otros han vinculado y usé una herramienta llamada XMLSpear para convertir el esquema en un archivo XML en blanco con las opciones que se muestran a continuación. Este es el resultado (ligeramente limpio):

<?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>

Algunos de estos elementos pueden ocurrir varias veces:

  • Solo puede haber un testsuiteselemento, ya que así es como funciona XML, pero puede haber varios testsuiteelementos dentro del testsuiteselemento.
  • Cada propertieselemento puede tener varios propertyhijos.
  • Cada testsuiteelemento puede tener varios testcasehijos.
  • Cada testcaseelemento puede tener múltiples error, failure, system-out, o system-errlos niños.

Opciones de XMLSpear

Todd Mazierski
fuente
1
¿Existe un documento que describa los valores válidos de ciertos atributos, como el estado del caso de prueba o el tipo de error?
Eric Cope
1
@EricCope Puedo recomendar mirar el código fuente svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/… . Básicamente es solo una cuerda.
Sulthan
44
¿Por qué se duplican las etiquetas?
Nakilon
Configuración de la réplica: imgur.com/quneFJf alt: Rootelement: testsuites, Max recursive de...: 2, Max Repeat factor: 2, include optional elements: (sí = marcada), include optional attributes: (sí = marcada)
n611x007
1
Es @Nakilon 2,5 años de retraso, pero me fijo
bdesham
45

La respuesta principal de la pregunta Anders Lindahl se refiere a un archivo xsd .

Personalmente, encontré este archivo xsd también muy útil (no recuerdo cómo encontré ese). Parece un poco menos intimidante y, por lo que yo he usado, todos los elementos y atributos parecen ser reconocidos por Jenkins (v1.451)

Sin embargo, una cosa: al agregar varios <failure ...elementos, solo uno se retuvo en Jenkins. Al crear el archivo xml, ahora concateno todas las fallas en una.


Actualización 2016-11 El enlace está roto ahora. Una mejor alternativa es esta página de cubic.org: formato de archivo de informes XML JUnit , donde se ha realizado un buen esfuerzo para proporcionar un ejemplo documentado sensato . Ejemplo y xsd se copian a continuación, pero su página se ve mucho mejor.


ejemplo de archivo XML JUnit

<?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>

Archivo 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>
parvus
fuente
¿Cómo logras que las fallas se vean bien entonces? Me gustaría agregar manualmente nuevos caracteres de línea, pero no aparecen en Jenkins.
racionalcoder
Esa es una desventaja con mi enfoque. Recuerdo haber luchado con eso un pozo. Intenta agregar algo como & lt; br / & gt; - Olvidé cómo se resolvió esto (y ya no lo estamos usando), pero parece que vale la pena intentarlo.
parvus
1
Encontré una forma de evitarlo. Como estamos usando c ++, solo estoy informando el número de fallas en el mensaje de falla y estoy usando el "Seguimiento de pila" para informar las fallas reales. Dado que el seguimiento de la pila se informa desde el texto dentro del cuerpo del elemento de falla, las nuevas líneas se admiten correctamente.
racionalcoder
25

No pude encontrar ninguna buena información sobre esto, así que hice un poco de prueba y error. Jenkins (v1.585) reconoce los siguientes atributos y campos (y solo estos).

<?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>

(Comencé con este documento XML de muestra y trabajé hacia atrás desde allí).

Ian
fuente
6

Estructura básica Aquí hay un ejemplo de un archivo de salida JUnit, que muestra un resultado omitido y fallido, así como un único resultado aprobado.

<?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>

A continuación se muestra la estructura documentada de un informe típico de JUnit XML. Tenga en cuenta que un informe puede contener 1 o más conjuntos de pruebas. Cada conjunto de pruebas tiene un conjunto de propiedades (información del entorno de grabación). Cada conjunto de pruebas también contiene 1 o más casos de prueba y cada caso de prueba contendrá un nodo omitido, de falla o error si la prueba no pasó. Si el caso de prueba ha pasado, entonces no contendrá ningún nodo. Para obtener más detalles sobre qué atributos son válidos para cada nodo, consulte la siguiente sección "Esquema".

<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>
Nayana Adassuriya
fuente
4

Existen múltiples esquemas para los resultados "JUnit" y "xUnit".

Tenga en cuenta que hay varias versiones del esquema en uso por el complemento xunit de Jenkins (la última versión actual es la junit-10.xsdque agrega soporte para el formato Erlang / OTP Junit).

Algunos marcos de prueba, así como los complementos de informes de estilo "xUnit" también usan su propia salsa secreta para generar informes de estilo "xUnit", es posible que no utilicen un esquema particular (lea: lo intentan, pero las herramientas pueden no validarse contra ninguna un esquema) ¿Pruebas unitarias de Python en Jenkins? ofrece una comparación rápida de varias de estas bibliotecas y ligeras diferencias entre los informes xml generados.

dnozay
fuente
2

Buenas respuestas aquí sobre el uso de python: (hay muchas formas de hacerlo) ¿ Pruebas unitarias de Python en Jenkins?

En mi humilde opinión, la mejor manera es escribir pruebas de prueba de unidad de Python e instalar pytest (algo así como 'yum install pytest') para instalar py.test. Luego ejecute pruebas como esta: 'py.test --junitxml results.xml test.py' . Puede ejecutar cualquier script python unittest y obtener resultados jUnit xml.

https://docs.python.org/2.7/library/unittest.html

En la configuración de compilación de jenkins Acciones posteriores a la compilación Agregue una acción "Publicar informe de resultados de prueba JUnit" con result.xml y cualquier otro archivo de resultados de prueba que produzca.

gaoithe
fuente
2

He decidido publicar una nueva respuesta, porque algunas respuestas existentes están desactualizadas o incompletas.

En primer lugar: no hay nada parecido JUnit XML Format Specification, simplemente porque JUnit no produce ningún tipo de informe XML o HTML.

La generación de informes XML en sí proviene de la tarea Ant JUnit / Maven Surefire Plugin / Gradle (lo que utilice para ejecutar sus pruebas). El formato de informe XML fue introducido por primera vez por Ant y luego adaptado por Maven (y Gradle).

Si alguien solo necesita un formato XML oficial, entonces:

  1. Existe un esquema para un informe XML de maven surefire generado y se puede encontrar aquí: surefire-test-report.xsd .
  2. Para un XML generado por hormigas hay un esquema de terceros disponible aquí (pero puede estar un poco desactualizado).

Espero que ayude a alguien.

G. Demecki
fuente
Gracias por tu aclaración. Estoy tratando de enviar resúmenes de prueba JUnit de una antigua instancia de Jenkins 1.6 a Slack , ¿tal vez me ayuden? ¿Dónde colocaría tal archivo XML?
JJD
@JJD Lo siento, no te entiendo. ¿Qué quieres decir exactamente con tal archivo XML ? Pero supongo que ya ejecutas tus pruebas JUnit con ant / maven / gradle, ¿sí? En caso afirmativo, estas herramientas, después de la ejecución de las pruebas, generan el informe resumido agradable. La versión de Jenkins no importa aquí.
G. Demecki
Sí, mi compilación se ejecuta a través de Gradle. Me gustaría enviar un resumen de la prueba JUnit a un canal de Slack mientras utilizo Jenkins 1.6. Al leer la discusión de GitHub, pensé que tenía que poner un archivo XML de configuración en algún lugar para permitir que el complemento Slack recogiera el resumen de la prueba. Tal vez no lo entiendo.
JJD
1
Verifique que existan informes de prueba generados correctamente después de que Gradle termine de iniciar sus pruebas JUnit. Entonces el complemento Slack debería poder usar estos informes.
G. Demecki
1
Finalmente, su consejo me empujó en la dirección correcta: tuve que configurar la ruta correcta para buscar los archivos XML . Por mi androide proyecto con múltiples sabores de productos Gradle , las obras siguientes: **/build/test-results/**/TEST-*.xml. ¡¡¡Muchas gracias!!!
JJD