Jaka jest specyfikacja formatu JUnit XML obsługiwana przez Hudson?


183

Mam Hudson jako serwer ciągłej integracji i chcę użyć opcji „Publikuj raport wyników testu JUnit”. Ale nie używam narzędzi xUnit do testowania, zamiast tego mam skrypty powłoki, które uruchamiają testy i zwracają wyniki w prostym formacie. Zastanawiam się nad stworzeniem skryptu, który przekształci te wyniki w format JUnit. Ciekawe, jak powinien wyglądać plik JUnit?


Czy jest jakiś powód, aby nie używać JUnit? Testy te można zautomatyzować na różne sposoby za pomocą różnych narzędzi cmd, interfejsu użytkownika itp.
Aaron McIver

6
@AaronMcIver: Skrypty powłoki są całkiem dobre w uruchamianiu testów (język inny niż Java). Jak wykorzystasz do tego JUnit?
Ben Voigt,

1
@BenVoigt Początkowo zakładałem, że OP ma włączoną Javę i chciałem ominąć JUnit jako uprząż testową. Najprawdopodobniej tak nie jest po przejrzeniu pytania. Wygląda na to, że code.google.com/p/shell2junit może nieco pomóc OP po drugim spojrzeniu.
Aaron McIver

1
Wzdłuż linii shell2unit znajduje się utworzona przeze mnie klasa JAXB, która może analizować / wyprowadzać JUnit XML: gist.github.com/agentgt/8583649
Adam Gent

Odpowiedzi:


127

Podobne działanie zrobiłem kilka miesięcy temu i okazało się, że ten prosty format wystarczył, aby Hudson zaakceptował go jako protokół testowy:

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

To pytanie zawiera odpowiedzi z dalszymi szczegółami: Spec. dla danych wyjściowych JUnit XML


Proszę poprawić tę odpowiedź, ponieważ wtyczka xunit odrzuca atrybut „classname” i akceptuje tylko „class”
andho

10
miałem odwrotny problem. classzostał odrzucony i classnamedziałał tylko .
ryanbrainard

1
Zaczęło mnie to nie powieść, kiedy zaktualizowałem wtyczkę xUnit do wersji 1.60. Odkryłem, że walidator stał się bardziej rygorystyczny i musiałem dodać <testsuite tests="(number of tests)">ex. <testsuite tests="10">.
Kevin Brotcke

2
Dzięki @KevinBrotcke, zaktualizuję odpowiedź, aby uwzględnić ten atrybut.
Anders Lindahl

2
Pamiętaj też, że aby Hudson zorganizował testy według pakietu / pakietu, musisz określić pakiet w atrybucie nazwa klasy. np .: <testcase classname="foo.bar" name="ATest" /> spowoduje to umieszczenie klasy bar w pakiecie foo na Jenkins, dzięki czemu twoja kolekcja testowa będzie bardziej zorganizowana.
juzuzwick

89

Właśnie chwyciłem junit-4.xsd, z którym inni się połączyli, i użyłem narzędzia o nazwie XMLSpear do konwersji schematu na pusty plik XML z opcjami pokazanymi poniżej. Oto (lekko oczyszczony) wynik:

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

Niektóre z tych elementów mogą występować wiele razy:

  • Może istnieć tylko jeden testsuiteselement, ponieważ tak działa XML, ale element może zawierać wiele testsuiteelementów testsuites.
  • Każdy propertieselement może mieć wiele propertydzieci.
  • Każdy testsuiteelement może mieć wiele testcasedzieci.
  • Każdy testcaseelement może mieć wiele error, failure, system-outani system-errdzieci.

Opcje XMLSpear


1
Czy istnieje dokument opisujący prawidłowe wartości niektórych atrybutów, takich jak stan testowej lub typ błędu?
Eric Cope

1
@EricCope mogę polecić patrząc na kod źródłowy svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/... . Zasadniczo to tylko struna.
Sułtan

4
Dlaczego tagi są duplikowane?
Nakilon

Ustawienia Lustro: imgur.com/quneFJf alt: Rootelement: testsuites, Max recursive de...: 2, Max Repeat factor: 2, include optional elements: (yes = zaznaczone) include optional attributes: (yes = zaznaczone)
n611x007

1
@Nakilon Jest 2,5 roku za późno, ale to naprawiłem
bdesham

45

Top odpowiedź na pytanie Anders Lindahl odnosi się do pliku XSD .

Osobiście uważam ten plik xsd za bardzo przydatny (nie pamiętam, jak go znalazłem). Wygląda to nieco mniej onieśmielająco i, o ile go użyłem, wszystkie elementy i atrybuty wydają się być rozpoznawane przez Jenkinsa (v1.451)

Jedno tylko: przy dodawaniu wielu <failure ...elementów tylko jeden zachował się w Jenkins. Podczas tworzenia pliku xml łączę teraz wszystkie błędy w jednym.


Aktualizacja 2016-11 Link jest teraz zepsuty. Lepszą alternatywą jest ta strona z cubic.org: format pliku raportowania JUnit XML , gdzie podjęto starania, aby dostarczyć sensownego udokumentowanego przykładu. Przykład i xsd są kopiowane poniżej, ale ich strona wygląda o wiele ładniej.


przykładowy plik 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>

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

Jak w takim razie błędy wyglądają ładnie? Chciałbym ręcznie dodać nowe znaki linii, ale nie pojawiają się w Jenkins.
rationalcoder,

To wada mojego podejścia. Pamiętam, jak miałem z tym problem. Spróbuj dodać coś takiego jak & lt; br / & gt; - Zapomniałem, jak to rozwiązano (i już tego nie używamy), ale wydaje się, że warto spróbować.
parvus,

1
Znalazłem sposób na obejście tego. Ponieważ używamy c ++, właśnie zgłaszam liczbę awarii w komunikacie o awarii i używam „stosu śledzenia” do zgłaszania faktycznych awarii. Ponieważ ślad stosu jest zgłaszany z tekstu w treści elementu awarii, nowe wiersze są obsługiwane poprawnie.
rationalcoder

25

Nie mogłem znaleźć żadnych dobrych informacji na ten temat, więc zrobiłem kilka prób i błędów. Jenkins (v1.585) rozpoznaje następujące atrybuty i pola (i tylko te).

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

(Zacząłem od tego przykładowego dokumentu XML i od tego momentu zacząłem pracować wstecz).


6

Podstawowa struktura Oto przykład pliku wyjściowego JUnit, pokazujący pominięcie i nieudany wynik, a także pojedynczy wynik.

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

Poniżej znajduje się udokumentowana struktura typowego raportu JUnit XML. Zauważ, że raport może zawierać 1 lub więcej zestawów testów. Każdy zestaw testowy ma zestaw właściwości (informacje o środowisku rejestrowania). Każdy zestaw testowy zawiera również 1 lub więcej przypadków testowych, a każdy przypadek testowy będzie zawierał węzeł pominięty, błędu lub błędu, jeśli test nie przejdzie pomyślnie. Jeśli przypadek testowy minął, nie będzie zawierał żadnych węzłów. Aby uzyskać więcej informacji o tym, które atrybuty są prawidłowe dla każdego węzła, zapoznaj się z następną sekcją „Schemat”.

<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

Istnieje wiele schematów dla wyników „JUnit” i „xUnit”.

Należy pamiętać, że istnieje kilka wersji schematu używanych przez wtyczkę Jenkins xunit (obecna najnowsza wersja junit-10.xsddodaje obsługę formatu Erlang / OTP Junit).

Niektóre frameworki testowe, a także wtyczki raportujące w stylu „xUnit” również używają własnego tajnego sosu do generowania raportów w stylu „xUnit”, mogą one nie używać określonego schematu (przeczytaj: próbują, ale narzędzia mogą nie sprawdzać poprawności w stosunku do dowolnego jeden schemat). Python unittests w Jenkins? daje szybkie porównanie kilku z tych bibliotek i niewielkie różnice między wygenerowanymi raportami xml.


2

Dobre odpowiedzi tutaj na temat używania Pythona: (istnieje wiele sposobów, aby to zrobić) Python unittests w Jenkins?

IMHO najlepszym sposobem jest napisanie testów w języku Python i zainstalowanie pytest (coś w stylu „yum install pytest”), aby zainstalować py.test. Następnie uruchom testy w następujący sposób: „py.test --junitxml results.xml test.py” . Możesz uruchomić dowolny niezabezpieczony skrypt Pythona i uzyskać wyniki jUnit xml.

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

W konfiguracji kompilacji jenkins Akcje po kompilacji Dodaj akcję „Publikuj raport wyników testu JUnit” wraz z plikiem result.xml i innymi tworzonymi plikami wyników testu.


2

Postanowiłem opublikować nową odpowiedź, ponieważ niektóre istniejące odpowiedzi są nieaktualne lub niepełne.

Po pierwsze: nie ma nic takiego JUnit XML Format Specification, po prostu dlatego, że JUnit nie tworzy żadnego rodzaju raportów XML ani HTML.

Samo generowanie raportów XML pochodzi z zadania Ant JUnit / Maven Surefire Plugin / Gradle (w zależności od tego, którego używasz do uruchamiania testów). Format raportu XML został po raz pierwszy wprowadzony przez Anta, a później dostosowany przez Maven (i Gradle).

Jeśli ktoś potrzebuje tylko oficjalnego formatu XML, to:

  1. Istnieje schemat raportu XML wygenerowanego przez maven surefire i można go znaleźć tutaj: surefire-test-report.xsd .
  2. W przypadku XML generowanego przez mrówki dostępny jest tutaj schemat innej firmy (ale może być nieco nieaktualny).

Mam nadzieję, że to komuś pomoże.


Dziękuję Ci za wyjaśnienie. Próbuję wysłać podsumowania testowe JUnit ze starej instancji Jenkins 1.6 do Slacka - może pomożesz? Gdzie mam umieścić taki plik XML?
JJD

@JJD Przepraszam, nie rozumiem cię. Co dokładnie rozumiesz przez taki plik XML ? Ale zakładam, że już uruchomiłeś testy JUnit z mrówką / maven / gradle, tak? Jeśli tak, narzędzia te po wykonaniu testów generują ładny raport podsumowujący. Wersja Jenkinsa nie ma tutaj znaczenia.
G. Demecki

Tak, moja kompilacja działa w trybie Gradle. Chciałbym wysłać podsumowanie testu JUnit do kanału Slack podczas korzystania z Jenkins 1.6. Czytając dyskusję na GitHub, pomyślałem, że muszę gdzieś umieścić plik konfiguracyjny XML, aby wtyczka Slack mogła pobrać podsumowanie testu. Może źle zrozumiałem.
JJD

1
Sprawdź, czy poprawnie wygenerowane raporty z testów istnieją po zakończeniu testów przez Gradle. Wtyczka Slack powinna móc korzystać z tych raportów.
G. Demecki

1
Wreszcie twoja rada popchnęła mnie we właściwym kierunku: musiałem skonfigurować prawidłową ścieżkę, aby szukać plików XML . Dla mnie Android projektu z wielu smaków produktów Gradle następujące prace: **/build/test-results/**/TEST-*.xml. Dziękuję Ci bardzo!!!
JJD
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.