diff --git a/README.rst b/README.rst
index a082467..79b6517 100644
--- a/README.rst
+++ b/README.rst
@@ -141,18 +141,24 @@ Junitparser also support extra schemas:
.. code-block:: python
+ # Extended with extra properties/attributes from the xunit2 schema.
from junitparser.xunit2 import TestCase, TestSuite, RerunFailure
- # These classes are redefined to support extra properties and attributes
- # of the xunit2 schema.
+
+ # TestSuite supports system_err.
suite = TestSuite("mySuite")
- suite.system_err = "System err" # xunit2 specific property
+ suite.system_err = "System err"
+
+ # TestCase supports interim results.
case = TestCase("myCase")
- rerun_failure = RerunFailure("Not found", "404") # case property
+ rerun_failure = RerunFailure("Not found", "404")
rerun_failure.stack_trace = "Stack"
rerun_failure.system_err = "E404"
rerun_failure.system_out = "NOT FOUND"
case.add_interim_result(rerun_failure)
+ # TestCase supports properties.
+ case.add_property("cmake_labels", "cuda;tier2")
+
Currently supported schemas including:
- xunit2_, supported by pytest, Erlang/OTP, Maven Surefire, CppTest, etc.
diff --git a/junitparser/junitparser.py b/junitparser/junitparser.py
index d77ef5a..5ca6be9 100644
--- a/junitparser/junitparser.py
+++ b/junitparser/junitparser.py
@@ -428,7 +428,7 @@ def system_err(self, value: str):
class Property(Element):
- """A key/value pare that's stored in the testsuite.
+ """A key/value pair that's stored in the testsuite or testcase properties.
Use it to store anything you find interesting or useful.
@@ -458,7 +458,7 @@ def __lt__(self, other):
class Properties(Element):
- """A list of properties inside a testsuite.
+ """A list of properties inside a testsuite or testcase.
See :class:`Property`
"""
diff --git a/junitparser/xunit2.py b/junitparser/xunit2.py
index 4f60827..5f03a6a 100644
--- a/junitparser/xunit2.py
+++ b/junitparser/xunit2.py
@@ -154,6 +154,36 @@ def add_interim_result(self, result: InterimResult):
"""Append an interim (rerun or flaky) result to the testcase. A testcase can have multiple interim results."""
self.append(result)
+ def add_property(self, name: str, value: str):
+ """Add a property *name* = *value* to the testcase.
+
+ See :class:`junitparser.Property` and :class:`junitparser.Properties`.
+ """
+
+ props = self.child(junitparser.Properties)
+ if props is None:
+ props = junitparser.Properties()
+ self.append(props)
+ prop = junitparser.Property(name, value)
+ props.add_property(prop)
+
+ def properties(self):
+ """Iterate through all :class:`junitparser.Property` elements in the testcase."""
+ props = self.child(junitparser.Properties)
+ if props is None:
+ return
+ for prop in props:
+ yield prop
+
+ def remove_property(self, property_: junitparser.Property):
+ """Remove property *property_* from the testcase."""
+ props = self.child(junitparser.Properties)
+ if props is None:
+ return
+ for prop in props:
+ if prop == property_:
+ props.remove(property_)
+
class TestSuite(junitparser.TestSuite):
"""TestSuite for Pytest, with some different attributes."""
diff --git a/tests/test_xunit2.py b/tests/test_xunit2.py
index 13d439e..904b788 100644
--- a/tests/test_xunit2.py
+++ b/tests/test_xunit2.py
@@ -1,5 +1,6 @@
+import textwrap
from junitparser.xunit2 import JUnitXml, TestSuite, TestCase, RerunFailure, RerunError, FlakyFailure, FlakyError
-from junitparser import Failure
+from junitparser import Failure, Property
from copy import deepcopy
@@ -137,6 +138,62 @@ def test_case_rerun(self):
case.add_interim_result(failure2)
assert len(case.rerun_failures()) == 2
+ def test_properties_and_output(self):
+ text = textwrap.dedent("""\
+
+
+
+
+
+
+ a line of output
+ another line
+
+
+ """)
+ case = TestCase.fromstring(text)
+ assert case.name == "test_pushstringvector"
+ assert case.system_out == "\na line of output\nanother line\n"
+ # Check that there are two properties in the TestCase, then check the values.
+ case_properties = list(case.properties())
+ assert len(case_properties) == 2
+ prop1, prop2 = case_properties
+ assert prop1.name == "cmake_labels" and prop1.value == "util;script"
+ assert prop2.name == "TestType" and prop2.value == "LATENCY_EDMA"
+
+ def test_suite_parses_testcase_properties(self):
+ text = textwrap.dedent("""\
+
+
+
+
+
+
+
+ """
+ )
+ test_suite1 = TestSuite.fromstring(text)
+ assert test_suite1.name == "suitename1"
+ cases = list(iter(test_suite1))
+ assert len(cases) == 1
+ case = cases[0]
+ case_properties = list(case.properties())
+ assert len(case_properties) == 2
+ prop1, prop2 = case_properties
+ assert prop1.name == "labels" and prop1.value == "foo;bar"
+ assert prop2.name == "test_type" and prop2.value == "latency"
+
+ def test_add_remove_property(self):
+ case = TestCase()
+ case.add_property("prop1", "foo")
+ case.add_property("prop2", "bar")
+ prop_to_remove = Property("prop1", "foo")
+ case.remove_property(prop_to_remove)
+ assert len(list(case.properties())) == 1
+ assert case.tostring() in [
+ b'',
+ b'',
+ ]
class Test_TestSuite:
def test_properties(self):