diff --git a/AUTHORS b/AUTHORS index 862378be9f5..502cfff7ee7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -149,6 +149,7 @@ Punyashloka Biswal Quentin Pradet Ralf Schmitt Ran Benita +Raphael Castaneda Raphael Pierzina Raquel Alegre Ravi Chandra diff --git a/_pytest/junitxml.py b/_pytest/junitxml.py index 7fb40dc3548..e929eeba8e4 100644 --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -85,6 +85,9 @@ def append(self, node): def add_property(self, name, value): self.properties.append((str(name), bin_xml_escape(value))) + def add_attribute(self, name, value): + self.attrs[str(name)] = bin_xml_escape(value) + def make_properties_node(self): """Return a Junit node containing custom properties, if any. """ @@ -98,6 +101,7 @@ def make_properties_node(self): def record_testreport(self, testreport): assert not self.testcase names = mangle_test_address(testreport.nodeid) + existing_attrs = self.attrs classnames = names[:-1] if self.xml.prefix: classnames.insert(0, self.xml.prefix) @@ -111,6 +115,7 @@ def record_testreport(self, testreport): if hasattr(testreport, "url"): attrs["url"] = testreport.url self.attrs = attrs + self.attrs.update(existing_attrs) # restore any user-defined attributes def to_xml(self): testcase = Junit.testcase(time=self.duration, **self.attrs) @@ -211,6 +216,27 @@ def add_property_noop(name, value): return add_property_noop +@pytest.fixture +def record_xml_attribute(request): + """Add extra xml attributes to the tag for the calling test. + The fixture is callable with ``(name, value)``, with value being automatically + xml-encoded + """ + request.node.warn( + code='C3', + message='record_xml_attribute 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_attribute + else: + def add_attr_noop(name, value): + pass + + return add_attr_noop + + def pytest_addoption(parser): group = parser.getgroup("terminal reporting") group.addoption( diff --git a/changelog/3130.feature b/changelog/3130.feature new file mode 100644 index 00000000000..3c60cd75cf2 --- /dev/null +++ b/changelog/3130.feature @@ -0,0 +1 @@ +Add fixture `record_xml_attribute` that allows modifying and inserting attributes on the `` xml node in junit reports. diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index b604c02a3de..d95f3de6209 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -879,6 +879,27 @@ def test_record_with_same_name(record_xml_property): pnodes[1].assert_attr(name="foo", value="baz") +def test_record_attribute(testdir): + testdir.makepyfile(""" + import pytest + + @pytest.fixture + def other(record_xml_property): + record_xml_property("bar", 1) + def test_record(record_xml_property, other): + record_xml_property("foo", "<1"); + """) + result, dom = runandparse(testdir, '-rw') + node = dom.find_first_by_tag("testsuite") + tnode = node.find_first_by_tag("testcase") + tnode.assert_attr(bar="1") + tnode.assert_attr(foo="<1") + result.stdout.fnmatch_lines([ + 'test_record_attribute.py::test_record', + '*record_xml_attribute*experimental*', + ]) + + def test_random_report_log_xdist(testdir): """xdist calls pytest_runtest_logreport as they are executed by the slaves, with nodes from several nodes overlapping, so junitxml must cope with that