Quelle est la spécification de format XML JUnit prise en charge par Hudson?

183

J'ai Hudson comme serveur d'intégration continue et je souhaite utiliser l'option «Publier le rapport de résultat du test JUnit». Mais je n'utilise pas les outils xUnit pour les tests, au lieu de cela, j'ai des scripts shell qui exécutent des tests et retournent les résultats dans un format simple. Je pense faire un script qui transforme ces résultats au format JUnit. Donc je suis intéressant à quoi le fichier JUnit doit ressembler?

Krvladislav
la source
Une raison de ne pas utiliser JUnit? Ces tests peuvent être automatisés dans une variété de modes via une variété d'outils cmd, UI, etc ...
Aaron McIver
6
@AaronMcIver: Les scripts shell sont assez bons pour exécuter des tests sur (un langage qui n'est pas Java). Comment utiliseriez-vous JUnit pour cela?
Ben Voigt
1
@BenVoigt J'avais initialement supposé que l'OP impliquait Java et cherchait à contourner JUnit comme harnais de test. Ce n'est probablement pas le cas après avoir examiné la question. Il semble que code.google.com/p/shell2junit peut fournir une certaine utilité à l'OP après un second regard.
Aaron McIver
1
Le long des lignes de shell2unit, voici une classe JAXB que j'ai créée qui peut analyser / générer le XML JUnit: gist.github.com/agentgt/8583649
Adam Gent

Réponses:

127

J'ai fait une chose similaire il y a quelques mois, et il s'est avéré que ce format simple était suffisant pour que Hudson l'accepte comme protocole de test:

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

Cette question a des réponses avec plus de détails: Spec. pour la sortie XML JUnit

Anders Lindahl
la source
Veuillez apporter une correction à cette réponse, car le plugin xunit rejette l'attribut 'classname' et n'accepte que 'class'
andho
10
J'ai eu le problème inverse. classa été rejeté et n'a classnamefonctionné.
ryanbrainard
1
Cela a commencé à échouer pour moi lorsque j'ai mis à niveau le plugin xUnit vers la version 1.60. J'ai trouvé que le validateur est devenu plus strict et j'ai dû ajouter <testsuite tests="(number of tests)">ex. <testsuite tests="10">.
Kevin Brotcke
2
Merci @KevinBrotcke, je vais mettre à jour la réponse pour inclure cet attribut.
Anders Lindahl
2
Notez également que pour que Hudson organise vos tests par package / suite, vous devez spécifier un package dans l'attribut classname. ex: <testcase classname="foo.bar" name="ATest" /> Cela placera la classe bar dans un package foo sur Jenkins, ce qui rendra votre collection de tests plus organisée.
jluzwick le
90

Je viens de saisir le junit-4.xsd auquel d'autres ont lié et j'ai utilisé un outil nommé XMLSpear pour convertir le schéma en un fichier XML vierge avec les options ci-dessous. Voici le résultat (légèrement nettoyé):

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

Certains de ces éléments peuvent se produire plusieurs fois:

  • Il ne peut y avoir qu'un seul testsuitesélément, puisque c'est ainsi que fonctionne XML, mais il peut y avoir plusieurs testsuiteéléments dans l' testsuitesélément.
  • Chaque propertiesélément peut avoir plusieurs propertyenfants.
  • Chaque testsuiteélément peut avoir plusieurs testcaseenfants.
  • Chaque testcaseélément peut avoir plusieurs error, failure, system-outou les system-errenfants.

Options XMLSpear

Todd Mazierski
la source
1
Existe-t-il un document qui décrit les valeurs valides de certains attributs, comme le statut de testcase ou le type d'erreur?
Eric Cope
1
@EricCope Je peux recommander de regarder le code source svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/… . En gros, ce n'est qu'une chaîne.
Sulthan
4
Pourquoi les balises sont-elles dupliquées?
Nakilon le
paramètres miroir: imgur.com/quneFJf alt: Rootelement: testsuites, Max recursive de...: 2, Max Repeat factor: 2, include optional elements: (oui = tic tac), include optional attributes: (oui = tic tac)
n611x007
1
@Nakilon Il a 2,5 ans de retard, mais je l'ai corrigé
bdesham
45

La réponse principale à la question Anders Lindahl fait référence à un fichier xsd .

Personnellement, j'ai trouvé ce fichier xsd également très utile (je ne me souviens pas comment j'ai trouvé celui-là). Cela semble un peu moins intimidant, et pour autant que je l'utilise, tous les éléments et attributs semblent être reconnus par Jenkins (v1.451)

Une chose cependant: lors de l'ajout de plusieurs <failure ...éléments, un seul était conservé dans Jenkins. Lors de la création du fichier xml, je concatène maintenant tous les échecs en un seul.


Mise à jour 2016-11 Le lien est maintenant rompu. Une meilleure alternative est cette page de cubic.org: format de fichier de rapport XML JUnit , où un bel effort a été fait pour fournir un exemple documenté judicieux . Exemple et xsd sont copiés ci-dessous, mais leur page est plus jolie.


exemple de fichier 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>

Fichier 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
la source
Comment faire pour que les échecs soient beaux alors? Je voudrais ajouter manuellement de nouveaux caractères de ligne mais ils n'apparaissent pas dans Jenkins.
rationalcoder
C'est un inconvénient avec mon approche. Je me souviens avoir lutté avec un puits. Essayez d'ajouter quelque chose comme & lt; br / & gt; - J'ai oublié comment cela a été résolu (et nous ne l'utilisons plus), mais cela semble valoir la peine d'essayer.
parvus
1
J'ai trouvé un moyen de contourner cela. Puisque nous utilisons C ++, je signale simplement le nombre d'échecs dans le message d'échec et j'utilise le "Stack Trace" pour signaler les échecs réels. Puisque la trace de pile est signalée à partir du texte avec le corps de l'élément d'échec, les nouvelles lignes sont prises en charge correctement.
rationalcoder
25

Je n'ai pas trouvé de bonnes informations à ce sujet, alors j'ai fait quelques essais et erreurs. Les attributs et champs suivants (et uniquement ceux-ci) sont reconnus par 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>

(J'ai commencé avec cet exemple de document XML et j'ai travaillé en arrière à partir de là.)

Ian
la source
6

Structure de base Voici un exemple de fichier de sortie JUnit, montrant un résultat ignoré et échoué, ainsi qu'un seul résultat passé.

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

Vous trouverez ci-dessous la structure documentée d'un rapport XML JUnit typique. Notez qu'un rapport peut contenir une ou plusieurs suites de tests. Chaque suite de tests possède un ensemble de propriétés (enregistrement des informations sur l'environnement). Chaque suite de tests contient également un ou plusieurs cas de test et chaque cas de test contiendra soit un nœud ignoré, un échec ou une erreur si le test n'a pas réussi. Si le scénario de test a réussi, il ne contiendra aucun nœud. Pour plus de détails sur les attributs valides pour chaque nœud, veuillez consulter la section suivante "Schéma".

<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
la source
4

Il existe plusieurs schémas pour les résultats "JUnit" et "xUnit".

Veuillez noter qu'il existe plusieurs versions du schéma utilisées par le plugin xunit Jenkins (la dernière version actuelle junit-10.xsdajoute la prise en charge du format Erlang / OTP Junit).

Certains frameworks de test ainsi que les plugins de rapport de style "xUnit" utilisent également leur propre sauce secrète pour générer des rapports de style "xUnit", ceux-ci peuvent ne pas utiliser un schéma particulier (veuillez lire: ils essaient mais les outils peuvent ne pas valider contre aucun un schéma). Unittests Python dans Jenkins?donne une comparaison rapide de plusieurs de ces bibliothèques et de légères différences entre les rapports xml générés.

Dnozay
la source
2

Bonnes réponses ici sur l'utilisation de python: (il y a plusieurs façons de le faire) Python unittests dans Jenkins?

À mon humble avis, le meilleur moyen est d' écrire des tests python unittest et d' installer pytest (quelque chose comme «yum install pytest») pour installer py.test. Ensuite, exécutez des tests comme celui-ci: 'py.test --junitxml results.xml test.py' . Vous pouvez exécuter n'importe quel script python unittest et obtenir des résultats xml jUnit.

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

Dans la configuration de build jenkins Actions post-build Ajoutez une action "Publier le rapport de résultat de test JUnit" avec result.xml et tout autre fichier de résultat de test que vous produisez.

Gaoithe
la source
2

J'ai décidé de publier une nouvelle réponse, car certaines réponses existantes sont obsolètes ou incomplètes.

Tout d'abord: il n'y a rien de tel JUnit XML Format Specification , simplement parce que JUnit ne produit aucun type de rapport XML ou HTML.

La génération de rapport XML elle-même provient de la tâche Ant JUnit / Maven Surefire Plugin / Gradle (selon ce que vous utilisez pour exécuter vos tests). Le format de rapport XML a été introduit pour la première fois par Ant puis adapté par Maven (et Gradle).

Si quelqu'un a juste besoin d'un format XML officiel, alors:

  1. Il existe un schéma pour un rapport XML généré par maven surefire et il peut être trouvé ici: surefire-test-report.xsd .
  2. Pour un XML généré par fourmi, un schéma tiers est disponible ici (mais il peut être légèrement obsolète).

J'espère que cela aidera quelqu'un.

G. Demecki
la source
Merci pour la clarification. J'essaie d' envoyer des résumés de test JUnit d'une ancienne instance de Jenkins 1.6 à Slack - peut-être que vous aidez? Où placerais-je un tel fichier XML?
JJD
@JJD Désolé, je ne comprends pas. Qu'entendez-vous exactement par un tel fichier XML ? Mais je suppose que vous avez déjà exécuté vos tests JUnit avec ant / maven / gradle, oui? Si oui, ces outils, après l'exécution des tests, génèrent le joli rapport de synthèse. La version de Jenkins n'a pas d'importance ici.
G. Demecki
Oui, ma version fonctionne via Gradle. Je souhaite envoyer un résumé de test JUnit à un canal Slack tout en utilisant Jenkins 1.6. En lisant la discussion sur GitHub, j'ai pensé que je devais mettre un fichier XML de configuration quelque part pour permettre au plugin Slack de récupérer le résumé du test. Peut-être ai-je mal compris.
JJD
1
Veuillez vérifier que les rapports de test correctement générés existent une fois que Gradle a fini de lancer vos tests JUnit. Ensuite, le plugin Slack devrait pouvoir utiliser ces rapports.
G. Demecki
1
Enfin, vos conseils m'ont poussé dans la bonne direction: je devais configurer le bon chemin pour rechercher les fichiers XML . Pour mon Android projet avec de multiples saveurs de produits Gradle , les travaux suivants: **/build/test-results/**/TEST-*.xml. Merci beaucoup!!!
JJD