Skip to main content

Manipulates JUnit/xUnit Result XML files

Project description

https://github.com/weiwei/junitparser/workflows/build/badge.svg?branch=master https://codecov.io/gh/weiwei/junitparser/branch/master/graph/badge.svg?token=UotlfRXNnK

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.

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.

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:

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

Merge XML files

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

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.

Create XML with custom attributes

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

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.

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:

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

$ 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
$ 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>.
$ 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:

pytest test.py

Contribute

PRs are welcome!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

junitparser-2.8.0.tar.gz (12.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

junitparser-2.8.0-py2.py3-none-any.whl (11.4 kB view details)

Uploaded Python 2Python 3

File details

Details for the file junitparser-2.8.0.tar.gz.

File metadata

  • Download URL: junitparser-2.8.0.tar.gz
  • Upload date:
  • Size: 12.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for junitparser-2.8.0.tar.gz
Algorithm Hash digest
SHA256 a8d4290ab9fb93f2015e0dbef61ef93b7ea110e99099087a453f55c8e1481adf
MD5 e1f6c7bb8b9b3781b003181b7c4879e0
BLAKE2b-256 8c05b57fc7dec2120920d5bc15c74f340250642c9326d8c489d94c971a1a91cf

See more details on using hashes here.

File details

Details for the file junitparser-2.8.0-py2.py3-none-any.whl.

File metadata

  • Download URL: junitparser-2.8.0-py2.py3-none-any.whl
  • Upload date:
  • Size: 11.4 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for junitparser-2.8.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 e1a7d41e0c92ca032c46eed07a6268c478f90ec1f411ea8a76b69f245e57cfb6
MD5 abac758c50dcbdd1ac3f7a3566dc360d
BLAKE2b-256 7dfcd14dc065156780558af1fd306a883f552daf38031fc02c0b2822f3fecc6c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page