summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorMichael Foord <fuzzyman@voidspace.org.uk>2009-05-02 20:15:05 (GMT)
committerMichael Foord <fuzzyman@voidspace.org.uk>2009-05-02 20:15:05 (GMT)
commite2fb98f467c05e089a177564d3077e888498a102 (patch)
tree29892e1a390e2a6b15a0cd22ad7209bf818eb297 /Lib
parent420d4eb1f3d647487a9c1bb855ec298653165624 (diff)
downloadcpython-e2fb98f467c05e089a177564d3077e888498a102.zip
cpython-e2fb98f467c05e089a177564d3077e888498a102.tar.gz
cpython-e2fb98f467c05e089a177564d3077e888498a102.tar.bz2
Add addCleanup and doCleanups to unittest.TestCase.
Closes issue 5679. Michael Foord
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_unittest.py108
-rw-r--r--Lib/unittest.py74
2 files changed, 158 insertions, 24 deletions
diff --git a/Lib/test/test_unittest.py b/Lib/test/test_unittest.py
index bb534fc..13bd78a 100644
--- a/Lib/test/test_unittest.py
+++ b/Lib/test/test_unittest.py
@@ -3041,6 +3041,111 @@ class TestLongMessage(TestCase):
"^unexpectedly identical: None : oops$"])
+class TestCleanUp(TestCase):
+
+ def testCleanUp(self):
+ class TestableTest(TestCase):
+ def testNothing(self):
+ pass
+
+ test = TestableTest('testNothing')
+ self.assertEqual(test._cleanups, [])
+
+ cleanups = []
+
+ def cleanup1(*args, **kwargs):
+ cleanups.append((1, args, kwargs))
+
+ def cleanup2(*args, **kwargs):
+ cleanups.append((2, args, kwargs))
+
+ test.addCleanup(cleanup1, 1, 2, 3, four='hello', five='goodbye')
+ test.addCleanup(cleanup2)
+
+ self.assertEqual(test._cleanups,
+ [(cleanup1, (1, 2, 3), dict(four='hello', five='goodbye')),
+ (cleanup2, (), {})])
+
+ result = test.doCleanups()
+ self.assertTrue(result)
+
+ self.assertEqual(cleanups, [(2, (), {}), (1, (1, 2, 3), dict(four='hello', five='goodbye'))])
+
+ def testCleanUpWithErrors(self):
+ class TestableTest(TestCase):
+ def testNothing(self):
+ pass
+
+ class MockResult(object):
+ errors = []
+ def addError(self, test, exc_info):
+ self.errors.append((test, exc_info))
+
+ result = MockResult()
+ test = TestableTest('testNothing')
+ test._result = result
+
+ exc1 = Exception('foo')
+ exc2 = Exception('bar')
+ def cleanup1():
+ raise exc1
+
+ def cleanup2():
+ raise exc2
+
+ test.addCleanup(cleanup1)
+ test.addCleanup(cleanup2)
+
+ self.assertFalse(test.doCleanups())
+
+ (test1, (Type1, instance1, _)), (test2, (Type2, instance2, _)) = reversed(MockResult.errors)
+ self.assertEqual((test1, Type1, instance1), (test, Exception, exc1))
+ self.assertEqual((test2, Type2, instance2), (test, Exception, exc2))
+
+ def testCleanupInRun(self):
+ blowUp = False
+ ordering = []
+
+ class TestableTest(TestCase):
+ def setUp(self):
+ ordering.append('setUp')
+ if blowUp:
+ raise Exception('foo')
+
+ def testNothing(self):
+ ordering.append('test')
+
+ def tearDown(self):
+ ordering.append('tearDown')
+
+ test = TestableTest('testNothing')
+
+ def cleanup1():
+ ordering.append('cleanup1')
+ def cleanup2():
+ ordering.append('cleanup2')
+ test.addCleanup(cleanup1)
+ test.addCleanup(cleanup2)
+
+ def success(some_test):
+ self.assertEqual(some_test, test)
+ ordering.append('success')
+
+ result = unittest.TestResult()
+ result.addSuccess = success
+
+ test.run(result)
+ self.assertEqual(ordering, ['setUp', 'test', 'tearDown',
+ 'cleanup2', 'cleanup1', 'success'])
+
+ blowUp = True
+ ordering = []
+ test = TestableTest('testNothing')
+ test.addCleanup(cleanup1)
+ test.run(result)
+ self.assertEqual(ordering, ['setUp', 'cleanup1'])
+
+
class Test_TestProgram(TestCase):
# Horrible white box test
@@ -3110,7 +3215,6 @@ class Test_TestProgram(TestCase):
testLoader=self.FooBarLoader())
-
######################################################################
## Main
######################################################################
@@ -3119,7 +3223,7 @@ def test_main():
test_support.run_unittest(Test_TestCase, Test_TestLoader,
Test_TestSuite, Test_TestResult, Test_FunctionTestCase,
Test_TestSkipping, Test_Assertions, TestLongMessage,
- Test_TestProgram)
+ Test_TestProgram, TestCleanUp)
if __name__ == "__main__":
test_main()
diff --git a/Lib/unittest.py b/Lib/unittest.py
index 43a2ced..d5c2927 100644
--- a/Lib/unittest.py
+++ b/Lib/unittest.py
@@ -340,12 +340,14 @@ class TestCase(object):
not have a method with the specified name.
"""
self._testMethodName = methodName
+ self._result = None
try:
testMethod = getattr(self, methodName)
except AttributeError:
raise ValueError("no such test method in %s: %s" % \
(self.__class__, methodName))
self._testMethodDoc = testMethod.__doc__
+ self._cleanups = []
# Map types to custom assertEqual functions that will compare
# instances of said type in more detail to generate a more useful
@@ -372,6 +374,14 @@ class TestCase(object):
"""
self._type_equality_funcs[typeobj] = _AssertWrapper(function)
+ def addCleanup(self, function, *args, **kwargs):
+ """Add a function, with arguments, to be called when the test is
+ completed. Functions added are called on a LIFO basis and are
+ called after tearDown on test failure or success.
+
+ Cleanup items are called even if setUp fails (unlike tearDown)."""
+ self._cleanups.append((function, args, kwargs))
+
def setUp(self):
"Hook method for setting up the test fixture before exercising it."
pass
@@ -429,44 +439,60 @@ class TestCase(object):
def run(self, result=None):
if result is None:
result = self.defaultTestResult()
+ self._result = result
result.startTest(self)
testMethod = getattr(self, self._testMethodName)
try:
- try:
- self.setUp()
- except SkipTest as e:
- result.addSkip(self, str(e))
- return
- except Exception:
- result.addError(self, sys.exc_info())
- return
-
success = False
try:
- testMethod()
- except self.failureException:
- result.addFailure(self, sys.exc_info())
- except _ExpectedFailure as e:
- result.addExpectedFailure(self, e.exc_info)
- except _UnexpectedSuccess:
- result.addUnexpectedSuccess(self)
+ self.setUp()
except SkipTest as e:
result.addSkip(self, str(e))
except Exception:
result.addError(self, sys.exc_info())
else:
- success = True
+ try:
+ testMethod()
+ except self.failureException:
+ result.addFailure(self, sys.exc_info())
+ except _ExpectedFailure as e:
+ result.addExpectedFailure(self, e.exc_info)
+ except _UnexpectedSuccess:
+ result.addUnexpectedSuccess(self)
+ except SkipTest as e:
+ result.addSkip(self, str(e))
+ except Exception:
+ result.addError(self, sys.exc_info())
+ else:
+ success = True
- try:
- self.tearDown()
- except Exception:
- result.addError(self, sys.exc_info())
- success = False
+ 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)
+ def doCleanups(self):
+ """Execute all cleanup functions. Normally called for you after
+ tearDown."""
+ result = self._result
+ ok = True
+ 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
+
def __call__(self, *args, **kwds):
return self.run(*args, **kwds)
@@ -1538,5 +1564,9 @@ Examples:
main = TestProgram
+##############################################################################
+# Executing this module from the command line
+##############################################################################
+
if __name__ == "__main__":
main(module=None)