Skip to content

Commit 20a3418

Browse files
committed
Introduce record_testsuite_property fixture
This exposes the functionality introduced in fa6acdc as a session-scoped fixture. Plugins that want to remain compatible with the `xunit2` standard should use this fixture instead of `record_property`. Fix pytest-dev#5202
1 parent 3f5622c commit 20a3418

File tree

5 files changed

+88
-24
lines changed

5 files changed

+88
-24
lines changed

changelog/5202.feature.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
New ``record_testsuite_property`` session-scoped fixture allows users to log ``<property>`` tags at the ``testsuite``
2+
level with the ``junitxml`` plugin.
3+
4+
The generated XML is compatible with the latest xunit standard, contrary to
5+
the properties recorded by ``record_property`` and ``record_xml_attribute``.

doc/en/reference.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,14 @@ record_property
424424

425425
.. autofunction:: _pytest.junitxml.record_property()
426426

427+
428+
record_testsuite_property
429+
~~~~~~~~~~~~~~~~~~~~~~~~~
430+
431+
**Tutorial**: :ref:`record_testsuite_property example`.
432+
433+
.. autofunction:: _pytest.junitxml.record_testsuite_property()
434+
427435
caplog
428436
~~~~~~
429437

doc/en/usage.rst

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -522,9 +522,7 @@ Will result in:
522522
523523
.. warning::
524524

525-
``record_property`` is an experimental feature and may change in the future.
526-
527-
Also please note that using this feature will break any schema verification.
525+
Please note that using this feature will break schema verifications for the latest JUnitXML schema.
528526
This might be a problem when used with some CI servers.
529527

530528
record_xml_attribute
@@ -587,32 +585,31 @@ Instead, this will add an attribute ``assertions="REQ-1234"`` inside the generat
587585
</xs:complexType>
588586
</xs:element>
589587
590-
LogXML: add_global_property
591-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
588+
.. warning::
592589

593-
.. versionadded:: 3.0
590+
Please note that using this feature will break schema verifications for the latest JUnitXML schema.
591+
This might be a problem when used with some CI servers.
594592

595-
If you want to add a properties node in the testsuite level, which may contains properties that are relevant
596-
to all testcases you can use ``LogXML.add_global_properties``
597593

598-
.. code-block:: python
594+
.. _record_testsuite_property example:
599595

600-
import pytest
596+
record_testsuite_property
597+
^^^^^^^^^^^^^^^^^^^^^^^^^
601598

599+
.. versionadded:: 4.5
602600

603-
@pytest.fixture(scope="session")
604-
def log_global_env_facts(f):
601+
If you want to add a properties node at the ``testsuite`` level, which may contains properties that are relevant
602+
to all tests, you can use the ``record_testsuite_property`` session-scoped fixture:
605603

606-
if pytest.config.pluginmanager.hasplugin("junitxml"):
607-
my_junit = getattr(pytest.config, "_xml", None)
604+
.. code-block:: python
608605
609-
my_junit.add_global_property("ARCH", "PPC")
610-
my_junit.add_global_property("STORAGE_TYPE", "CEPH")
606+
import pytest
611607
612608
613-
@pytest.mark.usefixtures(log_global_env_facts.__name__)
614-
def start_and_prepare_env():
615-
pass
609+
@pytest.fixture(scope="session", autouse=True)
610+
def log_global_env_facts(record_testsuite_property):
611+
record_testsuite_property("ARCH", "PPC")
612+
record_testsuite_property("STORAGE_TYPE", "CEPH")
616613
617614
618615
class TestMe(object):
@@ -623,19 +620,17 @@ This will add a property node below the testsuite node to the generated xml:
623620

624621
.. code-block:: xml
625622
626-
<testsuite errors="0" failures="0" name="pytest" skips="0" tests="1" time="0.006">
623+
<testsuite errors="0" failures="0" name="pytest" skipped="0" tests="1" time="0.006">
627624
<properties>
628625
<property name="ARCH" value="PPC"/>
629626
<property name="STORAGE_TYPE" value="CEPH"/>
630627
</properties>
631628
<testcase classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>
632629
</testsuite>
633630
634-
.. warning::
635631
636-
This is an experimental feature, and its interface might be replaced
637-
by something more powerful and general in future versions. The
638-
functionality per-se will be kept.
632+
The generated XML is compatible with the latest ``xunit`` standard.
633+
639634

640635
Creating resultlog format files
641636
----------------------------------------------------

src/_pytest/junitxml.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,30 @@ def add_attr_noop(name, value):
334334
return attr_func
335335

336336

337+
@pytest.fixture(scope="session")
338+
def record_testsuite_property(request):
339+
"""
340+
Records a new ``<property>`` tag as child of the root ``<testsuite>``. This is suitable to
341+
writing global information regarding the entire test suite, and is compatible with ``xunit2`` JUnit family.
342+
343+
This is a ``session``-scoped fixture which returns a callable with ``(name, value)``. Example:
344+
345+
.. code-block:: python
346+
347+
def test_foo(record_testsuite_property):
348+
record_testsuite_property("ARCH", "PPC")
349+
record_testsuite_property("STORAGE_TYPE", "CEPH")
350+
"""
351+
352+
def record_func(name, value):
353+
"""noop function in case --junitxml was not passed in the command-line"""
354+
355+
xml = getattr(request.config, "_xml", None)
356+
if xml is not None:
357+
record_func = xml.add_global_property # noqa
358+
return record_func
359+
360+
337361
def pytest_addoption(parser):
338362
group = parser.getgroup("terminal reporting")
339363
group.addoption(
@@ -433,6 +457,7 @@ def __init__(
433457
self.node_reporters = {} # nodeid -> _NodeReporter
434458
self.node_reporters_ordered = []
435459
self.global_properties = []
460+
436461
# List of reports that failed on call but teardown is pending.
437462
self.open_reports = []
438463
self.cnt_double_fail_tests = 0

testing/test_junitxml.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,37 @@ class Report(BaseReport):
12211221
), "The URL did not get written to the xml"
12221222

12231223

1224+
def test_record_testsuite_property(testdir):
1225+
testdir.makepyfile(
1226+
"""
1227+
def test_func1(record_testsuite_property):
1228+
record_testsuite_property("stats", "all good")
1229+
1230+
def test_func2(record_testsuite_property):
1231+
record_testsuite_property("stats", "almost good")
1232+
"""
1233+
)
1234+
result, dom = runandparse(testdir)
1235+
assert result.ret == 0
1236+
node = dom.find_first_by_tag("testsuite")
1237+
properties_node = node.find_first_by_tag("properties")
1238+
p1_node = properties_node.find_nth_by_tag("property", 0)
1239+
p2_node = properties_node.find_nth_by_tag("property", 1)
1240+
p1_node.assert_attr(name="stats", value="all good")
1241+
p2_node.assert_attr(name="stats", value="almost good")
1242+
1243+
1244+
def test_record_testsuite_property_junit_disabled(testdir):
1245+
testdir.makepyfile(
1246+
"""
1247+
def test_func1(record_testsuite_property):
1248+
record_testsuite_property("stats", "all good")
1249+
"""
1250+
)
1251+
result = testdir.runpytest()
1252+
assert result.ret == 0
1253+
1254+
12241255
@pytest.mark.parametrize("suite_name", ["my_suite", ""])
12251256
def test_set_suite_name(testdir, suite_name):
12261257
if suite_name:

0 commit comments

Comments
 (0)