diff --git a/junit_xml/__init__.py b/junit_xml/__init__.py index 0cfef29..0f339d0 100644 --- a/junit_xml/__init__.py +++ b/junit_xml/__init__.py @@ -94,6 +94,7 @@ def __init__( url=None, stdout=None, stderr=None, + attributes=None, ): self.name = name if not test_cases: @@ -113,6 +114,15 @@ def __init__( self.stdout = stdout self.stderr = stderr self.properties = properties + self.attributes = attributes + + @staticmethod + def _add_properties(properties, element, encoding=None): + if properties: + props_element = ET.SubElement(element, "properties") + for k, v in properties.items(): + attrs = {"name": decode(k, encoding), "value": decode(v, encoding)} + ET.SubElement(props_element, "property", attrs) def build_xml_doc(self, encoding=None): """ @@ -148,15 +158,14 @@ def build_xml_doc(self, encoding=None): test_suite_attributes["log"] = decode(self.log, encoding) if self.url: test_suite_attributes["url"] = decode(self.url, encoding) + if self.attributes: + test_suite_attributes.update(self.attributes) + xml_element = ET.Element("testsuite", test_suite_attributes) # add any properties - if self.properties: - props_element = ET.SubElement(xml_element, "properties") - for k, v in self.properties.items(): - attrs = {"name": decode(k, encoding), "value": decode(v, encoding)} - ET.SubElement(props_element, "property", attrs) + self._add_properties(self.properties, xml_element, encoding) # add test suite stdout if self.stdout: @@ -193,9 +202,14 @@ def build_xml_doc(self, encoding=None): test_case_attributes["log"] = decode(case.log, encoding) if case.url: test_case_attributes["url"] = decode(case.url, encoding) + if case.attributes: + test_case_attributes.update(case.attributes) test_case_element = ET.SubElement(xml_element, "testcase", test_case_attributes) + # add test case properties + self._add_properties(case.properties, test_case_element, encoding) + # failures for failure in case.failures: if failure["output"] or failure["message"]: @@ -222,7 +236,7 @@ def build_xml_doc(self, encoding=None): error_element.text = decode(error["output"], encoding) test_case_element.append(error_element) - # skippeds + # skipped for skipped in case.skipped: attrs = {"type": "skipped"} if skipped["message"]: @@ -380,6 +394,8 @@ def __init__( log=None, url=None, allow_multiple_subelements=False, + properties=None, + attributes=None, ): self.name = name self.assertions = assertions @@ -399,61 +415,38 @@ def __init__( self.errors = [] self.failures = [] self.skipped = [] - self.allow_multiple_subalements = allow_multiple_subelements + self.allow_multiple_subelements = allow_multiple_subelements + self.properties = properties + self.attributes = attributes - def add_error_info(self, message=None, output=None, error_type=None): - """Adds an error message, output, or both to the test case""" - error = {} - error["message"] = message - error["output"] = output - error["type"] = error_type - if self.allow_multiple_subalements: + def _add_info(self, infos, message=None, output=None, type_=None): + info = {"message": message, "output": output} + if type_ is not None: + info["type"] = type_ + if self.allow_multiple_subelements: if message or output: - self.errors.append(error) - elif not len(self.errors): - self.errors.append(error) + infos.append(info) + elif not len(infos): + infos.append(info) else: if message: - self.errors[0]["message"] = message + infos[0]["message"] = message if output: - self.errors[0]["output"] = output - if error_type: - self.errors[0]["type"] = error_type + infos[0]["output"] = output + if type_: + infos[0]["type"] = type_ - def add_failure_info(self, message=None, output=None, failure_type=None): + def add_error_info(self, message=None, output=None, error_type=""): + """Adds an error message, output, or both to the test case""" + self._add_info(self.errors, message, output, error_type) + + def add_failure_info(self, message=None, output=None, failure_type=""): """Adds a failure message, output, or both to the test case""" - failure = {} - failure["message"] = message - failure["output"] = output - failure["type"] = failure_type - if self.allow_multiple_subalements: - if message or output: - self.failures.append(failure) - elif not len(self.failures): - self.failures.append(failure) - else: - if message: - self.failures[0]["message"] = message - if output: - self.failures[0]["output"] = output - if failure_type: - self.failures[0]["type"] = failure_type + self._add_info(self.failures, message, output, failure_type) def add_skipped_info(self, message=None, output=None): """Adds a skipped message, output, or both to the test case""" - skipped = {} - skipped["message"] = message - skipped["output"] = output - if self.allow_multiple_subalements: - if message or output: - self.skipped.append(skipped) - elif not len(self.skipped): - self.skipped.append(skipped) - else: - if message: - self.skipped[0]["message"] = message - if output: - self.skipped[0]["output"] = output + self._add_info(self.skipped, message, output) def is_failure(self): """returns true if this test case is a failure""" diff --git a/setup.py b/setup.py index 8aba068..c01bb66 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ def read(fname): packages=find_packages(exclude=["tests"]), description="Creates JUnit XML test result documents that can be read by tools such as Jenkins", long_description=read("README.rst"), - version="1.9", + version="1.10", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", diff --git a/tests/test_test_case.py b/tests/test_test_case.py index b9817de..89c9dc6 100644 --- a/tests/test_test_case.py +++ b/tests/test_test_case.py @@ -3,10 +3,10 @@ from six import u -from .asserts import verify_test_case from junit_xml import TestCase as Case from junit_xml import TestSuite as Suite from junit_xml import decode +from .asserts import verify_test_case from .serializer import serialize_and_read @@ -15,9 +15,14 @@ def test_init(): verify_test_case(tcs[0], {"name": "Test1"}) -def test_init_classname(): - ts, tcs = serialize_and_read(Suite("test", [Case(name="Test1", classname="some.class.name")]))[0] +def test_init_classname_properties(): + properties = {decode("foö", "utf-8"): decode("bär", "utf-8")} + ts, tcs = serialize_and_read( + Suite("test", [Case(name="Test1", classname="some.class.name", properties=properties)]))[0] verify_test_case(tcs[0], {"name": "Test1", "classname": "some.class.name"}) + a = tcs[0].firstChild.firstChild.attributes + assert a["name"].value == decode("foö", "utf-8") + assert a["value"].value == decode("bär", "utf-8") def test_init_classname_time(): @@ -322,3 +327,9 @@ def test_multiple_skipped(): {"message": "Second skipped", "output": "Second skipped message"}, ], ) + + +def test_init_attributes(): + tc = Case("Attributes", attributes={"xml:id": "1"}) + ts, tcs = serialize_and_read(Suite("test", [tc]))[0] + verify_test_case(tcs[0], {"name": "Attributes", "xml:id": "1"}) diff --git a/tests/test_test_suite.py b/tests/test_test_suite.py index a6591fe..7e10946 100644 --- a/tests/test_test_suite.py +++ b/tests/test_test_suite.py @@ -166,6 +166,12 @@ def test_attribute_disable(): assert suites[0][0].attributes["disabled"].value == "1" +def test_init_attributes(): + tss = [Suite("suite1", attributes={"xml:id": "1"})] + suites = serialize_and_read(tss) + assert suites[0][0].attributes["xml:id"].value == "1" + + def test_stderr(): suites = serialize_and_read(Suite(name="test", stderr="I am stderr!", test_cases=[Case(name="Test1")]))[0] assert suites[0].getElementsByTagName("system-err")[0].firstChild.data == "I am stderr!"