Qual è la specifica del formato XML di JUnit supportata da Hudson?


183

Ho Hudson come server di integrazione continua e desidero utilizzare l'opzione "Pubblica rapporto sui risultati del test JUnit". Ma non uso gli strumenti xUnit per i test, invece ho script di shell che eseguono test e restituiscono risultati in formato semplice. Sto pensando di creare uno script che trasforma questi risultati nel formato JUnit. Quindi sono interessante come deve apparire il file JUnit?


Qualche motivo per non usare JUnit? Questi test possono essere automatizzati in una varietà di mode tramite una varietà di strumenti cmd, UI, ecc ...
Aaron McIver

6
@AaronMcIver: gli script della shell sono piuttosto efficaci nell'esecuzione dei test (linguaggio che non è Java). Come useresti JUnit per questo?
Ben Voigt,

1
@BenVoigt Avevo inizialmente supposto che l'OP avesse coinvolto Java e stavo cercando di bypassare JUnit come cablaggio di test. Molto probabilmente questo non è il caso dopo aver esaminato la domanda. Sembra che code.google.com/p/shell2junit possa fornire un certo uso all'OP dopo una seconda occhiata.
Aaron McIver,

1
Sulla falsariga di shell2unit ecco una classe JAXB che ho creato che può analizzare / generare XML JUnit: gist.github.com/agentgt/8583649
Adam Gent

Risposte:


127

Ho fatto una cosa simile qualche mese fa e si è scoperto che questo semplice formato era sufficiente per Hudson per accettarlo come protocollo di prova:

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

Questa domanda ha risposte con maggiori dettagli: Spec. per l'output XML di JUnit


Correggi questa risposta, perché il plugin xunit rifiuta l'attributo 'classname' e accetta solo 'class'
e il

10
ho avuto il problema opposto. classè stato rifiutato e ha classnamefunzionato solo .
Ryanbrainard,

1
Questo ha iniziato a fallire per me quando ho aggiornato il plugin xUnit a 1.60. Ho scoperto che il validatore è diventato più rigoroso e ho dovuto aggiungere <testsuite tests="(number of tests)">ex. <testsuite tests="10">.
Kevin Brotcke,

2
Grazie @KevinBrotcke, aggiornerò la risposta per includere quell'attributo.
Anders Lindahl,

2
Inoltre, per consentire a Hudson di organizzare i test in base al pacchetto / suite, è necessario specificare un pacchetto nell'attributo classname. es: <testcase classname="foo.bar" name="ATest" /> Questo metterà la classe bar in un pacchetto foo su Jenkins rendendo più organizzata la tua raccolta di test.
jluzwick,

90

Ho appena preso junit-4.xsd a cui altri hanno collegato e utilizzato uno strumento chiamato XMLSpear per convertire lo schema in un file XML vuoto con le opzioni mostrate di seguito. Questo è il risultato (leggermente ripulito):

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

Alcuni di questi elementi possono verificarsi più volte:

  • Può esserci solo un testsuiteselemento, poiché è così che funziona XML, ma possono esserci più testsuiteelementi all'interno ditestsuites dell'elemento.
  • Ogni propertieselemento può avere piùproperty figli.
  • Ogni testsuiteelemento può avere piùtestcase figli.
  • Ogni testcaseelemento può avere più error, failure, system-outo system-errbambini.

Opzioni XMLSpear


1
Esiste un documento che descrive i valori validi di determinati attributi, come lo stato del testcase o il tipo di errore?
Eric Cope,

1
@EricCope Posso consigliare di guardare il codice sorgente svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/… . Fondamentalmente è solo una stringa.
Sulthan,

4
Perché i tag sono duplicati?
Nakilon,

Impostazioni del mirror: imgur.com/quneFJf alt: Rootelement: testsuites, Max recursive de...: 2, Max Repeat factor: 2, include optional elements: (sì = spuntata), include optional attributes: (sì = spuntato)
n611x007

1
@Nakilon È in ritardo di 2,5 anni, ma l'ho corretto
bdesham,

45

La risposta migliore alla domanda Anders Lindahl fa riferimento a un file xsd .

Personalmente ho trovato anche questo file xsd molto utile (non ricordo come l'ho trovato). Sembra un po 'meno intimidatorio e, per quanto lo abbia usato, tutti gli elementi e gli attributi sembrano essere riconosciuti da Jenkins (v1.451)

Una cosa però: quando si aggiungono più <failure ...elementi, solo uno è stato mantenuto in Jenkins. Quando si crea il file XML, ora concateno tutti gli errori in uno.


Aggiornamento 2016-11 Il collegamento è interrotto ora. Un'alternativa migliore è questa pagina di cubic.org: formato di file di report XML di JUnit , in cui è stato fatto uno sforzo per fornire un ragionevole esempio documentato . Esempio e xsd sono copiati di seguito, ma la loro pagina sembra più bella.


file XML di esempio 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>

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

Come riesci a far sembrare belli i fallimenti? Vorrei aggiungere manualmente nuovi caratteri di linea ma non vengono visualizzati in Jenkins.
rationalcoder,

Questo è uno svantaggio con il mio approccio. Ricordo di aver lottato con questo un pozzo. Prova ad aggiungere qualcosa come & lt; br / & gt; - Ho dimenticato come è stato risolto (e non lo stiamo più usando), ma sembra qualcosa che valga la pena provare.
parvus

1
Ho trovato un modo per aggirarlo. Dal momento che stiamo usando c ++ sto solo riportando il numero di errori nel messaggio di errore e usando la "Stack Trace" per segnalare gli errori reali. Poiché la traccia dello stack viene riportata dal testo all'interno del corpo dell'elemento di errore, le nuove righe sono supportate correttamente.
rationalcoder

25

Non sono riuscito a trovare alcuna buona informazione al riguardo, quindi ho fatto qualche tentativo ed errore. I seguenti attributi e campi (e solo questi) sono riconosciuti da 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>

(Ho iniziato con questo documento XML di esempio e da lì ho lavorato all'indietro.)


6

Struttura di base Ecco un esempio di un file di output JUnit, che mostra un salto e un risultato non riuscito, nonché un singolo risultato passato.

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

Di seguito è riportata la struttura documentata di un tipico report XML JUnit. Si noti che un report può contenere 1 o più suite di test. Ogni suite di test ha una serie di proprietà (registrazione delle informazioni sull'ambiente). Ogni suite di test contiene anche 1 o più casi di test e ogni caso di test conterrà un nodo saltato, non funzionante o di errore se il test non ha superato. Se il test case è stato superato, non conterrà alcun nodo. Per maggiori dettagli su quali attributi sono validi per ciascun nodo, consultare la seguente sezione "Schema".

<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

Esistono più schemi per i risultati "JUnit" e "xUnit".

Si noti che esistono diverse versioni dello schema in uso dal plugin xunit Jenkins (l'attuale versione più recente è junit-10.xsd che aggiunge il supporto per il formato Junit Erlang / OTP).

Alcuni framework di test e plug-in di report in stile "xUnit" usano anche la loro salsa segreta per generare report in stile "xUnit", quelli potrebbero non utilizzare uno schema particolare (leggi: ci provano ma gli strumenti potrebbero non essere validi contro uno schema). Python si svela in Jenkins? offre un rapido confronto tra alcune di queste librerie e lievi differenze tra i report xml generati.


2

Buone risposte qui sull'uso di Python: (ci sono molti modi per farlo) Python si sblocca in Jenkins?

IMHO il modo migliore è scrivere test unittest python e installare pytest (qualcosa come 'yum install pytest') per installare py.test. Quindi esegui test come questo: 'py.test --junitxml results.xml test.py' . È possibile eseguire qualsiasi script Python unittest e ottenere risultati xml jUnit.

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

In jenkins build configuration Azioni post-build Aggiungi un'azione "Pubblica rapporto sui risultati del test JUnit" con result.xml e altri file di risultati del test che produci.


2

Ho deciso di pubblicare una nuova risposta, perché alcune risposte esistenti sono obsolete o incomplete.

Prima di tutto: non c'è niente di simile JUnit XML Format Specification , semplicemente perché JUnit non produce alcun tipo di report XML o HTML.

La generazione stessa del report XML proviene dall'attività Ant JUnit / Maven Surefire Plugin / Gradle (a seconda di quale si utilizza per eseguire i test). Il formato del report XML è stato inizialmente introdotto da Ant e successivamente adattato da Maven (e Gradle).

Se qualcuno ha solo bisogno di un formato XML ufficiale, allora:

  1. Esiste uno schema per un rapporto XML generato da surefire maven e può essere trovato qui: surefire-test-report.xsd .
  2. Per un XML formica generata c'è uno schema 3a parte disponibile qui (ma potrebbe essere un po 'datato).

Spero che possa aiutare qualcuno.


Grazie per il tuo chiarimento. Sto cercando di inviare a Slack riepiloghi dei test JUnit da una vecchia istanza di Jenkins 1.6 - forse mi aiuti? Dove posizionerei un tale file XML?
JJD,

@JJD Scusa, non ti capisco. Cosa intendi esattamente con tale file XML ? Ma suppongo che tu abbia già eseguito i tuoi test JUnit con ant / maven / gradle, sì? Se sì, questi strumenti, dopo l'esecuzione dei test, generano un bel rapporto di riepilogo. La versione di Jenkins non ha importanza qui.
G. Demecki,

Sì, la mia build viene eseguita tramite Gradle. Vorrei inviare un riepilogo del test JUnit a un canale Slack durante l'utilizzo di Jenkins 1.6. Leggendo la discussione su GitHub ho pensato di dover mettere un file XML di configurazione da qualche parte per consentire al plugin Slack di raccogliere il riepilogo del test. Forse fraintendere.
JJD

1
Verificare che i rapporti di test generati correttamente esistano dopo che Gradle ha terminato l'avvio dei test JUnit. Quindi il plug-in Slack dovrebbe essere in grado di utilizzare questi report.
G. Demecki,

1
Infine, il tuo consiglio mi ha spinto nella giusta direzione: ho dovuto configurare il percorso corretto per cercare i file XML . Per il mio Android progetto con molteplici sapori dei prodotti Gradle , i seguenti lavori: **/build/test-results/**/TEST-*.xml. Grazie mille!!!
JJD
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.