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.
This commit is contained in:
parent
5fd82078ad
commit
fa6acdcfd4
1
AUTHORS
1
AUTHORS
|
@ -75,6 +75,7 @@ Ronny Pfannschmidt
|
||||||
Ross Lawley
|
Ross Lawley
|
||||||
Ryan Wooden
|
Ryan Wooden
|
||||||
Samuele Pedroni
|
Samuele Pedroni
|
||||||
|
Tareq Alayan
|
||||||
Tom Viner
|
Tom Viner
|
||||||
Trevor Bekolay
|
Trevor Bekolay
|
||||||
Wouter van Ackooy
|
Wouter van Ackooy
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
tests.
|
tests.
|
||||||
Thanks `@kalekundert`_ for the complete PR (`#1441`_).
|
Thanks `@kalekundert`_ for the complete PR (`#1441`_).
|
||||||
|
|
||||||
|
* New Add ability to add global properties in the final xunit output file.
|
||||||
|
Thanks `@tareqalayan`_ for the complete PR `#1454`_).
|
||||||
|
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|
||||||
**Changes**
|
**Changes**
|
||||||
|
@ -28,10 +32,13 @@
|
||||||
.. _@milliams: https://github.com/milliams
|
.. _@milliams: https://github.com/milliams
|
||||||
.. _@novas0x2a: https://github.com/novas0x2a
|
.. _@novas0x2a: https://github.com/novas0x2a
|
||||||
.. _@kalekundert: https://github.com/kalekundert
|
.. _@kalekundert: https://github.com/kalekundert
|
||||||
|
.. _@tareqalayan: https://github.com/tareqalayan
|
||||||
|
|
||||||
.. _#1428: https://github.com/pytest-dev/pytest/pull/1428
|
.. _#1428: https://github.com/pytest-dev/pytest/pull/1428
|
||||||
.. _#1444: https://github.com/pytest-dev/pytest/pull/1444
|
.. _#1444: https://github.com/pytest-dev/pytest/pull/1444
|
||||||
.. _#1441: https://github.com/pytest-dev/pytest/pull/1441
|
.. _#1441: https://github.com/pytest-dev/pytest/pull/1441
|
||||||
|
.. _#1454: https://github.com/pytest-dev/pytest/pull/1454
|
||||||
|
|
||||||
|
|
||||||
2.9.1.dev1
|
2.9.1.dev1
|
||||||
==========
|
==========
|
||||||
|
|
|
@ -254,6 +254,7 @@ class LogXML(object):
|
||||||
], 0)
|
], 0)
|
||||||
self.node_reporters = {} # nodeid -> _NodeReporter
|
self.node_reporters = {} # nodeid -> _NodeReporter
|
||||||
self.node_reporters_ordered = []
|
self.node_reporters_ordered = []
|
||||||
|
self.global_properties = []
|
||||||
|
|
||||||
def finalize(self, report):
|
def finalize(self, report):
|
||||||
nodeid = getattr(report, 'nodeid', report)
|
nodeid = getattr(report, 'nodeid', report)
|
||||||
|
@ -273,9 +274,12 @@ class LogXML(object):
|
||||||
if key in self.node_reporters:
|
if key in self.node_reporters:
|
||||||
# TODO: breasks for --dist=each
|
# TODO: breasks for --dist=each
|
||||||
return self.node_reporters[key]
|
return self.node_reporters[key]
|
||||||
|
|
||||||
reporter = _NodeReporter(nodeid, self)
|
reporter = _NodeReporter(nodeid, self)
|
||||||
|
|
||||||
self.node_reporters[key] = reporter
|
self.node_reporters[key] = reporter
|
||||||
self.node_reporters_ordered.append(reporter)
|
self.node_reporters_ordered.append(reporter)
|
||||||
|
|
||||||
return reporter
|
return reporter
|
||||||
|
|
||||||
def add_stats(self, key):
|
def add_stats(self, key):
|
||||||
|
@ -361,7 +365,9 @@ class LogXML(object):
|
||||||
numtests = self.stats['passed'] + self.stats['failure']
|
numtests = self.stats['passed'] + self.stats['failure']
|
||||||
|
|
||||||
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
|
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
|
||||||
|
|
||||||
logfile.write(Junit.testsuite(
|
logfile.write(Junit.testsuite(
|
||||||
|
self._get_global_properties_node(),
|
||||||
[x.to_xml() for x in self.node_reporters_ordered],
|
[x.to_xml() for x in self.node_reporters_ordered],
|
||||||
name="pytest",
|
name="pytest",
|
||||||
errors=self.stats['error'],
|
errors=self.stats['error'],
|
||||||
|
@ -374,3 +380,18 @@ class LogXML(object):
|
||||||
def pytest_terminal_summary(self, terminalreporter):
|
def pytest_terminal_summary(self, terminalreporter):
|
||||||
terminalreporter.write_sep("-",
|
terminalreporter.write_sep("-",
|
||||||
"generated xml file: %s" % (self.logfile))
|
"generated xml file: %s" % (self.logfile))
|
||||||
|
|
||||||
|
def add_global_property(self, name, value):
|
||||||
|
self.global_properties.append((str(name), bin_xml_escape(value)))
|
||||||
|
|
||||||
|
def _get_global_properties_node(self):
|
||||||
|
"""Return a Junit node containing custom properties, if any.
|
||||||
|
"""
|
||||||
|
if self.global_properties:
|
||||||
|
return Junit.properties(
|
||||||
|
[
|
||||||
|
Junit.property(name=name, value=value)
|
||||||
|
for name, value in self.global_properties
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return ''
|
||||||
|
|
|
@ -193,6 +193,53 @@ This will add an extra property ``example_key="1"`` to the generated
|
||||||
Also please note that using this feature will break any schema verification.
|
Also please note that using this feature will break any schema verification.
|
||||||
This might be a problem when used with some CI servers.
|
This might be a problem when used with some CI servers.
|
||||||
|
|
||||||
|
LogXML: add_global_property
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. versionadded:: 2.10
|
||||||
|
|
||||||
|
If you want to add a properties node in the testsuite level, which may contains properties that are relevant
|
||||||
|
to all testcases you can use ``LogXML.add_global_properties``
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def log_global_env_facts(f):
|
||||||
|
|
||||||
|
if pytest.config.pluginmanager.hasplugin('junitxml'):
|
||||||
|
my_junit = getattr(pytest.config, '_xml', None)
|
||||||
|
|
||||||
|
my_junit.add_global_property('ARCH', 'PPC')
|
||||||
|
my_junit.add_global_property('STORAGE_TYPE', 'CEPH')
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures(log_global_env_facts)
|
||||||
|
def start_and_prepare_env():
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestMe:
|
||||||
|
def test_foo(self):
|
||||||
|
assert True
|
||||||
|
|
||||||
|
This will add a property node below the testsuite node to the generated xml:
|
||||||
|
|
||||||
|
.. code-block:: xml
|
||||||
|
|
||||||
|
<testsuite errors="0" failures="0" name="pytest" skips="0" tests="1" time="0.006">
|
||||||
|
<properties>
|
||||||
|
<property name="ARCH" value="PPC"/>
|
||||||
|
<property name="STORAGE_TYPE" value="CEPH"/>
|
||||||
|
</properties>
|
||||||
|
<testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>
|
||||||
|
</testsuite>
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is an experimental feature, and its interface might be replaced
|
||||||
|
by something more powerful and general in future versions. The
|
||||||
|
functionality per-se will be kept.
|
||||||
|
|
||||||
Creating resultlog format files
|
Creating resultlog format files
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -783,3 +783,38 @@ def test_fancy_items_regression(testdir):
|
||||||
u'test_fancy_items_regression test_pass'
|
u'test_fancy_items_regression test_pass'
|
||||||
u' test_fancy_items_regression.py',
|
u' test_fancy_items_regression.py',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_global_properties(testdir):
|
||||||
|
path = testdir.tmpdir.join("test_global_properties.xml")
|
||||||
|
log = LogXML(str(path), None)
|
||||||
|
from _pytest.runner import BaseReport
|
||||||
|
|
||||||
|
class Report(BaseReport):
|
||||||
|
sections = []
|
||||||
|
nodeid = "test_node_id"
|
||||||
|
|
||||||
|
log.pytest_sessionstart()
|
||||||
|
log.add_global_property('foo', 1)
|
||||||
|
log.add_global_property('bar', 2)
|
||||||
|
log.pytest_sessionfinish()
|
||||||
|
|
||||||
|
dom = minidom.parse(str(path))
|
||||||
|
|
||||||
|
properties = dom.getElementsByTagName('properties')
|
||||||
|
|
||||||
|
assert (properties.length == 1), "There must be one <properties> node"
|
||||||
|
|
||||||
|
property_list = dom.getElementsByTagName('property')
|
||||||
|
|
||||||
|
assert (property_list.length == 2), "There most be only 2 property nodes"
|
||||||
|
|
||||||
|
expected = {'foo': '1', 'bar': '2'}
|
||||||
|
actual = {}
|
||||||
|
|
||||||
|
for p in property_list:
|
||||||
|
k = str(p.getAttribute('name'))
|
||||||
|
v = str(p.getAttribute('value'))
|
||||||
|
actual[k] = v
|
||||||
|
|
||||||
|
assert actual == expected
|
||||||
|
|
Loading…
Reference in New Issue