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:
Tareq Alayan 2016-02-29 16:14:23 +02:00
parent 5fd82078ad
commit fa6acdcfd4
5 changed files with 111 additions and 0 deletions

View File

@ -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

View File

@ -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
========== ==========

View File

@ -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 ''

View File

@ -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
---------------------------------------------------- ----------------------------------------------------

View File

@ -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