Renamed the fixture record_xml_property to record_property and adapted logic so that the properties are passed to the TestReport object and thus allow compatibility with pytest-xdist.
This commit is contained in:
parent
97bb6abcfa
commit
8b49ddfa58
1
AUTHORS
1
AUTHORS
|
@ -35,6 +35,7 @@ Brianna Laugher
|
||||||
Bruno Oliveira
|
Bruno Oliveira
|
||||||
Cal Leeming
|
Cal Leeming
|
||||||
Carl Friedrich Bolz
|
Carl Friedrich Bolz
|
||||||
|
Carlos Jenkins
|
||||||
Ceridwen
|
Ceridwen
|
||||||
Charles Cloud
|
Charles Cloud
|
||||||
Charnjit SiNGH (CCSJ)
|
Charnjit SiNGH (CCSJ)
|
||||||
|
|
|
@ -41,6 +41,12 @@ MARK_PARAMETERSET_UNPACKING = RemovedInPytest4Warning(
|
||||||
"For more details, see: https://docs.pytest.org/en/latest/parametrize.html"
|
"For more details, see: https://docs.pytest.org/en/latest/parametrize.html"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
RECORD_XML_PROPERTY = (
|
||||||
|
'Fixture renamed from "record_xml_property" to "record_property" as user '
|
||||||
|
'properties are now available to all reporters.\n'
|
||||||
|
'"record_xml_property" is now deprecated.'
|
||||||
|
)
|
||||||
|
|
||||||
COLLECTOR_MAKEITEM = RemovedInPytest4Warning(
|
COLLECTOR_MAKEITEM = RemovedInPytest4Warning(
|
||||||
"pycollector makeitem was removed "
|
"pycollector makeitem was removed "
|
||||||
"as it is an accidentially leaked internal api"
|
"as it is an accidentially leaked internal api"
|
||||||
|
|
|
@ -233,31 +233,41 @@ class _NodeReporter(object):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def record_xml_property(request):
|
def record_property(request):
|
||||||
"""Add extra xml properties to the tag for the calling test.
|
"""Add an extra properties the calling test.
|
||||||
The fixture is callable with ``(name, value)``, with value being automatically
|
User properties become part of the test report and are available to the
|
||||||
xml-encoded.
|
configured reporters, like JUnit XML.
|
||||||
|
The fixture is callable with ``(name, value)``.
|
||||||
"""
|
"""
|
||||||
request.node.warn(
|
request.node.warn(
|
||||||
code='C3',
|
code='C3',
|
||||||
message='record_xml_property is an experimental feature',
|
message='record_property is an experimental feature',
|
||||||
)
|
)
|
||||||
xml = getattr(request.config, "_xml", None)
|
|
||||||
if xml is not None:
|
|
||||||
node_reporter = xml.node_reporter(request.node.nodeid)
|
|
||||||
return node_reporter.add_property
|
|
||||||
else:
|
|
||||||
def add_property_noop(name, value):
|
|
||||||
pass
|
|
||||||
|
|
||||||
return add_property_noop
|
def append_property(name, value):
|
||||||
|
request.node.user_properties.append((name, value))
|
||||||
|
return append_property
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def record_xml_property(request):
|
||||||
|
"""(Deprecated) use record_property."""
|
||||||
|
import warnings
|
||||||
|
from _pytest import deprecated
|
||||||
|
warnings.warn(
|
||||||
|
deprecated.RECORD_XML_PROPERTY,
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2
|
||||||
|
)
|
||||||
|
|
||||||
|
return record_property(request)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def record_xml_attribute(request):
|
def record_xml_attribute(request):
|
||||||
"""Add extra xml attributes to the tag for the calling test.
|
"""Add extra xml attributes to the tag for the calling test.
|
||||||
The fixture is callable with ``(name, value)``, with value being automatically
|
The fixture is callable with ``(name, value)``, with value being
|
||||||
xml-encoded
|
automatically xml-encoded
|
||||||
"""
|
"""
|
||||||
request.node.warn(
|
request.node.warn(
|
||||||
code='C3',
|
code='C3',
|
||||||
|
@ -442,6 +452,10 @@ class LogXML(object):
|
||||||
if report.when == "teardown":
|
if report.when == "teardown":
|
||||||
reporter = self._opentestcase(report)
|
reporter = self._opentestcase(report)
|
||||||
reporter.write_captured_output(report)
|
reporter.write_captured_output(report)
|
||||||
|
|
||||||
|
for propname, propvalue in report.user_properties:
|
||||||
|
reporter.add_property(propname, propvalue)
|
||||||
|
|
||||||
self.finalize(report)
|
self.finalize(report)
|
||||||
report_wid = getattr(report, "worker_id", None)
|
report_wid = getattr(report, "worker_id", None)
|
||||||
report_ii = getattr(report, "item_index", None)
|
report_ii = getattr(report, "item_index", None)
|
||||||
|
|
|
@ -360,6 +360,10 @@ class Item(Node):
|
||||||
super(Item, self).__init__(name, parent, config, session)
|
super(Item, self).__init__(name, parent, config, session)
|
||||||
self._report_sections = []
|
self._report_sections = []
|
||||||
|
|
||||||
|
#: user properties is a list of tuples (name, value) that holds user
|
||||||
|
#: defined properties for this test.
|
||||||
|
self.user_properties = []
|
||||||
|
|
||||||
def add_report_section(self, when, key, content):
|
def add_report_section(self, when, key, content):
|
||||||
"""
|
"""
|
||||||
Adds a new report section, similar to what's done internally to add stdout and
|
Adds a new report section, similar to what's done internally to add stdout and
|
||||||
|
|
|
@ -317,6 +317,7 @@ def pytest_runtest_makereport(item, call):
|
||||||
sections.append(("Captured %s %s" % (key, rwhen), content))
|
sections.append(("Captured %s %s" % (key, rwhen), content))
|
||||||
return TestReport(item.nodeid, item.location,
|
return TestReport(item.nodeid, item.location,
|
||||||
keywords, outcome, longrepr, when,
|
keywords, outcome, longrepr, when,
|
||||||
|
item.user_properties,
|
||||||
sections, duration)
|
sections, duration)
|
||||||
|
|
||||||
|
|
||||||
|
@ -326,7 +327,8 @@ class TestReport(BaseReport):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, nodeid, location, keywords, outcome,
|
def __init__(self, nodeid, location, keywords, outcome,
|
||||||
longrepr, when, sections=(), duration=0, **extra):
|
longrepr, when, user_properties,
|
||||||
|
sections=(), duration=0, **extra):
|
||||||
#: normalized collection node id
|
#: normalized collection node id
|
||||||
self.nodeid = nodeid
|
self.nodeid = nodeid
|
||||||
|
|
||||||
|
@ -348,6 +350,10 @@ class TestReport(BaseReport):
|
||||||
#: one of 'setup', 'call', 'teardown' to indicate runtest phase.
|
#: one of 'setup', 'call', 'teardown' to indicate runtest phase.
|
||||||
self.when = when
|
self.when = when
|
||||||
|
|
||||||
|
#: user properties is a list of tuples (name, value) that holds user
|
||||||
|
#: defined properties of the test
|
||||||
|
self.user_properties = user_properties
|
||||||
|
|
||||||
#: list of pairs ``(str, str)`` of extra information which needs to
|
#: list of pairs ``(str, str)`` of extra information which needs to
|
||||||
#: marshallable. Used by pytest to add captured text
|
#: marshallable. Used by pytest to add captured text
|
||||||
#: from ``stdout`` and ``stderr``, but may be used by other plugins
|
#: from ``stdout`` and ``stderr``, but may be used by other plugins
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
``record_xml_property`` renamed to ``record_property`` and is now compatible with xdist, markers and any reporter.
|
||||||
|
``record_xml_property`` name is now deprecated.
|
|
@ -112,10 +112,11 @@ You can ask for available builtin or project-custom
|
||||||
Inject names into the doctest namespace.
|
Inject names into the doctest namespace.
|
||||||
pytestconfig
|
pytestconfig
|
||||||
the pytest config object with access to command line opts.
|
the pytest config object with access to command line opts.
|
||||||
record_xml_property
|
record_property
|
||||||
Add extra xml properties to the tag for the calling test.
|
Add an extra properties the calling test.
|
||||||
The fixture is callable with ``(name, value)``, with value being automatically
|
User properties become part of the test report and are available to the
|
||||||
xml-encoded.
|
configured reporters, like JUnit XML.
|
||||||
|
The fixture is callable with ``(name, value)``.
|
||||||
record_xml_attribute
|
record_xml_attribute
|
||||||
Add extra xml attributes to the tag for the calling test.
|
Add extra xml attributes to the tag for the calling test.
|
||||||
The fixture is callable with ``(name, value)``, with value being automatically
|
The fixture is callable with ``(name, value)``, with value being automatically
|
||||||
|
|
|
@ -537,7 +537,7 @@ We can run this::
|
||||||
file $REGENDOC_TMPDIR/b/test_error.py, line 1
|
file $REGENDOC_TMPDIR/b/test_error.py, line 1
|
||||||
def test_root(db): # no db here, will error out
|
def test_root(db): # no db here, will error out
|
||||||
E fixture 'db' not found
|
E fixture 'db' not found
|
||||||
> available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_xml_attribute, record_xml_property, recwarn, tmpdir, tmpdir_factory
|
> available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_xml_attribute, record_property, recwarn, tmpdir, tmpdir_factory
|
||||||
> use 'pytest --fixtures [testpath]' for help on them.
|
> use 'pytest --fixtures [testpath]' for help on them.
|
||||||
|
|
||||||
$REGENDOC_TMPDIR/b/test_error.py:1
|
$REGENDOC_TMPDIR/b/test_error.py:1
|
||||||
|
|
|
@ -220,19 +220,24 @@ To set the name of the root test suite xml item, you can configure the ``junit_s
|
||||||
[pytest]
|
[pytest]
|
||||||
junit_suite_name = my_suite
|
junit_suite_name = my_suite
|
||||||
|
|
||||||
record_xml_property
|
record_property
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. versionadded:: 2.8
|
.. versionadded:: 2.8
|
||||||
|
.. versionchanged:: 3.5
|
||||||
|
|
||||||
|
Fixture renamed from ``record_xml_property`` to ``record_property`` as user
|
||||||
|
properties are now available to all reporters.
|
||||||
|
``record_xml_property`` is now deprecated.
|
||||||
|
|
||||||
If you want to log additional information for a test, you can use the
|
If you want to log additional information for a test, you can use the
|
||||||
``record_xml_property`` fixture:
|
``record_property`` fixture:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_function(record_xml_property):
|
def test_function(record_property):
|
||||||
record_xml_property("example_key", 1)
|
record_property("example_key", 1)
|
||||||
assert 0
|
assert True
|
||||||
|
|
||||||
This will add an extra property ``example_key="1"`` to the generated
|
This will add an extra property ``example_key="1"`` to the generated
|
||||||
``testcase`` tag:
|
``testcase`` tag:
|
||||||
|
@ -245,13 +250,42 @@ This will add an extra property ``example_key="1"`` to the generated
|
||||||
</properties>
|
</properties>
|
||||||
</testcase>
|
</testcase>
|
||||||
|
|
||||||
|
Alternatively, you can integrate this functionality with custom markers:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# content of conftest.py
|
||||||
|
|
||||||
|
def pytest_collection_modifyitems(session, config, items):
|
||||||
|
for item in items:
|
||||||
|
marker = item.get_marker('test_id')
|
||||||
|
if marker is not None:
|
||||||
|
test_id = marker.args[0]
|
||||||
|
item.user_properties.append(('test_id', test_id))
|
||||||
|
|
||||||
|
And in your tests:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# content of test_function.py
|
||||||
|
|
||||||
|
@pytest.mark.test_id(1501)
|
||||||
|
def test_function():
|
||||||
|
assert True
|
||||||
|
|
||||||
|
Will result in:
|
||||||
|
|
||||||
|
.. code-block:: xml
|
||||||
|
|
||||||
|
<testcase classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">
|
||||||
|
<properties>
|
||||||
|
<property name="test_id" value="1501" />
|
||||||
|
</properties>
|
||||||
|
</testcase>
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
``record_xml_property`` is an experimental feature, and its interface might be replaced
|
``record_property`` is an experimental feature and may change in the future.
|
||||||
by something more powerful and general in future versions. The
|
|
||||||
functionality per-se will be kept, however.
|
|
||||||
|
|
||||||
Currently it does not work when used with the ``pytest-xdist`` plugin.
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
|
@ -863,10 +863,10 @@ def test_record_property(testdir):
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def other(record_xml_property):
|
def other(record_property):
|
||||||
record_xml_property("bar", 1)
|
record_property("bar", 1)
|
||||||
def test_record(record_xml_property, other):
|
def test_record(record_property, other):
|
||||||
record_xml_property("foo", "<1");
|
record_property("foo", "<1");
|
||||||
""")
|
""")
|
||||||
result, dom = runandparse(testdir, '-rw')
|
result, dom = runandparse(testdir, '-rw')
|
||||||
node = dom.find_first_by_tag("testsuite")
|
node = dom.find_first_by_tag("testsuite")
|
||||||
|
@ -877,15 +877,15 @@ def test_record_property(testdir):
|
||||||
pnodes[1].assert_attr(name="foo", value="<1")
|
pnodes[1].assert_attr(name="foo", value="<1")
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
'test_record_property.py::test_record',
|
'test_record_property.py::test_record',
|
||||||
'*record_xml_property*experimental*',
|
'*record_property*experimental*',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def test_record_property_same_name(testdir):
|
def test_record_property_same_name(testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
def test_record_with_same_name(record_xml_property):
|
def test_record_with_same_name(record_property):
|
||||||
record_xml_property("foo", "bar")
|
record_property("foo", "bar")
|
||||||
record_xml_property("foo", "baz")
|
record_property("foo", "baz")
|
||||||
""")
|
""")
|
||||||
result, dom = runandparse(testdir, '-rw')
|
result, dom = runandparse(testdir, '-rw')
|
||||||
node = dom.find_first_by_tag("testsuite")
|
node = dom.find_first_by_tag("testsuite")
|
||||||
|
|
Loading…
Reference in New Issue