Qual é a especificação de formato XML do JUnit suportada pelo Hudson?

183

Eu tenho o Hudson como servidor de integração contínua e quero usar a opção 'Publicar relatório de resultado do teste JUnit'. Mas eu não uso ferramentas xUnit para teste, em vez disso, tenho scripts de shell que executam testes e retornam resultados em formato simples. Estou pensando em criar um script que transforme esses resultados no formato JUnit. Então, eu sou interessante como o arquivo JUnit deve ficar?

krvladislav
fonte
Algum motivo para não usar o JUnit? Estes testes podem ser automatizados em uma variedade de formas através de uma variedade de ferramentas cmd, UI, etc ...
Aaron McIver
6
@AaronMcIver: Os scripts do shell são muito bons para executar testes (linguagem que não é Java). Como você usaria o JUnit para isso?
Ben Voigt
1
@BenVoigt Inicialmente, assumi que o OP tinha o Java envolvido e estava procurando ignorar a JUnit como equipamento de teste. Provavelmente não é esse o caso depois de revisar a pergunta. Parece que o code.google.com/p/shell2junit pode fornecer alguma utilidade ao OP após uma segunda análise.
Aaron McIver
1
Ao longo das linhas de shell2unit aqui é uma classe JAXB que eu criei que pode analisar / saída JUnit XML: gist.github.com/agentgt/8583649
Adam Gent

Respostas:

127

Fiz uma coisa semelhante há alguns meses atrás, e esse formato simples foi suficiente para Hudson aceitá-lo como um protocolo de teste:

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

Esta pergunta tem respostas com mais detalhes: Especificações. para saída XML JUnit

Anders Lindahl
fonte
Por favor, faça uma correção para esta resposta, porque xUnit plug-in rejeita o atributo 'nome de classe' e aceita apenas 'classe'
andho
10
eu tive o problema oposto. classfoi rejeitado e só classnamefuncionou.
Ryanbrainard
1
Isso começou a falhar para mim quando atualizei o plugin xUnit para 1.60. Eu descobri que o validador se tornou mais rigoroso e eu tive que adicionar <testsuite tests="(number of tests)">ex. <testsuite tests="10">.
precisa saber é o seguinte
2
Obrigado @KevinBrotcke, atualizarei a resposta para incluir esse atributo.
precisa
2
Observe também que, para que o Hudson organize seus testes por pacote / suíte, você deve especificar um pacote no atributo classname. ex: <testcase classname="foo.bar" name="ATest" /> Isso colocará a classe bar em um pacote foo no Jenkins, tornando sua coleção de testes mais organizada.
Jluzwick
90

Acabei de pegar o junit-4.xsd ao qual outras pessoas vincularam e usaram uma ferramenta chamada XMLSpear para converter o esquema em um arquivo XML em branco com as opções mostradas abaixo. Este é o resultado (ligeiramente limpo):

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

Alguns desses itens podem ocorrer várias vezes:

  • Só pode haver um testsuiteselemento, pois é assim que o XML funciona, mas pode haver vários testsuiteelementos dentro do testsuiteselemento.
  • Cada propertieselemento pode ter vários propertyfilhos.
  • Cada testsuiteelemento pode ter vários testcasefilhos.
  • Cada testcaseelemento pode ter vários error, failure, system-out, ou system-errcrianças.

XMLSpear opções

Todd Mazierski
fonte
1
Existe um documento que descreva os valores válidos de certos atributos, como o status do testcase ou o tipo de erro?
Eric Cope
1
@EricCope Posso recomendar o código-fonte svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/… . Basicamente, é apenas uma corda.
Sulthan
4
Por que as tags são duplicadas?
Nakilon
Definição do espelho: imgur.com/quneFJf alt: Rootelement: testsuites, Max recursive de...: 2, Max Repeat factor: 2, include optional elements: (sim = assinalada), include optional attributes? (sim = assinalada)
n611x007
1
@Nakilon É 2,5 anos de atraso, mas eu fixa-lo
bdesham
45

A resposta principal da pergunta Anders Lindahl refere-se a um arquivo xsd .

Pessoalmente, achei esse arquivo xsd também muito útil (não me lembro de como encontrei esse). Parece um pouco menos intimidador e, tanto quanto eu o usei, todos os elementos e atributos parecem ser reconhecidos por Jenkins (v1.451)

Porém, uma coisa: ao adicionar vários <failure ...elementos, apenas um foi mantido em Jenkins. Ao criar o arquivo xml, concatenarei agora todas as falhas em uma.


Atualização 2016-11 O link está quebrado agora. Uma alternativa melhor é esta página do cubic.org: formato de arquivo de relatório XML JUnit , onde foi feito um bom esforço para fornecer um exemplo documentado sensato . Exemplo e xsd são copiados abaixo, mas sua página parece bem melhor.


arquivo XML de amostra 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>

Arquivo 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>
parvus
fonte
Como você consegue que as falhas tenham uma boa aparência então? Gostaria de adicionar manualmente novos caracteres de linha, mas eles não aparecem no Jenkins.
Racionalcoder
Essa é uma desvantagem na minha abordagem. Lembro-me de lutar com isso um poço. Tente adicionar algo como & lt; br / & gt; - Esqueci como isso foi resolvido (e não estamos mais usando isso), mas isso parece algo que vale a pena tentar.
parvus
1
Eu encontrei uma maneira de contornar isso. Como estamos usando c ++, estou apenas relatando o número de falhas na mensagem de falha e usando o "Rastreamento de pilha" para relatar as falhas reais. Como o rastreamento de pilha é relatado a partir do texto dentro do corpo do elemento de falha, novas linhas são suportadas corretamente.
Racionalcoder
25

Não consegui encontrar nenhuma informação boa sobre isso, então fiz algumas tentativas e erros. Os seguintes atributos e campos (e somente estes) são reconhecidos por 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>

(Comecei com este documento XML de amostra e trabalhei de trás para frente.)

Ian
fonte
6

Estrutura básica Aqui está um exemplo de um arquivo de saída JUnit, mostrando um resultado ignorado e com falha, além de um único resultado passado.

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

Abaixo está a estrutura documentada de um relatório XML JUnit típico. Observe que um relatório pode conter 1 ou mais suíte de testes. Cada suíte de testes possui um conjunto de propriedades (registrando informações do ambiente). Cada suíte de teste também contém 1 ou mais casos de teste e cada caso de teste conterá um nó ignorado, com falha ou erro, se o teste não for aprovado. Se o caso de teste tiver passado, ele não conterá nenhum nó. Para mais detalhes de quais atributos são válidos para cada nó, consulte a seção "Esquema".

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

Existem vários esquemas para os resultados "JUnit" e "xUnit".

Observe que existem várias versões do esquema em uso pelo xunit-plugin Jenkins (a versão mais recente atual é junit-10.xsdque adiciona suporte ao formato Erlang / OTP Junit).

Algumas estruturas de teste, bem como os plug-ins de relatório no estilo "xUnit", também usam seu próprio segredo para gerar relatórios no estilo "xUnit"; eles podem não usar um esquema específico (leia: eles tentam, mas as ferramentas podem não validar contra nenhum um esquema). Unittests Python em Jenkins? fornece uma comparação rápida de várias dessas bibliotecas e pequenas diferenças entre os relatórios xml gerados.

dnozay
fonte
2

Boas respostas aqui sobre o uso de python: (existem várias maneiras de fazer isso) Unittests de Python no Jenkins?

IMHO, a melhor maneira é escrever testes unittest do python e instalar o pytest (algo como 'yum install pytest') para instalar o py.test. Em seguida, execute testes como este: 'py.test --junitxml results.xml test.py' . Você pode executar qualquer script python mais unittest e obter resultados xml do jUnit.

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

Na configuração de construção do jenkins, ações de pós-construção Adicione uma ação "Publicar relatório de resultado do teste JUnit" com result.xml e mais arquivos de resultado do teste que você produz.

gaoithe
fonte
2

Decidi postar uma nova resposta, porque algumas respostas existentes estão desatualizadas ou incompletas.

Primeiro de tudo: não há nada como JUnit XML Format Specification, simplesmente porque o JUnit não produz nenhum tipo de relatório XML ou HTML.

A própria geração de relatório XML vem da tarefa Ant JUnit / Maven Surefire Plugin / Gradle (o que você usar para executar seus testes). O formato do relatório XML foi introduzido pela Ant e posteriormente adaptado pela Maven (e Gradle).

Se alguém apenas precisa de um formato XML oficial, então:

  1. Existe um esquema para um relatório XML gerado pelo surefire e pode ser encontrado aqui: surefire-test-report.xsd .
  2. Para um XML gerado por formigas, existe um esquema de terceiros disponível aqui (mas pode estar um pouco desatualizado).

Espero que ajude alguém.

G. Demecki
fonte
Obrigado pelo seu esclarecimento. Estou tentando enviar resumos de teste JUnit de uma instância antiga do Jenkins 1.6 para o Slack - talvez você ajude? Onde eu colocaria um arquivo XML?
JJD 26/04
@JJD Desculpe, eu não entendo você. O que você quer dizer exatamente com esse arquivo XML ? Mas suponho que você já execute seus testes JUnit com ant / maven / gradle, sim? Se sim, essas ferramentas, após a execução dos testes, geram um bom relatório de resumo. A versão do Jenkins não importa aqui.
G. Demecki 2/17/17
Sim, minha compilação é executada via Gradle. Gostaria de enviar um resumo do teste JUnit para um canal Slack enquanto usava o Jenkins 1.6. Ao ler a discussão do GitHub, pensei em colocar um arquivo XML de configuração em algum lugar para permitir que o plug-in Slack receba o resumo do teste. Talvez eu entenda mal.
JJD 2/17
1
Verifique se os relatórios de teste gerados corretamente existem após o Gradle concluir o lançamento dos testes JUnit. O plugin Slack deve poder usar esses relatórios.
G. Demecki 4/17
1
Por fim, seus conselhos me levaram na direção certa: tive que configurar o caminho correto para procurar os arquivos XML . Para o meu Android projeto com vários sabores de produtos Gradle , as obras seguintes: **/build/test-results/**/TEST-*.xml. Muito obrigado!!!
JJD