def to_xml(self): """Return an XML instance of the suite""" x = XMLBuilder('testsuite', name=self.name, tests=str(self.ntests), errors=str(self.nerrors), failures=str(self.nfailure), skip=str(self.nskipped)) for test_case in self.tests: classname = test_case.classname # sanitize for XML text = "" if test_case.text is None else test_case.text etype = "" if test_case.etype is None else test_case.etype with x.testcase(classname=classname, name=test_case.name, result=test_case.result, etype=etype, text=text): if test_case.result == 'failures': x.error(type=test_case.etype, message=test_case.message) elif test_case.result == 'failure': x.failure(type=test_case.etype, message=test_case.message) elif test_case.result == 'skipped': x.skipped(type=test_case.etype, message=test_case.message) elif test_case.result == 'error': x.error(type=test_case.etype, message=test_case.message) else: # Successful testcase pass return x
def convert_suite_and_result_to_xunit(suite, result, name="PysivXunitTestSuite"): """Custom a test suite and result to XML. The name is used to set the xml suitename for jenkins. <testsuite errors="1" failures="1" name="1234" skip="1" tests="5"> :param suite: unittest.TestSuite :param result: unittest.TestResult :param name: :return: XML instance """ # When a test fails in setUpClass, the result is a # unittest.suite._ErrorHolder rather than a TestCase. We need to handle # those differently. def parse_setupclass_error(klass_id): """Return what's inside the parentheses.""" return re.search("(?<=\().*(?=\))", klass_id).group(0) # Test cls names/id names = 'errors skipped failures'.split() klass_results = {} for n in names: klass_results[n] = [] for klass, out in getattr(result, n): if isinstance(klass, unittest.suite._ErrorHolder): klass_results[n].append(parse_setupclass_error(klass.id())) else: klass_results[n].append(klass.id()) nskipped = len(klass_results['skipped']) nerrors = len(klass_results['errors']) nfailures = len(klass_results['failures']) def _to_key(test_case): # If the test_case is an _ErrorHolder, the key should be parsed from # the description. It won't have a _testMethodName if isinstance(test_case, unittest.suite._ErrorHolder): return parse_setupclass_error(test_case.description) m = test_case.__module__ n = test_case.__class__.__name__ mn = test_case._testMethodName # d = test_case._testMethodDoc # return m, n, mn, d return ".".join([m, n, mn]) all_test_cases = {} for s in suite: if isinstance(s, unittest.suite.TestSuite): for tc in s: all_test_cases[_to_key(tc)] = None else: raise TypeError("Unsupported test suite case ({x})".format(x=type(s))) ntests = len(all_test_cases) # loop over failures, errors, skipped, assign message for n in names: # get all testcases with state ts = getattr(result, n) for t, msg in ts: k = _to_key(t) all_test_cases[k] = msg # import ipdb; ipdb.set_trace() # Create XML x = XMLBuilder('testsuite', name=name, tests=str(ntests), errors=str(nerrors), failures=str(nfailures), skip=str(nskipped)) for idx, message in all_test_cases.iteritems(): test_method = idx.split('.')[-1] with x.testcase(classname=idx, name=test_method, time="1.000"): if idx in klass_results['errors']: x.error(type="exceptions.Exception", message=message) elif idx in klass_results['failures']: x.failure(type="exceptions.Exception", message=message) elif idx in klass_results['skipped']: # print "skipped", idx x.skipped(type="unittest.case.SkipTest", message=message) else: # print "Success", idx pass return x