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

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


person Vlad Krylov    schedule 07.02.2011    source источник
comment
Есть ли причина не использовать JUnit? Эти тесты можно автоматизировать различными способами с помощью различных инструментов cmd, UI и т. Д.   -  person Aaron McIver    schedule 07.02.2011
comment
@AaronMcIver: сценарии оболочки довольно хорошо справляются с запуском тестов на (языке, отличном от Java). Как бы вы для этого использовали JUnit?   -  person Ben Voigt    schedule 08.11.2011
comment
@BenVoigt Я изначально предполагал, что в OP задействована Java, и хотел обойти JUnit в качестве средства тестирования. После рассмотрения вопроса, скорее всего, это не так. Похоже, что code.google.com/p/shell2junit может быть полезен OP после второго взгляда.   -  person Aaron McIver    schedule 08.11.2011
comment
Вдоль строк shell2unit находится созданный мной класс JAXB, который может анализировать / выводить JUnit XML: gist.github .com / agentgt / 8583649   -  person Adam Gent    schedule 23.01.2014


Ответы (8)


Я сделал то же самое несколько месяцев назад, и оказалось, что этого простого формата было достаточно, чтобы Хадсон принял его в качестве протокола тестирования:

<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. для вывода XML JUnit

person Anders Lindahl    schedule 07.02.2011
comment
Пожалуйста, внесите исправление в этот ответ, потому что плагин xunit отклоняет атрибут classname и принимает только class - person andho; 05.11.2011
comment
у меня была противоположная проблема. class был отклонен, и только classname сработало. - person ryanbrainard; 09.02.2012
comment
мне нужно создать этот файл вручную, но я не знаю, где его создать в файловой системе относительно домашнего каталога jenkins? - person mert inan; 25.07.2012
comment
вы можете создать его где угодно - вы должны указать относительный путь в рабочем каталоге в поле конфигурации jenkins jUnit publish jUnit result. - person blabla999; 07.11.2012
comment
У меня это начало терпеть неудачу, когда я обновил плагин xUnit до 1.60. Я обнаружил, что валидатор стал строже, и мне пришлось добавить <testsuite tests="(number of tests)"> ex. <testsuite tests="10">. - person Kevin Brotcke; 07.06.2013
comment
Спасибо @KevinBrotcke, я обновлю ответ, включив этот атрибут. - person Anders Lindahl; 07.06.2013
comment
Также обратите внимание, что для того, чтобы Хадсон организовал ваши тесты по пакетам / комплектам, вы должны указать пакет в атрибуте classname. Пример: <testcase classname="foo.bar" name="ATest" /> Это поместит класс bar в пакет foo на Jenkins, что сделает вашу тестовую коллекцию более организованной. - person jluzwick; 11.09.2013
comment
@andho Этот вопрос касается стандартного парсера отчетов об испытаниях, который понимает Хадсон / Дженкинс. Этот парсер предназначен для JUnit. Есть отдельный плагин для разбора отчетов в формате xUnit. xUnit и JUnit - это 2 разных формата. - person Alex Skrypnyk; 06.05.2015
comment
С точки зрения правильности (_1 _ / _ 2_) вы можете найти мой ответ ценным, поскольку я подготовил его методом проб и ошибок. Он также более полный - он включает в себя, как включить ваши STDOUT и STDERR в результаты. - person Ian; 16.12.2019

Я только что взял 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, но внутри testsuites элемента может быть несколько testsuite элементов.
  • У каждого properties элемента может быть несколько property дочерних элементов.
  • У каждого testsuite элемента может быть несколько testcase дочерних элементов.
  • У каждого testcase элемента может быть несколько error, failure, system-out или system-err дочерних элементов.

Параметры XMLSpear

person Todd Mazierski    schedule 13.03.2012
comment
Есть ли документ, в котором описаны допустимые значения определенных атрибутов, таких как статус тестового набора или тип ошибки? - person Eric Cope; 28.06.2012
comment
@EricCope Я могу порекомендовать посмотреть исходный код svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/. В основном это просто строка. - person Sulthan; 14.11.2012
comment
Почему дублируются теги? - person Nakilon; 26.09.2014
comment
зеркало настроек: imgur.com/quneFJf alt: Rootelement: testsuites, Max recursive de...: 2, Max Repeat factor: 2, include optional elements: (да = отмечен), include optional attributes: (да = отмечен) - person n611x007; 22.04.2015
comment
Могу ли я иметь более одного ‹testsuites› в XML-документе, и будет ли он по-прежнему соответствовать JUnit? - person nirvanaswap; 30.06.2016
comment
@nirvanaswap см. en.wikipedia.org/wiki/Root_element Каждый XML-документ имеет только один корень элемент. - person Janus Troelsen; 15.07.2016
comment
@Nakilon Уже 2,5 года поздно, но я исправил - person bdesham; 11.05.2017
comment
@bdesham, ха-ха, я сменил две работы и уже даже не работаю QA) - person Nakilon; 11.05.2017

главный ответ на вопрос Андерс Линдал ссылается на файл xsd.

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

Одно но: при добавлении нескольких элементов <failure ... в Jenkins остался только один. При создании файла 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>

Файл XSD JUnit

<?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>
person parvus    schedule 23.02.2012
comment
Как же тогда добиться того, чтобы неудачники хорошо выглядели? Я хотел бы вручную добавить символы новой строки, но они не отображаются в Jenkins. - person rationalcoder; 07.08.2015
comment
Это недостаток моего подхода. Я хорошо помню, как боролся с этим. Попробуйте добавить что-то вроде br / - я забыл, как это было решено (и мы больше этим не пользуемся), но, похоже, это стоит попробовать. - person parvus; 13.08.2015
comment
Я нашел способ обойти это. Поскольку мы используем C ++, я просто указываю количество сбоев в сообщении об ошибке и использую трассировку стека для отчета о фактических сбоях. Поскольку трассировка стека сообщается из текста в теле элемента сбоя, новые строки поддерживаются правильно. - person rationalcoder; 13.08.2015

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

person Ian    schedule 30.10.2014

Базовая структура Вот пример выходного файла 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>
person Nayana Adassuriya    schedule 09.05.2016

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

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

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

Если кому-то нужен только официальный формат XML, то:

  1. Существует схема для XML-отчета, созданного maven surefire, и ее можно найти здесь: surefire-test-report.xsd.
  2. Для сгенерированного муравьями XML есть сторонняя схема, доступная здесь (но, возможно, он немного устарел).

Надеюсь, это кому-нибудь поможет.

person G. Demecki    schedule 22.11.2016
comment
Благодарим Вас за разъяснения. Я пытаюсь отправить сводки тестов JUnit из старого экземпляра Jenkins 1.6 в Slack - может быть, поможете? Где бы я поместил такой XML-файл? - person JJD; 26.04.2017
comment
@JJD Извини, я тебя не понимаю. Что именно вы подразумеваете под таким XML-файлом? Но я предполагаю, что вы уже запускали свои тесты JUnit с помощью ant / maven / gradle, да? Если да, то эти инструменты после выполнения тестов генерируют красивый сводный отчет. Версия Дженкинса здесь не имеет значения. - person G. Demecki; 02.05.2017
comment
Да, моя сборка работает через Gradle. Я хотел бы отправить сводку теста JUnit на канал Slack при использовании Jenkins 1.6. Читая обсуждение на GitHub, я подумал, что мне нужно где-то разместить конфигурационный XML-файл, чтобы плагин Slack мог получить результаты теста. Может я неправильно понял. - person JJD; 02.05.2017
comment
Убедитесь, что правильно сгенерированные отчеты о тестах существуют после того, как Gradle завершит запуск ваших тестов JUnit. Тогда плагин Slack сможет использовать эти отчеты. - person G. Demecki; 04.05.2017
comment
Наконец, ваш совет подтолкнул меня в правильном направлении: мне пришлось настроить правильный путь для поиска файлов XML. Для моего проекта Android с несколькими вариантами продуктов Gradle работает следующее: **/build/test-results/**/TEST-*.xml. Большое тебе спасибо!!! - person JJD; 09.05.2017

Существует несколько схем для результатов "JUnit" и "xUnit".

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

Некоторые платформы тестирования, а также плагины для создания отчетов в стиле «xUnit» также используют свой секретный соус для создания отчетов в стиле «xUnit», которые могут не использовать определенную схему (пожалуйста, прочтите: они пытаются, но инструменты могут не проверять на соответствие каким-либо одна схема). Модульные тесты Python в Jenkins? дает быстрое сравнение нескольких из этих библиотек и небольшие различия между XML отчеты созданы.

person dnozay    schedule 02.03.2016

Хорошие ответы об использовании 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 и других файлов результатов тестирования, которые вы создаете.

person gaoithe    schedule 06.05.2016