File: README.rst

package info (click to toggle)
junitparser 4.0.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 324 kB
  • sloc: python: 2,166; xml: 81; makefile: 14
file content (297 lines) | stat: -rw-r--r-- 8,792 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
junitparser -- Pythonic JUnit/xUnit Result XML Parser
======================================================

.. image:: https://github.com/weiwei/junitparser/actions/workflows/ci.yml/badge.svg?branch=master
   :target: https://github.com/weiwei/junitparser/actions
.. image:: https://codecov.io/gh/weiwei/junitparser/branch/master/graph/badge.svg?token=UotlfRXNnK
   :target: https://codecov.io/gh/weiwei/junitparser

junitparser handles JUnit/xUnit Result XML files. Use it to parse and manipulate
existing Result XML files, or create new JUnit/xUnit result XMLs from scratch.

Features
--------

* Parse or modify existing JUnit/xUnit XML files.
* Parse or modify non-standard or customized JUnit/xUnit XML files, by monkey
  patching existing element definitions.
* Create JUnit/xUnit test results from scratch.
* Merge test result XML files.
* Specify XML parser. For example you can use lxml to speed things up.
* Invoke from command line, or `python -m junitparser`
* Python 2 and 3 support (As of Nov 2020, 1/4 of the users are still on Python
  2, so there is no plan to drop Python 2 support)

Note on version 2
-----------------

Version 2 improved support for pytest result XML files by fixing a few issues,
notably that there could be multiple <Failure> or <Error> entries. There is a
breaking change that ``TestCase.result`` is now a list instead of a single item.
If you are using this attribute, please update your code accordingly.

Installation
-------------

::

    pip install junitparser

Usage
-----

You should be relatively familiar with the Junit XML format. If not, run
``pydoc`` on the exposed classes and functions to see how it's structured.

Create Junit XML format reports from scratch
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You have some test result data, and you want to convert them into junit.xml
format.

.. code-block:: python

    from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error

    # Create cases
    case1 = TestCase('case1', 'class.name', 0.5) # params are optional
    case1.classname = "modified.class.name" # specify or change case attrs
    case1.result = [Skipped()] # You can have a list of results
    case2 = TestCase('case2')
    case2.result = [Error('Example error message', 'the_error_type')]

    # Create suite and add cases
    suite = TestSuite('suite1')
    suite.add_property('build', '55')
    suite.add_testcase(case1)
    suite.add_testcase(case2)
    suite.remove_testcase(case2)

    #Bulk add cases to suite
    case3 = TestCase('case3')
    case4 = TestCase('case4')
    suite.add_testcases([case3, case4])

    # Add suite to JunitXml
    xml = JUnitXml()
    xml.add_testsuite(suite)
    xml.write('junit.xml')

Read and manipulate existing JUnit/xUnit XML files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You have some existing junit.xml files, and you want to modify the content.

.. code-block:: python

    from junitparser import JUnitXml

    xml = JUnitXml.fromfile('/path/to/junit.xml')
    for suite in xml:
        # handle suites
        for case in suite:
            # handle cases
    xml.write() # Writes back to file

It is also possible to use a custom parser. For example lxml provides a plethora
of parsing options_. We can use them this way:

.. code-block:: python

    from lxml.etree import XMLParser, parse
    from junitparser import JUnitXml

    def parse_func(file_path):
        xml_parser = XMLParser(huge_tree=True)
        return parse(file_path, xml_parser)

    xml = JUnitXml.fromfile('/path/to/junit.xml', parse_func)
    # process xml...

.. _options: https://lxml.de/api/lxml.etree.XMLParser-class.html

Merge XML files
~~~~~~~~~~~~~~~

You have two or more XML files, and you want to merge them into one.

.. code-block:: python

    from junitparser import JUnitXml

    xml1 = JUnitXml.fromfile('/path/to/junit1.xml')
    xml2 = JUnitXml.fromfile('/path/to/junit2.xml')

    newxml = xml1 + xml2
    # Alternatively, merge in place
    xml1 += xml2

Note that it won't check for duplicate entries. You need to deal with them on
your own.

Schema Support
~~~~~~~~~~~~~~~

By default junitparser supports the schema of windyroad_, which is a relatively
simple schema.

.. _windyroad: https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd

Junitparser also support extra schemas:

.. code-block:: python

    from junitparser.xunit2 import TestCase, TestSuite, RerunFailure
    # These classes are redefined to support extra properties and attributes
    # of the xunit2 schema.
    suite = TestSuite("mySuite")
    suite.system_err = "System err" # xunit2 specific property
    case = TestCase("myCase")
    rerun_failure = RerunFailure("Not found", "404") # case property
    rerun_failure.stack_trace = "Stack"
    rerun_failure.system_err = "E404"
    rerun_failure.system_out = "NOT FOUND"
    case.add_interim_result(rerun_failure)

Currently supported schemas including:

- xunit2_, supported by pytest, Erlang/OTP, Maven Surefire, CppTest, etc.

.. _xunit2: https://github.com/jenkinsci/xunit-plugin/blob/xunit-2.3.2/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd

PRs are welcome to support more schemas.

Create XML with custom attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You want to use an attribute that is not supported by default.

.. code-block:: python

    from junitparser import TestCase, Attr, IntAttr, FloatAttr

    # Add the custom attribute
    TestCase.id = IntAttr('id')
    TestCase.rate = FloatAttr('rate')
    TestCase.custom = Attr('custom')
    case = TestCase()
    case.id = 123
    case.rate = 0.95
    case.custom = 'foobar'


Handling XML with custom element
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

There may be once in 1000 years you want to it this way, but anyways.
Suppose you want to add element CustomElement to TestCase.

.. code-block:: python

    from junitparser import Element, Attr, TestSuite

    # Create the new element by subclassing Element,
    # and add custom attributes to it.
    class CustomElement(Element):
        _tag = 'custom'
        foo = Attr()
        bar = Attr()

    testcase = TestCase()
    custom = CustomElement()
    testcase.append(custom)
    # To find a single sub-element:
    testcase.child(CustomElement)
    # To iterate over custom elements:
    for custom in testcase.iterchildren(CustomElement):
        ... # Do things with custom element

Handling custom XML attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Say you have some data stored in the XML as custom attributes and you want to
read them out:

.. code-block:: python

    from junitparser import TestCase, Attr, JUnitXml

    # Create the new element by subclassing Element or one of its child class,
    # and add custom attributes to it.
    class MyTestCase(TestCase):
        foo = Attr()

    xml = JUnitXml.fromfile('/path/to/junit.xml')
    for suite in xml:
        # handle suites
        for case in suite:
            my_case = MyTestCase.fromelem(case)
            print(my_case.foo)

Command Line
------------

.. code-block:: console

    $ junitparser --help
    usage: junitparser [-h] [-v] {merge} ...

    Junitparser CLI helper.

    positional arguments:
    {merge}        command
      merge        Merge Junit XML format reports with junitparser.
      verify       Return a non-zero exit code if one of the testcases failed or errored.

    optional arguments:
    -h, --help     show this help message and exit
    -v, --version  show program's version number and exit


.. code-block:: console

    $ junitparser merge --help
    usage: junitparser merge [-h] [--glob] paths [paths ...] output

    positional arguments:
      paths       Original XML path(s).
      output      Merged XML Path, setting to "-" will output console

    optional arguments:
      -h, --help  show this help message and exit
      --glob      Treat original XML path(s) as glob(s).
      --suite-name SUITE_NAME
                  Name added to <testsuites>.

.. code-block:: console

    $ junitparser verify --help
    usage: junitparser verify [-h] [--glob] paths [paths ...]

    positional arguments:
      paths       XML path(s) of reports to verify.

    optional arguments:
      -h, --help  show this help message and exit
      --glob      Treat original XML path(s) as glob(s).

Test
----

The tests are written with python ``unittest``, to run them, use
`pytest <https://pypi.org/project/pytest/>`_::

    pytest

If you get a failure like ``unsupported locale setting`` you may need to add
extra locales that the tests use. Refer to the steps used in the
`CI build workflow <.github/workflows/build.yml>`_::

        sudo locale-gen en_US.UTF-8
        sudo locale-gen de_DE.UTF-8
        sudo update-locale

Contribute
----------

PRs are welcome!