JUnitparser
JUnitparser is a Python library designed to parse and manipulate JUnit/xUnit Result XML files. It allows users to read and modify existing XML reports or create new ones from scratch. The library is actively maintained, with its current version being 4.0.2, and has a consistent release cadence addressing new features and bug fixes.
Warnings
- breaking In version 4.0.0, several arguments for writing methods were renamed or changed from positional to keyword. `filepath` in `write_xml`, `TestSuite.write`, and `JUnitXml.write` is now `file_or_filename`. `pretty` became a keyword-only argument. `to_console` was removed; use `sys.stdout` for console output. `JUnitXml.fromfile`'s `filepath` argument is now `file`.
- breaking As of version 4.0.0, `JUnitXml.fromfile`, `JUnitXml.fromstring`, and `JUnitXml.fromroot` methods consistently return a `JUnitXml` instance. Previously, if the root element was `<testsuite>`, it might return a `TestSuite` instance directly.
- breaking In version 3.0.0, support for Python 2 was completely dropped.
- breaking In version 2.0.0, the `TestCase.result` attribute was changed from a single object to a list of result objects (e.g., `Failure`, `Skipped`, `Error`, `Success`). This allows for multiple outcomes, particularly relevant for pytest reports.
- gotcha The `TestCase.result` setter in version 4.0.0 and above now throws a `ValueError` if an invalid type is assigned, whereas earlier versions might have silently ignored it.
Install
-
pip install junitparser
Imports
- JUnitXml
from junitparser import JUnitXml
- TestCase
from junitparser import TestCase
- TestSuite
from junitparser import TestSuite
- Skipped
from junitparser import Skipped
- Error
from junitparser import Error
Quickstart
import os
from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error
# Create some test cases
case1 = TestCase('test_feature_a', 'module.submodule', 1.23)
case1.result = Skipped('Not implemented yet')
case2 = TestCase('test_feature_b', 'module.submodule', 0.45)
# By default, a test case is considered successful if no result is explicitly set or if it's an instance of Success
case3 = TestCase('test_feature_c', 'another_module', 2.00)
case3.result = Error('Connection refused', 'NetworkError')
# Create a test suite and add cases
suite = TestSuite('MyTestSuite')
suite.add_testcase(case1)
suite.add_testcase(case2)
suite.add_testcase(case3)
# Create a JUnitXml object and add the suite
xml = JUnitXml()
xml.add_testsuite(suite)
# Define output path
output_filename = os.environ.get('JUNIT_OUTPUT_PATH', 'report.xml')
# Write the XML to a file
xml.write(output_filename, pretty=True)
print(f"Generated JUnit XML report at: {output_filename}")
# Example of reading and modifying an existing XML (requires a dummy file)
# with open('existing_report.xml', 'w') as f:
# f.write('<testsuites><testsuite name="Existing" tests="1" failures="0"><testcase name="passing_test" /></testsuite></testsuites>')
# existing_xml = JUnitXml.fromfile('existing_report.xml')
# for ts in existing_xml:
# ts.name = "Modified Existing Suite"
# existing_xml.write('modified_report.xml', pretty=True)