Skip to content

Commit a43ba78

Browse files
committed
Include <testsuites> root tag in generated XML
Fix #5477
1 parent 60a358f commit a43ba78

File tree

3 files changed

+38
-14
lines changed

3 files changed

+38
-14
lines changed

changelog/5477.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The XML file produced by ``--junitxml`` now correctly contain a ``<testsuites>`` root element.

src/_pytest/junitxml.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -657,18 +657,17 @@ def pytest_sessionfinish(self):
657657
)
658658
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
659659

660-
logfile.write(
661-
Junit.testsuite(
662-
self._get_global_properties_node(),
663-
[x.to_xml() for x in self.node_reporters_ordered],
664-
name=self.suite_name,
665-
errors=self.stats["error"],
666-
failures=self.stats["failure"],
667-
skipped=self.stats["skipped"],
668-
tests=numtests,
669-
time="%.3f" % suite_time_delta,
670-
).unicode(indent=0)
660+
suite_node = Junit.testsuite(
661+
self._get_global_properties_node(),
662+
[x.to_xml() for x in self.node_reporters_ordered],
663+
name=self.suite_name,
664+
errors=self.stats["error"],
665+
failures=self.stats["failure"],
666+
skipped=self.stats["skipped"],
667+
tests=numtests,
668+
time="%.3f" % suite_time_delta,
671669
)
670+
logfile.write(Junit.testsuites([suite_node]).unicode(indent=0))
672671
logfile.close()
673672

674673
def pytest_terminal_summary(self, terminalreporter):

testing/test_junitxml.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ def find_first_by_tag(self, tag):
4141
def _by_tag(self, tag):
4242
return self.__node.getElementsByTagName(tag)
4343

44+
@property
45+
def children(self):
46+
return [type(self)(x) for x in self.__node.childNodes]
47+
48+
@property
49+
def get_unique_child(self):
50+
children = self.children
51+
assert len(children) == 1
52+
return children[0]
53+
4454
def find_nth_by_tag(self, tag, n):
4555
items = self._by_tag(tag)
4656
try:
@@ -75,7 +85,7 @@ def tag(self):
7585
return self.__node.tagName
7686

7787
@property
78-
def next_siebling(self):
88+
def next_sibling(self):
7989
return type(self)(self.__node.nextSibling)
8090

8191

@@ -384,11 +394,11 @@ def test_fail():
384394
fnode = tnode.find_first_by_tag("failure")
385395
fnode.assert_attr(message="ValueError: 42")
386396
assert "ValueError" in fnode.toxml()
387-
systemout = fnode.next_siebling
397+
systemout = fnode.next_sibling
388398
assert systemout.tag == "system-out"
389399
assert "hello-stdout" in systemout.toxml()
390400
assert "info msg" not in systemout.toxml()
391-
systemerr = systemout.next_siebling
401+
systemerr = systemout.next_sibling
392402
assert systemerr.tag == "system-err"
393403
assert "hello-stderr" in systemerr.toxml()
394404
assert "info msg" not in systemerr.toxml()
@@ -1094,6 +1104,20 @@ def test_x(i):
10941104
assert failed == ["test_x[22]"]
10951105

10961106

1107+
def test_root_testsuites_tag(testdir):
1108+
testdir.makepyfile(
1109+
"""
1110+
def test_x():
1111+
pass
1112+
"""
1113+
)
1114+
_, dom = runandparse(testdir)
1115+
root = dom.get_unique_child
1116+
assert root.tag == "testsuites"
1117+
suite_node = root.get_unique_child
1118+
assert suite_node.tag == "testsuite"
1119+
1120+
10971121
def test_runs_twice(testdir):
10981122
f = testdir.makepyfile(
10991123
"""

0 commit comments

Comments
 (0)