diff --git a/tests/testsuite.py b/tests/testsuite.py index 7ce556e..17a5d87 100755 --- a/tests/testsuite.py +++ b/tests/testsuite.py @@ -64,6 +64,17 @@ def test_subTest_fail(self): with self.subTest(i=i): self.fail('this is a subtest.') + class DummyErrorInCallTest(unittest.TestCase): + def __call__(self, result): + try: + raise Exception('Massive fail') + except Exception: + result.addError(self, sys.exc_info()) + return + super(DummyErrorInCallTest, self).__call__(result) + def test_pass(self): + pass + def setUp(self): self.stream = StringIO() self.outdir = mkdtemp() @@ -311,3 +322,9 @@ def test_xmlrunner_patched_stdout(self): finally: sys.stdout, sys.stderr = old_stdout, old_stderr + def test_xmlrunner_error_in_call(self): + suite = unittest.TestSuite() + suite.addTest(self.DummyErrorInCallTest('test_pass')) + self._test_xmlrunner(suite) + testsuite_output = self.stream.getvalue() + self.assertIn('Exception: Massive fail', testsuite_output) diff --git a/xmlrunner/result.py b/xmlrunner/result.py index cfb8857..d04ede3 100644 --- a/xmlrunner/result.py +++ b/xmlrunner/result.py @@ -190,8 +190,13 @@ def startTest(self, test): self.stream.write(" ... ") def _save_output_data(self): - self._stdout_data = sys.stdout.getvalue() - self._stderr_data = sys.stderr.getvalue() + # Only try to get sys.stdout and sys.sterr as they not be + # StringIO yet, e.g. when test fails during __call__ + try: + self._stdout_data = sys.stdout.getvalue() + self._stderr_data = sys.stderr.getvalue() + except AttributeError: + pass def stopTest(self, test): """ @@ -339,7 +344,8 @@ def _report_testsuite(suite_name, tests, xml_document, parentElement, stdout = StringIO() for test in tests: # Merge the stdout from the tests in a class - stdout.write(test.stdout) + if test.stdout is not None: + stdout.write(test.stdout) _XMLTestResult._createCDATAsections( xml_document, systemout, stdout.getvalue()) @@ -349,7 +355,8 @@ def _report_testsuite(suite_name, tests, xml_document, parentElement, stderr = StringIO() for test in tests: # Merge the stderr from the tests in a class - stderr.write(test.stderr) + if test.stderr is not None: + stderr.write(test.stderr) _XMLTestResult._createCDATAsections( xml_document, systemerr, stderr.getvalue()) @@ -469,7 +476,12 @@ def _exc_info_to_string(self, err, test): """Converts a sys.exc_info()-style tuple of values into a string.""" if six.PY3: # It works fine in python 3 - return super(_XMLTestResult, self)._exc_info_to_string(err, test) + try: + return super(_XMLTestResult, self)._exc_info_to_string( + err, test) + except AttributeError: + # We keep going using the legacy python <= 2 way + pass # This comes directly from python2 unittest exctype, value, tb = err @@ -485,8 +497,16 @@ def _exc_info_to_string(self, err, test): msgLines = traceback.format_exception(exctype, value, tb) if self.buffer: - output = sys.stdout.getvalue() - error = sys.stderr.getvalue() + # Only try to get sys.stdout and sys.sterr as they not be + # StringIO yet, e.g. when test fails during __call__ + try: + output = sys.stdout.getvalue() + except AttributeError: + output = None + try: + error = sys.stderr.getvalue() + except AttributeError: + error = None if output: if not output.endswith('\n'): output += '\n'