Что такое спецификация формата JUnit XML, которую поддерживает Hudson?

183

У меня Hudson в качестве сервера непрерывной интеграции, и я хочу использовать опцию «Опубликовать отчет о результатах тестирования JUnit». Но я не использую инструменты xUnit для тестирования, вместо этого у меня есть сценарии оболочки, которые запускают тесты и возвращают результаты в простом формате. Я думаю сделать сценарий, который преобразует эти результаты в формат JUnit. Мне интересно, как должен выглядеть файл JUnit?

krvladislav
источник
Есть ли причина не использовать JUnit? Эти тесты можно автоматизировать различными способами с помощью различных инструментов cmd, пользовательского интерфейса и т. Д.
Аарон Макивер,
6
@AaronMcIver: Сценарии оболочки довольно хороши для запуска тестов (язык, который не является Java). Как бы вы использовали JUnit для этого?
Бен Фойгт
1
@BenVoigt Я изначально предполагал, что в OP был задействован Java, и намеревался обойти JUnit как проводку тестирования. Это, скорее всего, не так после рассмотрения вопроса. Похоже, что code.google.com/p/shell2junit может предоставить некоторое использование OP после второго просмотра.
Аарон Макивер
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>

На этот вопрос есть ответы с более подробной информацией: Спец. для вывода XML JUnit

Андерс Линдал
источник
Пожалуйста , сделайте поправку на этот ответ, потому что XUnit плагин отвергает атрибут «имя_класса» и принимает только «класс»
andho
10
У меня была противоположная проблема. classбыл отклонен и только classnameработал.
ryanbrainard
1
Это начало сбой для меня, когда я обновил плагин xUnit до 1.60. Я обнаружил, что валидатор стал более строгим, и мне пришлось добавить <testsuite tests="(number of tests)">экс. <testsuite tests="10">,
Кевин Бротке
2
Спасибо @KevinBrotcke, я обновлю ответ, чтобы включить этот атрибут.
Андерс Линдал
2
Также обратите внимание, что для того, чтобы Hudson организовал ваши тесты по пакетам / пакетам, вы должны указать пакет в атрибуте classname. пример: <testcase classname="foo.bar" name="ATest" /> это поместит класс bar в пакет foo для Jenkins, что сделает вашу тестовую коллекцию более организованной.
jluzwick
90

Я только что взял 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/… . В основном это просто строка.
Sulthan
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 года, но я исправил это
bdesham
45

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

Лично я нашел этот файл xsd также очень полезным (я не помню, как я нашел тот). Это выглядит немного менее пугающим, и, насколько я это использовал, все элементы и атрибуты, похоже, были распознаны Дженкинсом (v1.451)

Хотя одно: при добавлении нескольких <failure ...элементов в Jenkins оставался только один. При создании XML-файла я объединяю все ошибки в одном.


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


образец файла JUnit XML

<?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>
Парвус
источник
Как вы получаете неудачи, чтобы выглядеть красиво тогда? Я хотел бы добавить новые символы строки, но они не отображаются в Jenkins.
рациональный
Это недостаток моего подхода. Я помню, как боролся с этим колодцем. Попробуйте добавить что-то вроде & lt; br / & gt; - Я забыл, как это было решено (и мы больше не используем это), но это, кажется, стоит попробовать.
Парвус
1
Я нашел способ обойти это. Поскольку мы используем c ++, я просто сообщаю о количестве сбоев в сообщении о сбое и использую «Stack Trace» для сообщения о фактических сбоях. Поскольку трассировка стека сообщается из текста, содержащего тело элемента сбоя, новые строки поддерживаются корректно.
рациональный
25

Я не смог найти никакой хорошей информации по этому поводу, поэтому я сделал несколько проб и ошибок. Следующие атрибуты и поля (и только они) распознаются 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>

(Я начал с этого образца 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.

dnozay
источник
2

Хорошие ответы здесь об использовании Python: (есть много способов сделать это) Тесты Python в Jenkins?

ИМХО, лучший способ - написать тесты python unittest и установить pytest (что-то вроде 'yum install pytest'), чтобы установить py.test. Затем запустите такие тесты: «py.test --junitxml results.xml test.py» . Вы можете запустить любой скрипт Python unittest и получить результаты jUnit xml.

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

В конфигурации сборки jenkins Действия после сборки Добавьте действие «Опубликовать отчет о результатах теста JUnit» с помощью файла result.xml и других файлов результатов теста, которые вы создадите.

gaoithe
источник
2

Я решил опубликовать новый ответ, потому что некоторые существующие ответы устарели или неполны.

Прежде всего: ничего подобного нет JUnit XML Format Specification, просто потому, что JUnit не создает никаких отчетов XML или HTML.

Само генерирование XML-отчета происходит из задачи Ant JUnit / плагина Maven Surefire / 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