bpo-24412: Adds cleanUps for setUpClass and setUpModule. (GH-9190)

This commit is contained in:
Lisa Roach 2018-11-08 18:34:33 -08:00 committed by GitHub
parent 49fa4a9f1e
commit 0f221d09ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 783 additions and 18 deletions

View File

@ -1448,6 +1448,39 @@ Test cases
.. versionadded:: 3.1
.. classmethod:: addClassCleanup(function, *args, **kwargs)
Add a function to be called after :meth:`tearDownClass` to cleanup
resources used during the test class. Functions will be called in reverse
order to the order they are added (:abbr:`LIFO (last-in, first-out)`).
They are called with any arguments and keyword arguments passed into
:meth:`addClassCleanup` when they are added.
If :meth:`setUpClass` fails, meaning that :meth:`tearDownClass` is not
called, then any cleanup functions added will still be called.
.. versionadded:: 3.8
.. classmethod:: doClassCleanups()
This method is called unconditionally after :meth:`tearDownClass`, or
after :meth:`setUpClass` if :meth:`setUpClass` raises an exception.
It is responsible for calling all the cleanup functions added by
:meth:`addCleanupClass`. If you need cleanup functions to be called
*prior* to :meth:`tearDownClass` then you can call
:meth:`doCleanupsClass` yourself.
:meth:`doCleanupsClass` pops methods off the stack of cleanup
functions one at a time, so it can be called at any time.
.. versionadded:: 3.8
.. class:: FunctionTestCase(testFunc, setUp=None, tearDown=None, description=None)
@ -2268,6 +2301,38 @@ module will be run and the ``tearDownModule`` will not be run. If the exception
:exc:`SkipTest` exception then the module will be reported as having been skipped
instead of as an error.
To add cleanup code that must be run even in the case of an exception, use
``addModuleCleanup``:
.. function:: addModuleCleanup(function, *args, **kwargs)
Add a function to be called after :func:`tearDownModule` to cleanup
resources used during the test class. Functions will be called in reverse
order to the order they are added (:abbr:`LIFO (last-in, first-out)`).
They are called with any arguments and keyword arguments passed into
:meth:`addModuleCleanup` when they are added.
If :meth:`setUpModule` fails, meaning that :func:`tearDownModule` is not
called, then any cleanup functions added will still be called.
.. versionadded:: 3.8
.. function:: doModuleCleanups()
This function is called unconditionally after :func:`tearDownModule`, or
after :func:`setUpModule` if :func:`setUpModule` raises an exception.
It is responsible for calling all the cleanup functions added by
:func:`addCleanupModule`. If you need cleanup functions to be called
*prior* to :func:`tearDownModule` then you can call
:func:`doModuleCleanups` yourself.
:func:`doModuleCleanups` pops methods off the stack of cleanup
functions one at a time, so it can be called at any time.
.. versionadded:: 3.8
Signal Handling
---------------

View File

@ -233,6 +233,15 @@ unicodedata
is in a specific normal form. (Contributed by Max Belanger and David Euresti in
:issue:`32285`).
unittest
--------
* Added :func:`~unittest.addModuleCleanup()` and
:meth:`~unittest.TestCase.addClassCleanup()` to unittest to support
cleanups for :func:`~unittest.setUpModule()` and
:meth:`~unittest.TestCase.setUpClass()`.
(Contributed by Lisa Roach in :issue:`24412`.)
venv
----

View File

@ -48,7 +48,8 @@ __all__ = ['TestResult', 'TestCase', 'TestSuite',
'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main',
'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless',
'expectedFailure', 'TextTestResult', 'installHandler',
'registerResult', 'removeResult', 'removeHandler']
'registerResult', 'removeResult', 'removeHandler',
'addModuleCleanup']
# Expose obsolete functions for backwards compatibility
__all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])
@ -56,8 +57,8 @@ __all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])
__unittest = True
from .result import TestResult
from .case import (TestCase, FunctionTestCase, SkipTest, skip, skipIf,
skipUnless, expectedFailure)
from .case import (addModuleCleanup, TestCase, FunctionTestCase, SkipTest, skip,
skipIf, skipUnless, expectedFailure)
from .suite import BaseTestSuite, TestSuite
from .loader import (TestLoader, defaultTestLoader, makeSuite, getTestCaseNames,
findTestCases)

View File

@ -84,6 +84,30 @@ class _Outcome(object):
def _id(obj):
return obj
_module_cleanups = []
def addModuleCleanup(function, *args, **kwargs):
"""Same as addCleanup, except the cleanup items are called even if
setUpModule fails (unlike tearDownModule)."""
_module_cleanups.append((function, args, kwargs))
def doModuleCleanups():
"""Execute all module cleanup functions. Normally called for you after
tearDownModule."""
exceptions = []
while _module_cleanups:
function, args, kwargs = _module_cleanups.pop()
try:
function(*args, **kwargs)
except Exception as exc:
exceptions.append(exc)
if exceptions:
# Swallows all but first exception. If a multi-exception handler
# gets written we should use that here instead.
raise exceptions[0]
def skip(reason):
"""
Unconditionally skip a test.
@ -390,6 +414,8 @@ class TestCase(object):
_classSetupFailed = False
_class_cleanups = []
def __init__(self, methodName='runTest'):
"""Create an instance of the class that will use the named test
method when executed. Raises a ValueError if the instance does
@ -445,6 +471,12 @@ class TestCase(object):
Cleanup items are called even if setUp fails (unlike tearDown)."""
self._cleanups.append((function, args, kwargs))
@classmethod
def addClassCleanup(cls, function, *args, **kwargs):
"""Same as addCleanup, except the cleanup items are called even if
setUpClass fails (unlike tearDownClass)."""
cls._class_cleanups.append((function, args, kwargs))
def setUp(self):
"Hook method for setting up the test fixture before exercising it."
pass
@ -651,9 +683,21 @@ class TestCase(object):
function(*args, **kwargs)
# return this for backwards compatibility
# even though we no longer us it internally
# even though we no longer use it internally
return outcome.success
@classmethod
def doClassCleanups(cls):
"""Execute all class cleanup functions. Normally called for you after
tearDownClass."""
cls.tearDown_exceptions = []
while cls._class_cleanups:
function, args, kwargs = cls._class_cleanups.pop()
try:
function(*args, **kwargs)
except Exception as exc:
cls.tearDown_exceptions.append(sys.exc_info())
def __call__(self, *args, **kwds):
return self.run(*args, **kwds)

View File

@ -166,10 +166,18 @@ class TestSuite(BaseTestSuite):
raise
currentClass._classSetupFailed = True
className = util.strclass(currentClass)
errorName = 'setUpClass (%s)' % className
self._addClassOrModuleLevelException(result, e, errorName)
self._createClassOrModuleLevelException(result, e,
'setUpClass',
className)
finally:
_call_if_exists(result, '_restoreStdout')
if currentClass._classSetupFailed is True:
currentClass.doClassCleanups()
if len(currentClass.tearDown_exceptions) > 0:
for exc in currentClass.tearDown_exceptions:
self._createClassOrModuleLevelException(
result, exc[1], 'setUpClass', className,
info=exc)
def _get_previous_module(self, result):
previousModule = None
@ -199,21 +207,37 @@ class TestSuite(BaseTestSuite):
try:
setUpModule()
except Exception as e:
try:
case.doModuleCleanups()
except Exception as exc:
self._createClassOrModuleLevelException(result, exc,
'setUpModule',
currentModule)
if isinstance(result, _DebugResult):
raise
result._moduleSetUpFailed = True
errorName = 'setUpModule (%s)' % currentModule
self._addClassOrModuleLevelException(result, e, errorName)
self._createClassOrModuleLevelException(result, e,
'setUpModule',
currentModule)
finally:
_call_if_exists(result, '_restoreStdout')
def _addClassOrModuleLevelException(self, result, exception, errorName):
def _createClassOrModuleLevelException(self, result, exc, method_name,
parent, info=None):
errorName = f'{method_name} ({parent})'
self._addClassOrModuleLevelException(result, exc, errorName, info)
def _addClassOrModuleLevelException(self, result, exception, errorName,
info=None):
error = _ErrorHolder(errorName)
addSkip = getattr(result, 'addSkip', None)
if addSkip is not None and isinstance(exception, case.SkipTest):
addSkip(error, str(exception))
else:
if not info:
result.addError(error, sys.exc_info())
else:
result.addError(error, info)
def _handleModuleTearDown(self, result):
previousModule = self._get_previous_module(result)
@ -235,10 +259,17 @@ class TestSuite(BaseTestSuite):
except Exception as e:
if isinstance(result, _DebugResult):
raise
errorName = 'tearDownModule (%s)' % previousModule
self._addClassOrModuleLevelException(result, e, errorName)
self._createClassOrModuleLevelException(result, e,
'tearDownModule',
previousModule)
finally:
_call_if_exists(result, '_restoreStdout')
try:
case.doModuleCleanups()
except Exception as e:
self._createClassOrModuleLevelException(result, e,
'tearDownModule',
previousModule)
def _tearDownPreviousClass(self, test, result):
previousClass = getattr(result, '_previousTestClass', None)
@ -261,10 +292,19 @@ class TestSuite(BaseTestSuite):
if isinstance(result, _DebugResult):
raise
className = util.strclass(previousClass)
errorName = 'tearDownClass (%s)' % className
self._addClassOrModuleLevelException(result, e, errorName)
self._createClassOrModuleLevelException(result, e,
'tearDownClass',
className)
finally:
_call_if_exists(result, '_restoreStdout')
previousClass.doClassCleanups()
if len(previousClass.tearDown_exceptions) > 0:
for exc in previousClass.tearDown_exceptions:
className = util.strclass(previousClass)
self._createClassOrModuleLevelException(result, exc[1],
'tearDownClass',
className,
info=exc)
class _ErrorHolder(object):

View File

@ -11,8 +11,41 @@ from unittest.test.support import (LoggingResult,
ResultWithNoStartTestRunStopTestRun)
class TestCleanUp(unittest.TestCase):
def resultFactory(*_):
return unittest.TestResult()
def getRunner():
return unittest.TextTestRunner(resultclass=resultFactory,
stream=io.StringIO())
def runTests(*cases):
suite = unittest.TestSuite()
for case in cases:
tests = unittest.defaultTestLoader.loadTestsFromTestCase(case)
suite.addTests(tests)
runner = getRunner()
# creating a nested suite exposes some potential bugs
realSuite = unittest.TestSuite()
realSuite.addTest(suite)
# adding empty suites to the end exposes potential bugs
suite.addTest(unittest.TestSuite())
realSuite.addTest(unittest.TestSuite())
return runner.run(realSuite)
def cleanup(ordering, blowUp=False):
if not blowUp:
ordering.append('cleanup_good')
else:
ordering.append('cleanup_exc')
raise Exception('CleanUpExc')
class TestCleanUp(unittest.TestCase):
def testCleanUp(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
@ -47,10 +80,10 @@ class TestCleanUp(unittest.TestCase):
test = TestableTest('testNothing')
outcome = test._outcome = _Outcome()
exc1 = Exception('foo')
CleanUpExc = Exception('foo')
exc2 = Exception('bar')
def cleanup1():
raise exc1
raise CleanUpExc
def cleanup2():
raise exc2
@ -63,7 +96,7 @@ class TestCleanUp(unittest.TestCase):
((_, (Type1, instance1, _)),
(_, (Type2, instance2, _))) = reversed(outcome.errors)
self.assertEqual((Type1, instance1), (Exception, exc1))
self.assertEqual((Type1, instance1), (Exception, CleanUpExc))
self.assertEqual((Type2, instance2), (Exception, exc2))
def testCleanupInRun(self):
@ -135,6 +168,575 @@ class TestCleanUp(unittest.TestCase):
self.assertEqual(ordering, ['setUp', 'test', 'tearDown', 'cleanup1', 'cleanup2'])
class TestClassCleanup(unittest.TestCase):
def test_addClassCleanUp(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
test = TestableTest('testNothing')
self.assertEqual(test._class_cleanups, [])
class_cleanups = []
def class_cleanup1(*args, **kwargs):
class_cleanups.append((3, args, kwargs))
def class_cleanup2(*args, **kwargs):
class_cleanups.append((4, args, kwargs))
TestableTest.addClassCleanup(class_cleanup1, 1, 2, 3,
four='hello', five='goodbye')
TestableTest.addClassCleanup(class_cleanup2)
self.assertEqual(test._class_cleanups,
[(class_cleanup1, (1, 2, 3),
dict(four='hello', five='goodbye')),
(class_cleanup2, (), {})])
TestableTest.doClassCleanups()
self.assertEqual(class_cleanups, [(4, (), {}), (3, (1, 2, 3),
dict(four='hello', five='goodbye'))])
def test_run_class_cleanUp(self):
ordering = []
blowUp = True
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering)
if blowUp:
raise Exception()
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
runTests(TestableTest)
self.assertEqual(ordering, ['setUpClass', 'cleanup_good'])
ordering = []
blowUp = False
runTests(TestableTest)
self.assertEqual(ordering,
['setUpClass', 'test', 'tearDownClass', 'cleanup_good'])
def test_debug_executes_classCleanUp(self):
ordering = []
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering)
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
suite.debug()
self.assertEqual(ordering,
['setUpClass', 'test', 'tearDownClass', 'cleanup_good'])
def test_doClassCleanups_with_errors_addClassCleanUp(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
pass
def cleanup1():
raise Exception('cleanup1')
def cleanup2():
raise Exception('cleanup2')
TestableTest.addClassCleanup(cleanup1)
TestableTest.addClassCleanup(cleanup2)
with self.assertRaises(Exception) as e:
TestableTest.doClassCleanups()
self.assertEquals(e, 'cleanup1')
def test_with_errors_addCleanUp(self):
ordering = []
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering)
def setUp(self):
ordering.append('setUp')
self.addCleanup(cleanup, ordering, blowUp=True)
def testNothing(self):
pass
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'setUp', 'cleanup_exc',
'tearDownClass', 'cleanup_good'])
def test_run_with_errors_addClassCleanUp(self):
ordering = []
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering, blowUp=True)
def setUp(self):
ordering.append('setUp')
self.addCleanup(cleanup, ordering)
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'setUp', 'test', 'cleanup_good',
'tearDownClass', 'cleanup_exc'])
def test_with_errors_in_addClassCleanup_and_setUps(self):
ordering = []
class_blow_up = False
method_blow_up = False
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering, blowUp=True)
if class_blow_up:
raise Exception('ClassExc')
def setUp(self):
ordering.append('setUp')
if method_blow_up:
raise Exception('MethodExc')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'setUp', 'test',
'tearDownClass', 'cleanup_exc'])
ordering = []
class_blow_up = True
method_blow_up = False
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: ClassExc')
self.assertEqual(result.errors[1][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'cleanup_exc'])
ordering = []
class_blow_up = False
method_blow_up = True
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: MethodExc')
self.assertEqual(result.errors[1][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(ordering,
['setUpClass', 'setUp', 'tearDownClass',
'cleanup_exc'])
class TestModuleCleanUp(unittest.TestCase):
def test_add_and_do_ModuleCleanup(self):
module_cleanups = []
def module_cleanup1(*args, **kwargs):
module_cleanups.append((3, args, kwargs))
def module_cleanup2(*args, **kwargs):
module_cleanups.append((4, args, kwargs))
class Module(object):
unittest.addModuleCleanup(module_cleanup1, 1, 2, 3,
four='hello', five='goodbye')
unittest.addModuleCleanup(module_cleanup2)
self.assertEqual(unittest.case._module_cleanups,
[(module_cleanup1, (1, 2, 3),
dict(four='hello', five='goodbye')),
(module_cleanup2, (), {})])
unittest.case.doModuleCleanups()
self.assertEqual(module_cleanups, [(4, (), {}), (3, (1, 2, 3),
dict(four='hello', five='goodbye'))])
self.assertEqual(unittest.case._module_cleanups, [])
def test_doModuleCleanup_with_errors_in_addModuleCleanup(self):
module_cleanups = []
def module_cleanup_good(*args, **kwargs):
module_cleanups.append((3, args, kwargs))
def module_cleanup_bad(*args, **kwargs):
raise Exception('CleanUpExc')
class Module(object):
unittest.addModuleCleanup(module_cleanup_good, 1, 2, 3,
four='hello', five='goodbye')
unittest.addModuleCleanup(module_cleanup_bad)
self.assertEqual(unittest.case._module_cleanups,
[(module_cleanup_good, (1, 2, 3),
dict(four='hello', five='goodbye')),
(module_cleanup_bad, (), {})])
with self.assertRaises(Exception) as e:
unittest.case.doModuleCleanups()
self.assertEqual(str(e.exception), 'CleanUpExc')
self.assertEqual(unittest.case._module_cleanups, [])
def test_run_module_cleanUp(self):
blowUp = True
ordering = []
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
if blowUp:
raise Exception('setUpModule Exc')
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
result = runTests(TestableTest)
self.assertEqual(ordering, ['setUpModule', 'cleanup_good'])
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: setUpModule Exc')
ordering = []
blowUp = False
runTests(TestableTest)
self.assertEqual(ordering,
['setUpModule', 'setUpClass', 'test', 'tearDownClass',
'tearDownModule', 'cleanup_good'])
self.assertEqual(unittest.case._module_cleanups, [])
def test_run_multiple_module_cleanUp(self):
blowUp = True
blowUp2 = False
ordering = []
class Module1(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
if blowUp:
raise Exception()
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class Module2(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule2')
unittest.addModuleCleanup(cleanup, ordering)
if blowUp2:
raise Exception()
@staticmethod
def tearDownModule():
ordering.append('tearDownModule2')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
class TestableTest2(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass2')
def testNothing(self):
ordering.append('test2')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass2')
TestableTest.__module__ = 'Module1'
sys.modules['Module1'] = Module1
TestableTest2.__module__ = 'Module2'
sys.modules['Module2'] = Module2
runTests(TestableTest, TestableTest2)
self.assertEqual(ordering, ['setUpModule', 'cleanup_good',
'setUpModule2', 'setUpClass2', 'test2',
'tearDownClass2', 'tearDownModule2',
'cleanup_good'])
ordering = []
blowUp = False
blowUp2 = True
runTests(TestableTest, TestableTest2)
self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
'tearDownClass', 'tearDownModule',
'cleanup_good', 'setUpModule2',
'cleanup_good'])
ordering = []
blowUp = False
blowUp2 = False
runTests(TestableTest, TestableTest2)
self.assertEqual(ordering,
['setUpModule', 'setUpClass', 'test', 'tearDownClass',
'tearDownModule', 'cleanup_good', 'setUpModule2',
'setUpClass2', 'test2', 'tearDownClass2',
'tearDownModule2', 'cleanup_good'])
self.assertEqual(unittest.case._module_cleanups, [])
def test_debug_module_executes_cleanUp(self):
ordering = []
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
suite.debug()
self.assertEqual(ordering,
['setUpModule', 'setUpClass', 'test', 'tearDownClass',
'tearDownModule', 'cleanup_good'])
self.assertEqual(unittest.case._module_cleanups, [])
def test_with_errors_in_addClassCleanup(self):
ordering = []
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering, blowUp=True)
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(ordering,
['setUpModule', 'setUpClass', 'test', 'tearDownClass',
'cleanup_exc', 'tearDownModule', 'cleanup_good'])
def test_with_errors_in_addCleanup(self):
ordering = []
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
def setUp(self):
ordering.append('setUp')
self.addCleanup(cleanup, ordering, blowUp=True)
def testNothing(self):
ordering.append('test')
def tearDown(self):
ordering.append('tearDown')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(ordering,
['setUpModule', 'setUp', 'test', 'tearDown',
'cleanup_exc', 'tearDownModule', 'cleanup_good'])
def test_with_errors_in_addModuleCleanup_and_setUps(self):
ordering = []
module_blow_up = False
class_blow_up = False
method_blow_up = False
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering, blowUp=True)
if module_blow_up:
raise Exception('ModuleExc')
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
if class_blow_up:
raise Exception('ClassExc')
def setUp(self):
ordering.append('setUp')
if method_blow_up:
raise Exception('MethodExc')
def testNothing(self):
ordering.append('test')
@classmethod
def tearDownClass(cls):
ordering.append('tearDownClass')
TestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(ordering,
['setUpModule', 'setUpClass', 'setUp', 'test',
'tearDownClass', 'tearDownModule',
'cleanup_exc'])
ordering = []
module_blow_up = True
class_blow_up = False
method_blow_up = False
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(result.errors[1][1].splitlines()[-1],
'Exception: ModuleExc')
self.assertEqual(ordering, ['setUpModule', 'cleanup_exc'])
ordering = []
module_blow_up = False
class_blow_up = True
method_blow_up = False
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: ClassExc')
self.assertEqual(result.errors[1][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(ordering, ['setUpModule', 'setUpClass',
'tearDownModule', 'cleanup_exc'])
ordering = []
module_blow_up = False
class_blow_up = False
method_blow_up = True
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
'Exception: MethodExc')
self.assertEqual(result.errors[1][1].splitlines()[-1],
'Exception: CleanUpExc')
self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'setUp',
'tearDownClass', 'tearDownModule',
'cleanup_exc'])
def test_module_cleanUp_with_multiple_classes(self):
ordering =[]
def cleanup1():
ordering.append('cleanup1')
def cleanup2():
ordering.append('cleanup2')
def cleanup3():
ordering.append('cleanup3')
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup1)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
class TestableTest(unittest.TestCase):
def setUp(self):
ordering.append('setUp')
self.addCleanup(cleanup2)
def testNothing(self):
ordering.append('test')
def tearDown(self):
ordering.append('tearDown')
class OtherTestableTest(unittest.TestCase):
def setUp(self):
ordering.append('setUp2')
self.addCleanup(cleanup3)
def testNothing(self):
ordering.append('test2')
def tearDown(self):
ordering.append('tearDown2')
TestableTest.__module__ = 'Module'
OtherTestableTest.__module__ = 'Module'
sys.modules['Module'] = Module
runTests(TestableTest, OtherTestableTest)
self.assertEqual(ordering,
['setUpModule', 'setUp', 'test', 'tearDown',
'cleanup2', 'setUp2', 'test2', 'tearDown2',
'cleanup3', 'tearDownModule', 'cleanup1'])
class Test_TextTestRunner(unittest.TestCase):
"""Tests for TextTestRunner."""

View File

@ -0,0 +1,4 @@
Add :func:`~unittest.addModuleCleanup()` and
:meth:`~unittest.TestCase.addClassCleanup()` to unittest to support
cleanups for :func:`~unittest.setUpModule()` and
:meth:`~unittest.TestCase.setUpClass()`. Patch by Lisa Roach.