Fix user_properties not saved to XML if fixture errors during teardown

Move handling of user_properties to `finalize()`.

Previously if a fixture failed during teardown, `pytest_runtest_logreport` would not be called with "teardown", resulting in the user properties not being saved on the JUnit XML file.

Fixes: #11367
This commit is contained in:
Israel Fruchter 2023-09-03 20:33:54 +03:00 committed by GitHub
parent 4e3a0874df
commit 917ce9aa01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 3 deletions

View File

@ -170,6 +170,7 @@ Ian Lesperance
Ilya Konstantinov Ilya Konstantinov
Ionuț Turturică Ionuț Turturică
Isaac Virshup Isaac Virshup
Israel Fruchter
Itxaso Aizpurua Itxaso Aizpurua
Iwan Briquemont Iwan Briquemont
Jaap Broekhuizen Jaap Broekhuizen

View File

@ -0,0 +1 @@
Fixed bug where `user_properties` where not being saved in the JUnit XML file if a fixture failed during teardown.

View File

@ -502,6 +502,10 @@ class LogXML:
# Local hack to handle xdist report order. # Local hack to handle xdist report order.
workernode = getattr(report, "node", None) workernode = getattr(report, "node", None)
reporter = self.node_reporters.pop((nodeid, workernode)) reporter = self.node_reporters.pop((nodeid, workernode))
for propname, propvalue in report.user_properties:
reporter.add_property(propname, str(propvalue))
if reporter is not None: if reporter is not None:
reporter.finalize() reporter.finalize()
@ -599,9 +603,6 @@ class LogXML:
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, str(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)

View File

@ -1228,6 +1228,36 @@ def test_record_property(pytester: Pytester, run_and_parse: RunAndParse) -> None
result.stdout.fnmatch_lines(["*= 1 passed in *"]) result.stdout.fnmatch_lines(["*= 1 passed in *"])
def test_record_property_on_test_and_teardown_failure(
pytester: Pytester, run_and_parse: RunAndParse
) -> None:
pytester.makepyfile(
"""
import pytest
@pytest.fixture
def other(record_property):
record_property("bar", 1)
yield
assert 0
def test_record(record_property, other):
record_property("foo", "<1")
assert 0
"""
)
result, dom = run_and_parse()
node = dom.find_first_by_tag("testsuite")
tnodes = node.find_by_tag("testcase")
for tnode in tnodes:
psnode = tnode.find_first_by_tag("properties")
assert psnode, f"testcase didn't had expected properties:\n{tnode}"
pnodes = psnode.find_by_tag("property")
pnodes[0].assert_attr(name="bar", value="1")
pnodes[1].assert_attr(name="foo", value="<1")
result.stdout.fnmatch_lines(["*= 1 failed, 1 error *"])
def test_record_property_same_name( def test_record_property_same_name(
pytester: Pytester, run_and_parse: RunAndParse pytester: Pytester, run_and_parse: RunAndParse
) -> None: ) -> None: