Merge pull request #3132 from raphaelcastaneda/feature/add-record-xml-attribute
implement #3130 - add record_xml_attribute fixture
This commit is contained in:
		
						commit
						49773b573f
					
				
							
								
								
									
										1
									
								
								AUTHORS
								
								
								
								
							
							
						
						
									
										1
									
								
								AUTHORS
								
								
								
								
							|  | @ -154,6 +154,7 @@ Punyashloka Biswal | |||
| Quentin Pradet | ||||
| Ralf Schmitt | ||||
| Ran Benita | ||||
| Raphael Castaneda | ||||
| Raphael Pierzina | ||||
| Raquel Alegre | ||||
| Ravi Chandra | ||||
|  |  | |||
|  | @ -85,6 +85,9 @@ class _NodeReporter(object): | |||
|     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 @@ class _NodeReporter(object): | |||
|     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 @@ class _NodeReporter(object): | |||
|         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 record_xml_property(request): | |||
|         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( | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| New fixture ``record_xml_attribute`` that allows modifying and inserting attributes on the ``<testcase>`` xml node in JUnit reports. | ||||
|  | @ -256,6 +256,66 @@ This will add an extra property ``example_key="1"`` to the generated | |||
|     Also please note that using this feature will break any schema verification. | ||||
|     This might be a problem when used with some CI servers. | ||||
| 
 | ||||
| record_xml_attribute | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| .. versionadded:: 3.4 | ||||
| 
 | ||||
| To add an additional xml attribute to a testcase element, you can use | ||||
| ``record_xml_attribute`` fixture. This can also be used to override existing values: | ||||
| 
 | ||||
| .. code-block:: python | ||||
| 
 | ||||
|     def test_function(record_xml_attribute): | ||||
|         record_xml_attribute("assertions", "REQ-1234") | ||||
|         record_xml_attribute("classname", "custom_classname") | ||||
|         print('hello world') | ||||
|         assert True | ||||
| 
 | ||||
| Unlike ``record_xml_property``, this will not add a new child element. | ||||
| Instead, this will add an attribute ``assertions="REQ-1234"`` inside the generated | ||||
| ``testcase`` tag and override the default ``classname`` with ``"classname=custom_classname"``: | ||||
| 
 | ||||
| .. code-block:: xml | ||||
| 
 | ||||
|     <testcase classname="custom_classname" file="test_function.py" line="0" name="test_function" time="0.003" assertions="REQ-1234"> | ||||
|         <system-out> | ||||
|             hello world | ||||
|         </system-out> | ||||
|     </testcase> | ||||
| 
 | ||||
| .. warning:: | ||||
| 
 | ||||
|     ``record_xml_attribute`` is an experimental feature, and its interface might be replaced | ||||
|     by something more powerful and general in future versions. The | ||||
|     functionality per-se will be kept, however. | ||||
| 
 | ||||
|     Using this over ``record_xml_property`` can help when using ci tools to parse the xml report. | ||||
|     However, some parsers are quite strict about the elements and attributes that are allowed. | ||||
|     Many tools use an xsd schema (like the example below) to validate incoming xml. | ||||
|     Make sure you are using attribute names that are allowed by your parser. | ||||
| 
 | ||||
|     Below is the Scheme used by Jenkins to validate the XML report: | ||||
| 
 | ||||
|     .. code-block:: xml | ||||
| 
 | ||||
|         <xs:element name="testcase"> | ||||
|             <xs:complexType> | ||||
|                 <xs:sequence> | ||||
|                     <xs:element ref="skipped" minOccurs="0" maxOccurs="1"/> | ||||
|                     <xs:element ref="error" minOccurs="0" maxOccurs="unbounded"/> | ||||
|                     <xs:element ref="failure" minOccurs="0" maxOccurs="unbounded"/> | ||||
|                     <xs:element ref="system-out" minOccurs="0" maxOccurs="unbounded"/> | ||||
|                     <xs:element ref="system-err" minOccurs="0" maxOccurs="unbounded"/> | ||||
|                 </xs:sequence> | ||||
|                 <xs:attribute name="name" type="xs:string" use="required"/> | ||||
|                 <xs:attribute name="assertions" type="xs:string" use="optional"/> | ||||
|                 <xs:attribute name="time" type="xs:string" use="optional"/> | ||||
|                 <xs:attribute name="classname" type="xs:string" use="optional"/> | ||||
|                 <xs:attribute name="status" type="xs:string" use="optional"/> | ||||
|             </xs:complexType> | ||||
|         </xs:element> | ||||
| 
 | ||||
| LogXML: add_global_property | ||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
|  |  | |||
|  | @ -879,6 +879,27 @@ def test_record_property_same_name(testdir): | |||
|     pnodes[1].assert_attr(name="foo", value="baz") | ||||
| 
 | ||||
| 
 | ||||
| def test_record_attribute(testdir): | ||||
|     testdir.makepyfile(""" | ||||
|         import pytest | ||||
| 
 | ||||
|         @pytest.fixture | ||||
|         def other(record_xml_attribute): | ||||
|             record_xml_attribute("bar", 1) | ||||
|         def test_record(record_xml_attribute, other): | ||||
|             record_xml_attribute("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 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue