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.
Install & Compatibility
Where this runs
tested against v5.0.1 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.925 runs
installs and imports cleanly · install 0.0s · import 0.091s · 17.9MB
glibcpy 3.10–3.925 runs
installs and imports cleanly · install 1.6s · import 0.078s · 18MB
16MB installed
● package 16MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
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
This quickstart demonstrates how to create a JUnit XML report from scratch, defining test cases with different results (skipped, successful, error), grouping them into a test suite, and then writing the complete report to an XML file. The `pretty=True` argument ensures human-readable formatting.
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)
Errors
Common errors & fixes
ModuleNotFoundError: No module named 'junitparser'
The 'junitparser' library is not installed in your Python environment.
fixRun `pip install junitparser` in your terminal to install the library.
TypeError: 'TestCase' object is not iterable
This error often occurs when attempting to iterate directly over a `TestCase` object, or if the JUnit XML file structure does not match expectations (e.g., missing a top-level `<testsuites>` tag when iterating an XML object expecting multiple suites).
fixEnsure you are iterating over a `JUnitXml` object to get `TestSuite` objects, and then iterating over `TestSuite` objects to get `TestCase` objects. If your XML has only one suite and no `<testsuites>` root, `JUnitXml.fromfile()` will parse it correctly, but direct iteration assumes a root of `TestSuite` or `JUnitXml` containing suites. For example: `xml = JUnitXml.fromfile('report.xml'); for suite in xml: for case in suite: pass`. AttributeError: 'list' object has no attribute 'message' (or similar when accessing TestCase.result)
In `junitparser` version 2.0.0 and later, the `TestCase.result` attribute was changed from a single `Result` object to a list of `Result` objects to accommodate multiple failures/errors per test case. Code written for older versions will fail when trying to access attributes directly on this list.
fixUpdate your code to iterate over the `TestCase.result` list to access individual result objects and their attributes. For example, change `case.result.message` to `for r in case.result: print(r.message)`.
junitparser.JUnitXmlError: Missing file argument.
This error occurs when attempting to write a JUnit XML object to a file without providing a target file path or a file-like object.
fixProvide a valid file path (string or `Path` object) or a file-like object when calling `write_xml()` or `JUnitXml.write()`. For example: `xml.write('output.xml')` or `write_xml(xml, 'output.xml')`. Audit
Dependencies
lxmloptionalOptional dependency for faster XML parsing performance.