diff options
author | Michael Foord <fuzzyman@voidspace.org.uk> | 2010-12-19 03:19:47 (GMT) |
---|---|---|
committer | Michael Foord <fuzzyman@voidspace.org.uk> | 2010-12-19 03:19:47 (GMT) |
commit | b3468f79efa45c8adaf86c0b9b797b9b3d4c12a2 (patch) | |
tree | 14e9ae4dcb071ee7bf154026ecac93c3ea352c7a /Lib/unittest/case.py | |
parent | addc6f5a21d5d04c5bf895e8ea46f67de129d75c (diff) | |
download | cpython-b3468f79efa45c8adaf86c0b9b797b9b3d4c12a2.zip cpython-b3468f79efa45c8adaf86c0b9b797b9b3d4c12a2.tar.gz cpython-b3468f79efa45c8adaf86c0b9b797b9b3d4c12a2.tar.bz2 |
Issue 10611. Issue 9857. Improve the way exception handling, including test skipping, is done inside TestCase.run
Diffstat (limited to 'Lib/unittest/case.py')
-rw-r--r-- | Lib/unittest/case.py | 128 |
1 files changed, 78 insertions, 50 deletions
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 177a2fe..b02d475 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -25,7 +25,6 @@ class SkipTest(Exception): Usually you can use TestResult.skip() or one of the skipping decorators instead of raising this directly. """ - pass class _ExpectedFailure(Exception): """ @@ -42,7 +41,17 @@ class _UnexpectedSuccess(Exception): """ The test was supposed to fail, but it didn't! """ - pass + + +class _Outcome(object): + def __init__(self): + self.success = True + self.skipped = None + self.unexpectedSuccess = None + self.expectedFailure = None + self.errors = [] + self.failures = [] + def _id(obj): return obj @@ -263,7 +272,7 @@ class TestCase(object): not have a method with the specified name. """ self._testMethodName = methodName - self._resultForDoCleanups = None + self._outcomeForDoCleanups = None try: testMethod = getattr(self, methodName) except AttributeError: @@ -367,6 +376,36 @@ class TestCase(object): RuntimeWarning, 2) result.addSuccess(self) + def _executeTestPart(self, function, outcome, isTest=False): + try: + function() + except KeyboardInterrupt: + raise + except SkipTest as e: + outcome.success = False + outcome.skipped = str(e) + except _UnexpectedSuccess: + exc_info = sys.exc_info() + outcome.success = False + if isTest: + outcome.unexpectedSuccess = exc_info + else: + outcome.errors.append(exc_info) + except _ExpectedFailure: + outcome.success = False + exc_info = sys.exc_info() + if isTest: + outcome.expectedFailure = exc_info + else: + outcome.errors.append(exc_info) + except self.failureException: + outcome.success = False + outcome.failures.append(sys.exc_info()) + exc_info = sys.exc_info() + except: + outcome.success = False + outcome.errors.append(sys.exc_info()) + def run(self, result=None): orig_result = result if result is None: @@ -375,7 +414,6 @@ class TestCase(object): if startTestRun is not None: startTestRun() - self._resultForDoCleanups = result result.startTest(self) testMethod = getattr(self, self._testMethodName) @@ -390,51 +428,42 @@ class TestCase(object): result.stopTest(self) return try: - success = False - try: - self.setUp() - except SkipTest as e: - self._addSkip(result, str(e)) - except Exception: - result.addError(self, sys.exc_info()) + outcome = _Outcome() + self._outcomeForDoCleanups = outcome + + self._executeTestPart(self.setUp, outcome) + if outcome.success: + self._executeTestPart(testMethod, outcome, isTest=True) + self._executeTestPart(self.tearDown, outcome) + + self.doCleanups() + if outcome.success: + result.addSuccess(self) else: - try: - testMethod() - except self.failureException: - result.addFailure(self, sys.exc_info()) - except _ExpectedFailure as e: - addExpectedFailure = getattr(result, 'addExpectedFailure', None) - if addExpectedFailure is not None: - addExpectedFailure(self, e.exc_info) - else: - warnings.warn("TestResult has no addExpectedFailure method, reporting as passes", - RuntimeWarning) - result.addSuccess(self) - except _UnexpectedSuccess: + if outcome.skipped is not None: + self._addSkip(result, outcome.skipped) + for exc_info in outcome.errors: + result.addError(self, exc_info) + for exc_info in outcome.failures: + result.addFailure(self, exc_info) + if outcome.unexpectedSuccess is not None: addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None) if addUnexpectedSuccess is not None: addUnexpectedSuccess(self) else: warnings.warn("TestResult has no addUnexpectedSuccess method, reporting as failures", RuntimeWarning) - result.addFailure(self, sys.exc_info()) - except SkipTest as e: - self._addSkip(result, str(e)) - except Exception: - result.addError(self, sys.exc_info()) - else: - success = True + result.addFailure(self, outcome.unexpectedSuccess) + + if outcome.expectedFailure is not None: + addExpectedFailure = getattr(result, 'addExpectedFailure', None) + if addExpectedFailure is not None: + addExpectedFailure(self, outcome.expectedFailure) + else: + warnings.warn("TestResult has no addExpectedFailure method, reporting as passes", + RuntimeWarning) + result.addSuccess(self) - try: - self.tearDown() - except Exception: - result.addError(self, sys.exc_info()) - success = False - - cleanUpSuccess = self.doCleanups() - success = success and cleanUpSuccess - if success: - result.addSuccess(self) finally: result.stopTest(self) if orig_result is None: @@ -445,16 +474,15 @@ class TestCase(object): def doCleanups(self): """Execute all cleanup functions. Normally called for you after tearDown.""" - result = self._resultForDoCleanups - ok = True + outcome = self._outcomeForDoCleanups or _Outcome() while self._cleanups: - function, args, kwargs = self._cleanups.pop(-1) - try: - function(*args, **kwargs) - except Exception: - ok = False - result.addError(self, sys.exc_info()) - return ok + function, args, kwargs = self._cleanups.pop() + part = lambda: function(*args, **kwargs) + self._executeTestPart(part, outcome) + + # return this for backwards compatibility + # even though we no longer us it internally + return outcome.success def __call__(self, *args, **kwds): return self.run(*args, **kwds) |