Welche JUnit XML-Formatspezifikation unterstützt Hudson?

182

Ich habe Hudson als Server für die kontinuierliche Integration und möchte die Option "JUnit-Testergebnisbericht veröffentlichen" verwenden. Aber ich benutze keine xUnit-Tools zum Testen, stattdessen habe ich Shell-Skripte, die Tests ausführen und Ergebnisse in einfachem Format zurückgeben. Ich denke daran, ein Skript zu erstellen, das diese Ergebnisse in das JUnit-Format umwandelt. Ich bin also interessant, wie die JUnit-Datei aussehen muss.

krvladislav
quelle
Gibt es einen Grund, JUnit nicht zu verwenden? Diese Tests können auf verschiedene Weise über eine Vielzahl von Tools cmd, UI usw. automatisiert werden.
Aaron McIver
6
@AaronMcIver: Shell-Skripte können ziemlich gut Tests ausführen (Sprache, die nicht Java ist). Wie würden Sie JUnit dafür verwenden?
Ben Voigt
1
@BenVoigt Ich hatte ursprünglich angenommen, dass am OP Java beteiligt war, und wollte JUnit als Testkabel umgehen. Dies ist nach Prüfung der Frage höchstwahrscheinlich nicht der Fall. Es scheint, dass code.google.com/p/shell2junit dem OP nach einem zweiten Blick eine gewisse Verwendung bieten kann.
Aaron McIver
1
In Anlehnung an shell2unit ist hier eine von mir erstellte JAXB-Klasse, die JUnit XML analysieren / ausgeben kann: gist.github.com/agentgt/8583649
Adam Gent

Antworten:

127

Ich habe vor ein paar Monaten etwas Ähnliches gemacht, und es stellte sich heraus, dass dieses einfache Format für Hudson ausreichte, um es als Testprotokoll zu akzeptieren:

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

Diese Frage hat Antworten mit mehr Details: Spec. für JUnit XML-Ausgabe

Anders Lindahl
quelle
Bitte korrigieren Sie diese Antwort, da das xunit-Plugin das Attribut 'classname' ablehnt und nur 'class' akzeptiert
andho
10
Ich hatte das gegenteilige Problem. classwurde abgelehnt und nur classnamefunktioniert.
Ryanbrainard
1
Dies schlug für mich fehl, als ich das xUnit-Plugin auf 1.60 aktualisierte. Ich stellte fest, dass der Validator strenger wurde und ich <testsuite tests="(number of tests)">Ex hinzufügen musste . <testsuite tests="10">.
Kevin Brotcke
2
Danke @ KevinBrotcke, ich werde die Antwort aktualisieren, um dieses Attribut aufzunehmen.
Anders Lindahl
2
Beachten Sie außerdem, dass Hudson im Attribut classname ein Paket angeben muss, damit Hudson Ihre Tests nach Paket / Suite organisiert. Beispiel: <testcase classname="foo.bar" name="ATest" /> Dadurch wird die Bar-Klasse in ein Foo-Paket für Jenkins aufgenommen, wodurch Ihre Testsammlung besser organisiert wird.
Jluzwick
89

Ich habe gerade die junit-4.xsd abgerufen , mit der andere verknüpft sind, und ein Tool namens XMLSpear verwendet , um das Schema mit den unten gezeigten Optionen in eine leere XML-Datei zu konvertieren. Dies ist das (leicht aufgeräumte) Ergebnis:

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

Einige dieser Elemente können mehrfach vorkommen:

  • Es kann nur ein testsuitesElement geben, da XML so funktioniert, aber es kann mehrere testsuiteElemente innerhalb des XML gebentestsuites Elements vorhanden sein.
  • Jedes propertiesElement kann mehrere untergeordnete Elemente haben property.
  • Jedes testsuiteElement kann mehrere untergeordnete Elemente haben testcase.
  • Jedes testcaseElement kann mehrere haben error, failure, system-out, oder system-errKinder.

XMLSpear-Optionen

Todd Mazierski
quelle
1
Gibt es ein Dokument, das die gültigen Werte bestimmter Attribute beschreibt, z. B. den Status des Testfalls oder den Fehlertyp?
Eric Cope
1
@EricCope Ich kann empfehlen, den Quellcode svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/… zu lesen . Im Grunde ist es nur eine Zeichenfolge.
Sulthan
4
Warum werden Tags dupliziert?
Nakilon
Einstellungen spiegeln: imgur.com/quneFJf alt: Rootelement: testsuites, Max recursive de...: 2, Max Repeat factor: 2, include optional elements: (ja = tickte) include optional attributes: (ja = tickte)
n611x007
1
@ Nakilon Es ist 2,5 Jahre zu spät, aber ich habe es behoben
Bdesham
45

Die Top-Antwort auf die Frage Anders Lindahl bezieht sich auf eine xsd-Datei .

Persönlich fand ich diese xsd-Datei auch sehr nützlich (ich erinnere mich nicht, wie ich diese gefunden habe). Es sieht ein bisschen weniger einschüchternd aus, und soweit ich es benutzt habe, scheinen alle Elemente und Attribute von Jenkins (v1.451) erkannt zu werden.

Eines jedoch: Beim Hinzufügen mehrerer <failure ...Elemente wurde nur eines in Jenkins beibehalten. Beim Erstellen der XML-Datei verkette ich jetzt alle Fehler in einem.


Update 2016-11 Der Link ist jetzt defekt. Eine bessere Alternative ist diese Seite von cubic.org: JUnit XML-Berichtsdateiformat , auf der große Anstrengungen unternommen wurden, um ein vernünftiges dokumentiertes Beispiel bereitzustellen . Beispiel und xsd werden unten kopiert, aber ihre Seite sieht viel besser aus.


Beispiel-JUnit-XML-Datei

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

<?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
quelle
Wie bringen Sie die Fehler dazu, gut auszusehen? Ich möchte neue Zeilenzeichen manuell hinzufügen, aber sie werden in Jenkins nicht angezeigt.
Rationalcoder
Das ist ein Nachteil bei meinem Ansatz. Ich erinnere mich, dass ich damit gut zu kämpfen hatte. Versuchen Sie, etwas wie & lt; br / & gt; - Ich habe vergessen, wie dies gelöst wurde (und wir verwenden dies nicht mehr), aber dies scheint einen Versuch wert zu sein.
Parvus
1
Ich habe einen Weg gefunden, es zu umgehen. Da wir c ++ verwenden, melde ich nur die Anzahl der Fehler in der Fehlermeldung und verwende die "Stapelverfolgung", um die tatsächlichen Fehler zu melden. Da die Stapelverfolgung aus dem Text mit dem Hauptteil des Fehlerelements gemeldet wird, werden neue Zeilen korrekt unterstützt.
Rationalcoder
25

Ich konnte keine guten Informationen dazu finden, also habe ich ein paar Versuche gemacht. Die folgenden Attribute und Felder (und nur diese) werden von Jenkins (v1.585) erkannt.

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

(Ich habe mit diesem XML-Beispieldokument begonnen und von dort aus rückwärts gearbeitet.)

Ian
quelle
6

Grundstruktur Hier ist ein Beispiel für eine JUnit-Ausgabedatei, die ein übersprungenes und fehlgeschlagenes Ergebnis sowie ein einzelnes übergebenes Ergebnis zeigt.

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

Unten finden Sie die dokumentierte Struktur eines typischen JUnit-XML-Berichts. Beachten Sie, dass ein Bericht eine oder mehrere Testsuiten enthalten kann. Jede Testsuite verfügt über eine Reihe von Eigenschaften (Informationen zur Aufzeichnungsumgebung). Jede Testsuite enthält außerdem einen oder mehrere Testfälle, und jeder Testfall enthält entweder einen übersprungenen Fehler- oder Fehlerknoten, wenn der Test nicht bestanden wurde. Wenn der Testfall bestanden wurde, enthält er keine Knoten. Weitere Informationen darüber, welche Attribute für jeden Knoten gültig sind, finden Sie im folgenden Abschnitt "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>
Nayana Adassuriya
quelle
4

Es gibt mehrere Schemas für die Ergebnisse "JUnit" und "xUnit".

Bitte beachten Sie, dass das Jenkins xunit-Plugin mehrere Versionen des Schemas verwendet (die aktuellste Version bietet junit-10.xsdUnterstützung für das Erlang / OTP Junit-Format).

Einige Test-Frameworks sowie Berichts-Plugins im "xUnit" -Stil verwenden auch ihre eigene geheime Sauce, um Berichte im "xUnit" -Stil zu generieren. Diese verwenden möglicherweise kein bestimmtes Schema (bitte lesen Sie: Sie versuchen es, aber die Tools validieren möglicherweise nicht gegen eines ein Schema). Python-Unittests in Jenkins? bietet einen schnellen Vergleich mehrerer dieser Bibliotheken und geringfügige Unterschiede zwischen den generierten XML-Berichten.

dnozay
quelle
2

Gute Antworten zur Verwendung von Python: (Es gibt viele Möglichkeiten, dies zu tun) Python-Unittests in Jenkins?

IMHO ist der beste Weg , Python unittest Tests zu schreiben und pytest (so etwas wie 'yum install pytest') zu installieren, um py.test zu installieren. Dann Tests wie folgt ausführen: 'py.test --junitxml results.xml test.py' . Sie können jedes unittest Python-Skript ausführen und jUnit xml-Ergebnisse erhalten.

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

In der Jenkins-Build-Konfiguration Aktionen nach dem Build Fügen Sie eine Aktion "JUnit-Testergebnisbericht veröffentlichen" mit result.xml und weiteren von Ihnen erstellten Testergebnisdateien hinzu.

gaoithe
quelle
2

Ich habe beschlossen, eine neue Antwort zu veröffentlichen, da einige vorhandene Antworten veraltet oder unvollständig sind.

Zuallererst: Es gibt nichts Vergleichbares JUnit XML Format Specification, einfach weil JUnit keinerlei XML- oder HTML-Bericht erstellt.

Die XML-Berichtsgenerierung selbst stammt aus der Ant JUnit-Task / dem Maven Surefire Plugin / Gradle (je nachdem, was Sie zum Ausführen Ihrer Tests verwenden). Das XML-Berichtsformat wurde zuerst von Ant eingeführt und später von Maven (und Gradle) angepasst.

Wenn jemand nur ein offizielles XML-Format benötigt, dann:

  1. Es gibt ein Schema für einen von Maven Surefire generierten XML-Bericht, den Sie hier finden: surefire-test-report.xsd .
  2. Für eine Ameise generierte XML ist ein 3rd - Party - Schema verfügbar hier (aber es könnte etwas veraltet sein).

Hoffe es wird jemandem helfen.

G. Demecki
quelle
Vielen Dank für Ihre Klarstellung. Ich versuche , JUnit-Testzusammenfassungen von einer alten Jenkins 1.6-Instanz an Slack zu senden - vielleicht helfen Sie? Wo würde ich eine solche XML-Datei platzieren?
JJD
@JJD Entschuldigung, ich verstehe dich nicht. Was genau meinst du mit einer solchen XML-Datei ? Aber ich nehme an, Sie führen Ihre JUnit-Tests bereits mit ant / maven / gradle durch, ja? Wenn ja, generieren diese Tools nach der Testausführung den schönen zusammenfassenden Bericht. Die Version von Jenkins spielt hier keine Rolle.
G. Demecki
Ja, mein Build läuft über Gradle. Ich möchte eine JUnit-Testzusammenfassung an einen Slack-Kanal senden, während ich Jenkins 1.6 verwende. Beim Lesen der GitHub-Diskussion dachte ich, ich müsste irgendwo eine XML-Konfigurationsdatei ablegen, damit das Slack-Plugin die Testzusammenfassung abruft. Vielleicht verstehe ich falsch.
JJD
1
Bitte überprüfen Sie, ob korrekt generierte Testberichte vorhanden sind, nachdem Gradle Ihre JUnit-Tests gestartet hat. Dann sollte das Slack-Plugin diese Berichte verwenden können.
G. Demecki
1
Schließlich hat mich Ihr Rat in die richtige Richtung getrieben: Ich musste den richtigen Pfad konfigurieren , um nach den XML-Dateien zu suchen . Für mein Android- Projekt mit mehreren Gradle-Produktvarianten funktioniert Folgendes : **/build/test-results/**/TEST-*.xml. Vielen Dank!!!
JJD