self.addCleanup(foo.unlock)
...
+Cleanups can also report multiple errors, if appropriate by wrapping them in
+a testtools.MultipleExceptions object::
+
+ raise MultipleExceptions(exc_info1, exc_info2)
+
TestCase.addOnException
~~~~~~~~~~~~~~~~~~~~~~~
Improvements
------------
+* Cleanups can raise ``testtools.MultipleExceptions`` if they have multiple
+ exceptions to report. For instance, a cleanup which is itself responsible for
+ running several different internal cleanup routines might use this.
+
* Code duplication between assertEqual and the matcher Equals has been removed.
* In normal circumstances, a TestCase will no longer share details with clones
'ErrorHolder',
'ExtendedToOriginalDecorator',
'iterate_tests',
+ 'MultipleExceptions',
'MultiTestResult',
'PlaceHolder',
'TestCase',
)
from testtools.testcase import (
ErrorHolder,
+ MultipleExceptions,
PlaceHolder,
TestCase,
clone_test_with_new_id,
__metaclass__ = type
__all__ = [
'clone_test_with_new_id',
+ 'MultipleExceptions',
'TestCase',
'skip',
'skipIf',
"""
+class MultipleExceptions(Exception):
+ """Represents many exceptions raised from some operation.
+
+ :ivar args: The sys.exc_info() tuples for each exception.
+ """
+
+
class TestCase(unittest.TestCase):
"""Extensions to the basic TestCase.
except KeyboardInterrupt:
raise
except:
- exc_info = sys.exc_info()
- self._report_traceback(exc_info)
- last_exception = exc_info[1]
+ exceptions = [sys.exc_info()]
+ while exceptions:
+ exc_info = exceptions.pop()
+ if exc_info[0] is MultipleExceptions:
+ exceptions.extend(exc_info[1].args)
+ continue
+ self._report_traceback(exc_info)
+ last_exception = exc_info[1]
return last_exception
def addCleanup(self, function, *arguments, **keywordArguments):
from testtools import (
ErrorHolder,
+ MultipleExceptions,
PlaceHolder,
TestCase,
clone_test_with_new_id,
self.assertRaises(
KeyboardInterrupt, self.test.run, self.logging_result)
+ def test_all_errors_from_MultipleExceptions_reported(self):
+ # When a MultipleExceptions exception is caught, all the errors are
+ # reported.
+ def raiseMany():
+ try:
+ 1/0
+ except Exception:
+ exc_info1 = sys.exc_info()
+ try:
+ 1/0
+ except Exception:
+ exc_info2 = sys.exc_info()
+ raise MultipleExceptions(exc_info1, exc_info2)
+ self.test.addCleanup(raiseMany)
+ self.logging_result = ExtendedTestResult()
+ self.test.run(self.logging_result)
+ self.assertEqual(['startTest', 'addError', 'stopTest'],
+ [event[0] for event in self.logging_result._events])
+ self.assertEqual(set(['traceback', 'traceback-1']),
+ set(self.logging_result._events[1][2].keys()))
+
def test_multipleCleanupErrorsReported(self):
# Errors from all failing cleanups are reported as separate backtraces.
self.test.addCleanup(lambda: 1/0)