Skip to content

Instantly share code, notes, and snippets.

@cgoldberg
Last active January 10, 2024 19:19
Show Gist options
  • Star 34 You must be signed in to star a gist
  • Fork 24 You must be signed in to fork a gist
  • Save cgoldberg/4320815 to your computer and use it in GitHub Desktop.
Save cgoldberg/4320815 to your computer and use it in GitHub Desktop.
Merge multiple JUnit XML results files into a single results file.
#!/usr/bin/env python
"""Merge multiple JUnit XML results files into a single results file."""
# MIT License
#
# Copyright (c) 2012 Corey Goldberg
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os
import sys
import xml.etree.ElementTree as ET
"""Merge multiple JUnit XML files into a single results file.
Output dumps to stdout.
example usage:
$ python merge_junit_results.py results1.xml results2.xml > results.xml
"""
def main():
args = sys.argv[1:]
if not args:
usage()
sys.exit(2)
if '-h' in args or '--help' in args:
usage()
sys.exit(2)
merge_results(args[:])
def merge_results(xml_files):
failures = 0
tests = 0
errors = 0
time = 0.0
cases = []
for file_name in xml_files:
tree = ET.parse(file_name)
test_suite = tree.getroot()
failures += int(test_suite.attrib['failures'])
tests += int(test_suite.attrib['tests'])
errors += int(test_suite.attrib['errors'])
time += float(test_suite.attrib['time'])
cases.append(test_suite.getchildren())
new_root = ET.Element('testsuite')
new_root.attrib['failures'] = '%s' % failures
new_root.attrib['tests'] = '%s' % tests
new_root.attrib['errors'] = '%s' % errors
new_root.attrib['time'] = '%s' % time
for case in cases:
new_root.extend(case)
new_tree = ET.ElementTree(new_root)
ET.dump(new_tree)
def usage():
this_file = os.path.basename(__file__)
print 'Usage: %s results1.xml results2.xml' % this_file
if __name__ == '__main__':
main()
@jswetzen
Copy link

This is very useful! Can I use this commercially? If so, with what kind of license?

@spolischook
Copy link

@yodaqua
Copy link

yodaqua commented Dec 19, 2017

Hi there

This code drops the name attribute of a test suite when merging.

I've added the name attribute to the code, however the merged reports, has as it's name, the last test suite.

This means that all the test suites test cases are reported as part of only one test suite (the last test suite, or, in this case the last Junit XML report).

I'm working on a fix. As soon as I have it I'll send merge request.

Thanks

@Malar88
Copy link

Malar88 commented Sep 29, 2020

Hi there, if i use this script, always get failures , test keyerrors although the sample result1.xml and result2.xml has these attributes.

failures += int(test_suite.attrib['failures'])
KeyError: 'failures

I am new to python,Am i missing anything?
Please guide

@bitti
Copy link

bitti commented Jan 10, 2024

I don't think this snipped is maintained anymore, but junitparser should be a well suited replacement, since it also has a merge command.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment