Skip to content

Commit dbbd9fb

Browse files
committed
junitxml: add properties node in testsuite level
The commit allow users to add a properties node in testsuite level see example below: <testsuite errors="0" failures="0" name="pytest" skips="1" tests="1" time="11.824"> <properties> <property name="ARCH" value="PPC"/> <property name="OS" value="RHEL 7.2"/> <property name="TestPlanURL" value="https://url.."/> <property name="Automated" value="True"/> </properties> <testcase classname="git.....> </testcase> </testsuite> The current situation is that properties node can be added to every testcase node. However, sometimes we need some global properties that applies to all testcases and give better description for the testsuite itself.
1 parent 0cacdef commit dbbd9fb

File tree

4 files changed

+66
-1
lines changed

4 files changed

+66
-1
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Ronny Pfannschmidt
7474
Ross Lawley
7575
Ryan Wooden
7676
Samuele Pedroni
77+
Tareq Alayan
7778
Tom Viner
7879
Trevor Bekolay
7980
Wouter van Ackooy

CHANGELOG.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
for a fixture (to solve the funcarg-shadowing-fixture problem).
1212
Thanks `@novas0x2a`_ for the complete PR (`#1444`_).
1313

14-
*
14+
* New Add ability to add global properties in the final xunit output file.
15+
Thanks `@tareqalayan`_ for the complete PR `#1446`_).
16+
1517

1618
*
1719

_pytest/junitxml.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ def __init__(self, logfile, prefix):
254254
], 0)
255255
self.node_reporters = {} # nodeid -> _NodeReporter
256256
self.node_reporters_ordered = []
257+
self.global_properties = []
257258

258259
def finalize(self, report):
259260
nodeid = getattr(report, 'nodeid', report)
@@ -273,9 +274,12 @@ def node_reporter(self, report):
273274
if key in self.node_reporters:
274275
# TODO: breasks for --dist=each
275276
return self.node_reporters[key]
277+
276278
reporter = _NodeReporter(nodeid, self)
279+
277280
self.node_reporters[key] = reporter
278281
self.node_reporters_ordered.append(reporter)
282+
279283
return reporter
280284

281285
def add_stats(self, key):
@@ -361,7 +365,9 @@ def pytest_sessionfinish(self):
361365
numtests = self.stats['passed'] + self.stats['failure']
362366

363367
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
368+
364369
logfile.write(Junit.testsuite(
370+
self._get_global_properties_node(),
365371
[x.to_xml() for x in self.node_reporters_ordered],
366372
name="pytest",
367373
errors=self.stats['error'],
@@ -374,3 +380,18 @@ def pytest_sessionfinish(self):
374380
def pytest_terminal_summary(self, terminalreporter):
375381
terminalreporter.write_sep("-",
376382
"generated xml file: %s" % (self.logfile))
383+
384+
def add_global_property(self, name, value):
385+
self.global_properties.append((str(name), bin_xml_escape(value)))
386+
387+
def _get_global_properties_node(self):
388+
"""Return a Junit node containing custom properties, if any.
389+
"""
390+
if self.global_properties:
391+
return Junit.properties(
392+
[
393+
Junit.property(name=name, value=value)
394+
for name, value in self.global_properties
395+
]
396+
)
397+
return ''

testing/test_junitxml.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,3 +783,44 @@ def test_pass():
783783
u'test_fancy_items_regression test_pass'
784784
u' test_fancy_items_regression.py',
785785
]
786+
787+
788+
def test_global_properties(testdir):
789+
path = testdir.tmpdir.join("test_global_properties.xml")
790+
log = LogXML(str(path), None)
791+
from _pytest.runner import BaseReport
792+
793+
class Report(BaseReport):
794+
sections = []
795+
nodeid = "test_node_id"
796+
797+
log.pytest_sessionstart()
798+
log.add_global_property('foo', 1)
799+
log.add_global_property('bar', 2)
800+
log.pytest_sessionfinish()
801+
802+
dom = minidom.parse(str(path))
803+
804+
properties = dom.getElementsByTagName('properties')
805+
806+
assert (properties.length == 1), "There must be one <properties> node"
807+
808+
property_list = dom.getElementsByTagName('property')
809+
810+
assert (property_list.length == 2), "There most be only 2 property nodes"
811+
812+
expected = {'foo': '1', 'bar': '2'}
813+
actual = {}
814+
815+
for p in property_list:
816+
k = str(p.getAttribute('name'))
817+
v = str(p.getAttribute('value'))
818+
actual[k] = v
819+
820+
assert (len(actual.keys()) == len(expected.keys()))
821+
822+
for key, value in list(actual.items()):
823+
if key in expected and expected[k] == v:
824+
continue
825+
else:
826+
assert False, "Actual : %s != expected: %s" % (actual, expected)

0 commit comments

Comments
 (0)