diff --git a/junit_xml/__init__.py b/junit_xml/__init__.py index f53e9e5..4ab3ab8 100644 --- a/junit_xml/__init__.py +++ b/junit_xml/__init__.py @@ -80,7 +80,8 @@ class TestSuite(object): """ def __init__(self, name, test_cases=None, hostname=None, id=None, - package=None, timestamp=None, properties=None): + package=None, timestamp=None, properties=None, file=None, + log=None, url=None, stdout=None, stderr=None): self.name = name if not test_cases: test_cases = [] @@ -89,10 +90,15 @@ def __init__(self, name, test_cases=None, hostname=None, id=None, except TypeError: raise Exception('test_cases must be a list of test cases') self.test_cases = test_cases + self.timestamp = timestamp self.hostname = hostname self.id = id self.package = package - self.timestamp = timestamp + self.file = file + self.log = log + self.url = url + self.stdout = stdout + self.stderr = stderr self.properties = properties def build_xml_doc(self, encoding=None): @@ -106,6 +112,11 @@ def build_xml_doc(self, encoding=None): # build the test suite element test_suite_attributes = dict() test_suite_attributes['name'] = decode(self.name, encoding) + if any(c.assertions for c in self.test_cases): + test_suite_attributes['assertions'] = \ + str(sum([int(c.assertions) for c in self.test_cases if c.assertions])) + test_suite_attributes['disabled'] = \ + str(len([c for c in self.test_cases if not c.is_enabled])) test_suite_attributes['failures'] = \ str(len([c for c in self.test_cases if c.is_failure()])) test_suite_attributes['errors'] = \ @@ -124,6 +135,12 @@ def build_xml_doc(self, encoding=None): test_suite_attributes['package'] = decode(self.package, encoding) if self.timestamp: test_suite_attributes['timestamp'] = decode(self.timestamp, encoding) + if self.timestamp: + test_suite_attributes['file'] = decode(self.file, encoding) + if self.timestamp: + test_suite_attributes['log'] = decode(self.log, encoding) + if self.timestamp: + test_suite_attributes['url'] = decode(self.url, encoding) xml_element = ET.Element("testsuite", test_suite_attributes) @@ -134,12 +151,23 @@ def build_xml_doc(self, encoding=None): attrs = {'name': decode(k, encoding), 'value': decode(v, encoding)} ET.SubElement(props_element, "property", attrs) + # add test suite stdout + if self.stdout: + stdout_element = ET.SubElement(xml_element, "system-out") + stdout_element.text = decode(self.stdout, encoding) + + # add test suite stderr + if self.stderr: + stderr_element = ET.SubElement(xml_element, "system-err") + stderr_element.text = decode(self.stderr, encoding) + # test cases for case in self.test_cases: test_case_attributes = dict() test_case_attributes['name'] = decode(case.name, encoding) if case.assertions: - test_case_attributes['assertions'] = decode(case.assertions, encoding) + # Number of assertions in the test case + test_case_attributes['assertions'] = "%d" % case.assertions if case.elapsed_sec: test_case_attributes['time'] = "%f" % case.elapsed_sec if case.timestamp: @@ -227,7 +255,7 @@ def to_xml_string(test_suites, prettyprint=True, encoding=None): attributes = defaultdict(int) for ts in test_suites: ts_xml = ts.build_xml_doc(encoding=encoding) - for key in ['failures', 'errors', 'tests']: + for key in ['failures', 'errors', 'tests', 'disabled']: attributes[key] += int(ts_xml.get(key, 0)) for key in ['time']: attributes[key] += float(ts_xml.get(key, 0)) @@ -291,9 +319,10 @@ def _clean_illegal_xml_chars(string_to_clean): class TestCase(object): """A JUnit test case with a result and possibly some stdout or stderr""" - def __init__(self, name, assertions=None, elapsed_sec=None, - timestamp=None, classname=None, status=None, category=None, file=None, line=None, - log=None, group=None, url=None, stdout=None, stderr=None): + def __init__(self, name, classname=None, elapsed_sec=None, stdout=None, + stderr=None, assertions=None, timestamp=None, status=None, + category=None, file=None, line=None, log=None, group=None, + url=None): self.name = name self.assertions = assertions self.elapsed_sec = elapsed_sec @@ -308,6 +337,7 @@ def __init__(self, name, assertions=None, elapsed_sec=None, self.stdout = stdout self.stderr = stderr + self.is_enabled = True self.error_message = None self.error_output = None self.error_type = None diff --git a/test_junit_xml.py b/test_junit_xml.py index 51cf8ef..1c59aed 100644 --- a/test_junit_xml.py +++ b/test_junit_xml.py @@ -73,8 +73,8 @@ def test_single_suite_no_test_cases(self): (ts, tcs) = serialize_and_read( TestSuite( - 'test', - [], + name='test', + test_cases=[], hostname='localhost', id=1, properties=properties, @@ -100,8 +100,8 @@ def test_single_suite_no_test_cases_utf8(self): timestamp = 1398382805 test_suite = TestSuite( - 'äöü', - [], + name='äöü', + test_cases=[], hostname='löcalhost', id='äöü', properties=properties, @@ -131,8 +131,8 @@ def test_single_suite_no_test_cases_unicode(self): (ts, tcs) = serialize_and_read( TestSuite( - decode('äöü', 'utf-8'), - [], + name=decode('äöü', 'utf-8'), + test_cases=[], hostname=decode('löcalhost', 'utf-8'), id=decode('äöü', 'utf-8'), properties=properties, @@ -198,9 +198,8 @@ def test_multiple_suites_to_string(self): verify_test_case(self, suites[1][1][0], {'name': 'Test2'}) def test_attribute_time(self): - tss = [TestSuite('suite1', - [TestCase('Test1', 'some.class.name', 123.345), - TestCase('Test2', 'some2.class.name', 123.345)]), + tss = [TestSuite('suite1', [TestCase(name='Test1', classname='some.class.name', elapsed_sec=123.345), + TestCase(name='Test2', classname='some2.class.name', elapsed_sec=123.345)]), TestSuite('suite2', [TestCase('Test2')])] suites = serialize_and_read(tss) @@ -212,21 +211,60 @@ def test_attribute_time(self): # testcase self.assertEqual('0', suites[1][0].attributes['time'].value) + def test_attribute_disable(self): + tc = TestCase('Disabled-Test') + tc.is_enabled = False + tss = [TestSuite('suite1', [tc])] + suites = serialize_and_read(tss) + + self.assertEqual('1', suites[0][0].attributes['disabled'].value) + + def test_stderr(self): + suites = serialize_and_read( + TestSuite(name='test', stderr='I am stderr!', + test_cases=[TestCase(name='Test1')]))[0] + self.assertEqual('I am stderr!', + suites[0].getElementsByTagName('system-err')[0].firstChild.data) + + def test_stdout_stderr(self): + suites = serialize_and_read( + TestSuite(name='test', stdout='I am stdout!', + stderr='I am stderr!', + test_cases=[TestCase(name='Test1')]))[0] + self.assertEqual('I am stderr!', + suites[0].getElementsByTagName('system-err')[0].firstChild.data) + self.assertEqual('I am stdout!', + suites[0].getElementsByTagName('system-out')[0].firstChild.data) + + def test_no_assertions(self): + suites = serialize_and_read( + TestSuite(name='test', + test_cases=[TestCase(name='Test1')]))[0] + self.assertFalse(suites[0].getElementsByTagName('testcase')[0].hasAttribute('assertions')) + + def test_assertions(self): + suites = serialize_and_read( + TestSuite(name='test', + test_cases=[TestCase(name='Test1', + assertions=5)]))[0] + self.assertEquals('5', + suites[0].getElementsByTagName('testcase')[0].attributes['assertions'].value) + # @todo: add more tests for the other attributes and properties def test_to_xml_string(self): - test_suites = [TestSuite('suite1', [TestCase('Test1')]), - TestSuite('suite2', [TestCase('Test2')])] + test_suites = [TestSuite(name='suite1', test_cases=[TestCase(name='Test1')]), + TestSuite(name='suite2', test_cases=[TestCase(name='Test2')])] xml_string = TestSuite.to_xml_string(test_suites) if PY2: self.assertTrue(isinstance(xml_string, unicode)) expected_xml_string = textwrap.dedent(""" - - \t + + \t \t\t \t - \t + \t \t\t \t @@ -298,6 +336,12 @@ def test_init_stdout_stderr(self): 'time': ("%f" % 123.345)}, stdout='I am stdout!', stderr='I am stderr!') + def test_init_disable(self): + tc = TestCase('Disabled-Test') + tc.is_enabled = False + (ts, tcs) = serialize_and_read(TestSuite('test', [tc]))[0] + verify_test_case(self, tcs[0], {'name': 'Disabled-Test'}) + def test_init_failure_message(self): tc = TestCase('Failure-Message') tc.add_failure_info("failure message")