Що таке специфікація формату JUnit XML, яку підтримує Хадсон?


183

У мене Хадсон як сервер безперервної інтеграції, і я хочу використовувати опцію "Опублікувати звіт про результати тестування JUnit". Але я не використовую інструменти xUnit для тестування, замість цього у мене є скрипти оболонки, які виконують тести і повертають результати у простому форматі. Я думаю зробити сценарій, який перетворює ці результати у формат JUnit. Тож мені цікаво, як повинен виглядати файл JUnit?


Будь-яка причина не використовувати JUnit? Ці тести можна автоматизувати по-різному за допомогою різних інструментів cmd, інтерфейсу користувача тощо ...
Аарон Маківер

6
@AaronMcIver: Сценарії оболонки досить хороші при виконанні тестів (мова, яка не є Java). Як би ти використовував JUnit для цього?
Ben Voigt

1
@BenVoigt Я спочатку припускав, що в ОП була включена Java, і я хотів обійти JUnit як тестовий джгут. Це, швидше за все, не так після розгляду питання. Здається, що code.google.com/p/shell2junit може забезпечити деяке використання ОП після другого огляду.
Аарон Маківер

1
По лінії shell2unit ось клас JAXB, який я створив, який може проаналізувати / вивести JUnit XML: gist.github.com/agentgt/8583649
Адам Гент

Відповіді:


127

Я робив подібну річ кілька місяців тому, і виявилося, що цього простого формату було достатньо, щоб Хадсон прийняв це як протокол тестування:

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

На це питання є відповіді з більш детальною інформацією: Spec. для виводу XUnit XML


Будь ласка, виправте цю відповідь, оскільки плагін xunit відхиляє атрибут 'classname' і приймає просто 'class'
а

10
У мене було протилежне питання. classбув відхилений і classnameпрацював лише .
ryanbrainard

1
Це почалося для мене, коли я оновив плагін xUnit до 1,60. Я виявив, що валідатор став суворішим, і мені довелося додати <testsuite tests="(number of tests)">екс. <testsuite tests="10">.
Кевін Бротке

2
Дякую @KevinBrotcke, я оновлю відповідь, щоб включити цей атрибут.
Андерс Ліндаль

2
Також зауважте, що для того, щоб Хадсон організував свої тести за пакетом / пакетом, ви повинні вказати пакет у атрибуті назви класу. напр .: <testcase classname="foo.bar" name="ATest" /> Це дозволить перетворити бар-клас у пакет foo на Jenkins, що зробить вашу тестову колекцію більш організованою.
jluzwick

89

Я просто схопив junit-4.xsd , з яким пов’язані інші, і застосував інструмент на ім'я XMLSpear для перетворення схеми в порожній XML-файл із наведеними нижче параметрами. Це результат (злегка очищений):

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

Деякі з цих елементів можуть виникати кілька разів:

  • Тут може бути лише один testsuitesелемент, оскільки так працює XML, але testsuiteвсередині може бути декілька елементівtestsuites елемента .
  • Кожен propertiesелемент може мати кількаproperty дітей.
  • Кожен testsuiteелемент може мати кількаtestcase дітей.
  • Кожен testcaseелемент може мати декілька error, failure, system-outабо system-errдитина.

Параметри XMLSpear


1
Чи є документ, який описує дійсні значення певних атрибутів, наприклад, статусу тестової скриньки чи тип помилки?
Ерік Коуп

1
@EricCope Я можу порекомендувати подивитися вихідний код svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/… . В основному це просто струна.
Султан

4
Чому теги дублюються?
Накілон

Параметри дзеркало: imgur.com/quneFJf альта: Rootelement: testsuites, Max recursive de...: 2, Max Repeat factor: 2, include optional elements(да = галочка) include optional attributes: (да = галочка)
n611x007

1
@Nakilon Запізнилося на 2,5 роки, але я це
виправив

45

Верхній відповідь на питання Андерс Ліндаль відноситься до файлу XSD .

Особисто я знайшов цей файл xsd також дуже корисним (не пам’ятаю, як знайшов його). Це виглядає трохи менш залякано, і, наскільки я його використав, всі елементи та атрибути, здається, визнані Дженкінсом (v1.451)

Однак одне: при додаванні декількох <failure ...елементів у Дженкінсі зберігався лише один. Під час створення файлу xml я тепер об'єдную всі збої в одному.


Оновити 2016-11 Посилання перервано зараз. Кращою альтернативою є ця сторінка з cubic.org: формат файлу звітів JUnit XML , де було зроблено хороших зусиль, щоб надати розумний задокументований приклад. Приклад і xsd скопійовані нижче, але їх сторінка виглядає набагато приємніше.


зразок файлу 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>

Файл 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>

Як ви отримуєте невдачі, щоб тоді виглядати красиво? Я хотів би вручну додати нові символи рядків, але вони не з’являються в Дженкінсі.
rationalcoder

Це недолік мого підходу. Я пам’ятаю, що боровся з цією криницею. Спробуйте додати щось на зразок & lt; br / & gt; - Я забув, як це було вирішено (і ми цим більше не користуємось), але це, здається, щось варто спробувати.
parvus

1
Я знайшов шлях навколо цього. Оскільки ми використовуємо c ++, я просто повідомляю про кількість збоїв у повідомленні про відмову та використовую "Слід стека", щоб повідомити про фактичні збої. Оскільки про текст стека повідомляється з тексту, що містить тіло елемента відмови, нові рядки підтримуються правильно.
rationalcoder

25

Я не зміг знайти жодної хорошої інформації з цього приводу, тому я зробив деякі спроби та помилки. Дженкінс розпізнає такі атрибути та поля (і лише ці) (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>

(Я почав з цього зразкового XML-документа і працював звідти назад.)


6

Основна структура Ось приклад вихідного файлу JUnit, що показує пропускний і невдалий результат, а також один пройдений результат.

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

Нижче представлена ​​документально зафіксована структура типового звіту JUnit XML. Зауважте, що звіт може містити 1 або більше тестових наборів. Кожен тестовий набір має набір властивостей (інформація про середовище запису). Кожен тестовий набір також містить 1 або більше тестових випадків, і кожен тестовий випадок буде містити пропущений вузол, помилку чи помилку, якщо тест не пройшов. Якщо тестовий випадок пройшов, він не буде містити жодних вузлів. Для отримання більш детальної інформації про те, які атрибути дійсні для кожного вузла, перегляньте наступний розділ "Схема".

<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

Існує кілька схем для результатів "JUnit" та "xUnit".

Зверніть увагу, що існує кілька версій схеми, яку використовує плагін Jenkins xunit-плагін (поточна остання версія - junit-10.xsd яка додає підтримку формату Erlang / OTP Junit).

Деякі тестові рамки, а також плагіни звітування "xUnit" також використовують власний секретний соус для створення звітів "xUnit" -стили, вони можуть не використовувати певну схему (будь ласка, прочитайте: вони намагаються, але інструменти можуть не підтвердити жодну одна схема). Елітні тести Python у Дженкінсі? дає швидке порівняння кількох цих бібліотек та невеликі відмінності між згенерованими звітами xml.


2

Хороші відповіді щодо використання python: (Є багато способів зробити це) Unittests Python у Дженкінсі?

IMHO найкращий спосіб - написати тести python unittest та встановити pytest (щось на кшталт "yum install pytest"), щоб встановити py.test. Потім запустіть такі тести: 'py.test --junitxml results.xml test.py' . Ви можете запустити будь-який скрипт python для одиничного тестування та отримати jUnit xml результати.

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

У конфігурації збирання jenkins Дії після збирання Додайте дію "Опублікувати звіт про результати тестування JUnit" з result.xml та будь-якими іншими файлами результатів тестування, які ви створюєте.


2

Я вирішив опублікувати нову відповідь, оскільки деякі існуючі відповіді застаріли або неповні.

Перш за все: нічого подібного немає JUnit XML Format Specification просто тому, що JUnit не створює будь-яких звітів XML чи HTML.

Сама генерація звітів XML походить із завдання Ant JUnit / Maven Surefire Plugin / Gradle (що б ви не використовували для запуску тестів). Формат звіту XML був вперше введений Ant, а згодом адаптований Maven (та Gradle).

Якщо комусь просто потрібен офіційний формат XML, виконайте такі дії:

  1. Існує схема для зведеного XML звіту, що генерується підтвердженим файлом, і його можна знайти тут: surefire-test-report.xsd .
  2. Для мурашки згенерованих XML є третя схема партії доступна тут (але це може бути трохи застаріли).

Сподіваюся, це комусь допоможе.


Дякую за роз’яснення. Я намагаюся надсилати резюме тестів JUnit зі старого екземпляра Jenkins 1.6 на Slack - можливо, ви допоможете? Де б я розмістив такий XML-файл?
JJD

@JJD Вибачте, я не розумію вас. Що ви точно маєте на увазі під таким XML-файлом ? Але я припускаю, що ви вже виконуєте свої тести на JUnit з ant / maven / gradle, так? Якщо так, ці інструменти після виконання тестів створюють хороший підсумковий звіт. Версія Дженкінса тут не має значення.
Г. Демецький

Так, моя збірка працює через Gradle. Я хотів би надіслати резюме тесту JUnit на канал Slack під час використання Jenkins 1.6. Читаючи дискусію про GitHub, я подумав, що я повинен десь поставити XML-файл конфігурації, щоб плагін Slack підбирав підсумок тесту. Можливо, я неправильно розумію.
JJD

1
Перевірте, чи правильно створені звіти про тести існують після того, як Gradle закінчить запуск ваших тестів JUnit. Тоді плагін Slack повинен мати можливість використовувати ці звіти.
Г. Демецький

1
Нарешті, ваша порада підштовхнула мене до правильного напрямку: мені довелося налаштувати правильний шлях пошуку файлів XML . Для мого Android проекту з декількома смаками продукту Gradle , такі види робіт: **/build/test-results/**/TEST-*.xml. Велике спасибі!!!
JJD
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.