summaryrefslogtreecommitdiffstats
path: root/Lib/unittest/case.py
diff options
context:
space:
mode:
authorMichael Foord <fuzzyman@voidspace.org.uk>2010-12-19 03:19:47 (GMT)
committerMichael Foord <fuzzyman@voidspace.org.uk>2010-12-19 03:19:47 (GMT)
commitb3468f79efa45c8adaf86c0b9b797b9b3d4c12a2 (patch)
tree14e9ae4dcb071ee7bf154026ecac93c3ea352c7a /Lib/unittest/case.py
parentaddc6f5a21d5d04c5bf895e8ea46f67de129d75c (diff)
downloadcpython-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.py128
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)