Fix user_properties not saved to XML if fixture errors during teardown (#11382)
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
(cherry picked from commit 917ce9aa01)
Co-authored-by: Israel Fruchter <israel.fruchter@gmail.com>
			
			
This commit is contained in:
		
							parent
							
								
									82eb86f707
								
							
						
					
					
						commit
						7f5d9b9df4
					
				
							
								
								
									
										2
									
								
								AUTHORS
								
								
								
								
							
							
						
						
									
										2
									
								
								AUTHORS
								
								
								
								
							| 
						 | 
					@ -166,6 +166,8 @@ Ian Bicking
 | 
				
			||||||
Ian Lesperance
 | 
					Ian Lesperance
 | 
				
			||||||
Ilya Konstantinov
 | 
					Ilya Konstantinov
 | 
				
			||||||
Ionuț Turturică
 | 
					Ionuț Turturică
 | 
				
			||||||
 | 
					Isaac Virshup
 | 
				
			||||||
 | 
					Israel Fruchter
 | 
				
			||||||
Itxaso Aizpurua
 | 
					Itxaso Aizpurua
 | 
				
			||||||
Iwan Briquemont
 | 
					Iwan Briquemont
 | 
				
			||||||
Jaap Broekhuizen
 | 
					Jaap Broekhuizen
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					Fixed bug where `user_properties` where not being saved in the JUnit XML file if a fixture failed during teardown.
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue