mirror of
https://github.com/python/cpython.git
synced 2025-08-01 07:33:08 +00:00
Addition of setUpClass and setUpModule shared fixtures to unittest.
This commit is contained in:
parent
0d4bfec134
commit
5ffa325a82
5 changed files with 600 additions and 11 deletions
|
@ -1,17 +1,13 @@
|
|||
"""TestSuite"""
|
||||
|
||||
import sys
|
||||
|
||||
from . import case
|
||||
from . import util
|
||||
|
||||
|
||||
class TestSuite(object):
|
||||
"""A test suite is a composite test consisting of a number of TestCases.
|
||||
|
||||
For use, create an instance of TestSuite, then add test case instances.
|
||||
When all tests have been added, the suite can be passed to a test
|
||||
runner, such as TextTestRunner. It will run the individual test cases
|
||||
in the order in which they were added, aggregating the results. When
|
||||
subclassing, do not forget to call the base class constructor.
|
||||
class BaseTestSuite(object):
|
||||
"""A simple test suite that doesn't provide class or module shared fixtures.
|
||||
"""
|
||||
def __init__(self, tests=()):
|
||||
self._tests = []
|
||||
|
@ -70,3 +66,190 @@ class TestSuite(object):
|
|||
"""Run the tests without collecting errors in a TestResult"""
|
||||
for test in self:
|
||||
test.debug()
|
||||
|
||||
|
||||
class TestSuite(BaseTestSuite):
|
||||
"""A test suite is a composite test consisting of a number of TestCases.
|
||||
|
||||
For use, create an instance of TestSuite, then add test case instances.
|
||||
When all tests have been added, the suite can be passed to a test
|
||||
runner, such as TextTestRunner. It will run the individual test cases
|
||||
in the order in which they were added, aggregating the results. When
|
||||
subclassing, do not forget to call the base class constructor.
|
||||
"""
|
||||
|
||||
|
||||
def run(self, result):
|
||||
self._wrapped_run(result)
|
||||
self._tearDownPreviousClass(None, result)
|
||||
self._handleModuleTearDown(result)
|
||||
return result
|
||||
|
||||
################################
|
||||
# private methods
|
||||
def _wrapped_run(self, result):
|
||||
for test in self:
|
||||
if result.shouldStop:
|
||||
break
|
||||
|
||||
if _isnotsuite(test):
|
||||
self._tearDownPreviousClass(test, result)
|
||||
self._handleModuleFixture(test, result)
|
||||
self._handleClassSetUp(test, result)
|
||||
result._previousTestClass = test.__class__
|
||||
|
||||
if (getattr(test.__class__, '_classSetupFailed', False) or
|
||||
getattr(result, '_moduleSetUpFailed', False)):
|
||||
continue
|
||||
|
||||
if hasattr(test, '_wrapped_run'):
|
||||
test._wrapped_run(result)
|
||||
else:
|
||||
test(result)
|
||||
|
||||
def _handleClassSetUp(self, test, result):
|
||||
previousClass = getattr(result, '_previousTestClass', None)
|
||||
currentClass = test.__class__
|
||||
if currentClass == previousClass:
|
||||
return
|
||||
if result._moduleSetUpFailed:
|
||||
return
|
||||
if getattr(currentClass, "__unittest_skip__", False):
|
||||
return
|
||||
|
||||
currentClass._classSetupFailed = False
|
||||
|
||||
setUpClass = getattr(currentClass, 'setUpClass', None)
|
||||
if setUpClass is not None:
|
||||
try:
|
||||
setUpClass()
|
||||
except:
|
||||
currentClass._classSetupFailed = True
|
||||
self._addClassSetUpError(result, currentClass)
|
||||
|
||||
def _get_previous_module(self, result):
|
||||
previousModule = None
|
||||
previousClass = getattr(result, '_previousTestClass', None)
|
||||
if previousClass is not None:
|
||||
previousModule = previousClass.__module__
|
||||
return previousModule
|
||||
|
||||
|
||||
def _handleModuleFixture(self, test, result):
|
||||
previousModule = self._get_previous_module(result)
|
||||
currentModule = test.__class__.__module__
|
||||
if currentModule == previousModule:
|
||||
return
|
||||
|
||||
self._handleModuleTearDown(result)
|
||||
|
||||
|
||||
result._moduleSetUpFailed = False
|
||||
try:
|
||||
module = sys.modules[currentModule]
|
||||
except KeyError:
|
||||
return
|
||||
setUpModule = getattr(module, 'setUpModule', None)
|
||||
if setUpModule is not None:
|
||||
try:
|
||||
setUpModule()
|
||||
except:
|
||||
result._moduleSetUpFailed = True
|
||||
error = _ErrorHolder('setUpModule (%s)' % currentModule)
|
||||
result.addError(error, sys.exc_info())
|
||||
|
||||
def _handleModuleTearDown(self, result):
|
||||
previousModule = self._get_previous_module(result)
|
||||
if previousModule is None:
|
||||
return
|
||||
if result._moduleSetUpFailed:
|
||||
return
|
||||
|
||||
try:
|
||||
module = sys.modules[previousModule]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
tearDownModule = getattr(module, 'tearDownModule', None)
|
||||
if tearDownModule is not None:
|
||||
try:
|
||||
tearDownModule()
|
||||
except:
|
||||
error = _ErrorHolder('tearDownModule (%s)' % previousModule)
|
||||
result.addError(error, sys.exc_info())
|
||||
|
||||
def _tearDownPreviousClass(self, test, result):
|
||||
previousClass = getattr(result, '_previousTestClass', None)
|
||||
currentClass = test.__class__
|
||||
if currentClass == previousClass:
|
||||
return
|
||||
if getattr(previousClass, '_classSetupFailed', False):
|
||||
return
|
||||
if getattr(result, '_moduleSetUpFailed', False):
|
||||
return
|
||||
if getattr(previousClass, "__unittest_skip__", False):
|
||||
return
|
||||
|
||||
tearDownClass = getattr(previousClass, 'tearDownClass', None)
|
||||
if tearDownClass is not None:
|
||||
try:
|
||||
tearDownClass()
|
||||
except:
|
||||
self._addClassTearDownError(result)
|
||||
|
||||
def _addClassTearDownError(self, result):
|
||||
className = util.strclass(result._previousTestClass)
|
||||
error = _ErrorHolder('classTearDown (%s)' % className)
|
||||
result.addError(error, sys.exc_info())
|
||||
|
||||
def _addClassSetUpError(self, result, klass):
|
||||
className = util.strclass(klass)
|
||||
error = _ErrorHolder('classSetUp (%s)' % className)
|
||||
result.addError(error, sys.exc_info())
|
||||
|
||||
|
||||
class _ErrorHolder(object):
|
||||
"""
|
||||
Placeholder for a TestCase inside a result. As far as a TestResult
|
||||
is concerned, this looks exactly like a unit test. Used to insert
|
||||
arbitrary errors into a test suite run.
|
||||
"""
|
||||
# Inspired by the ErrorHolder from Twisted:
|
||||
# http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
|
||||
|
||||
# attribute used by TestResult._exc_info_to_string
|
||||
failureException = None
|
||||
|
||||
def __init__(self, description):
|
||||
self.description = description
|
||||
|
||||
def id(self):
|
||||
return self.description
|
||||
|
||||
def shortDescription(self):
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
return "<ErrorHolder description=%r>" % (self.description,)
|
||||
|
||||
def __str__(self):
|
||||
return self.id()
|
||||
|
||||
def run(self, result):
|
||||
# could call result.addError(...) - but this test-like object
|
||||
# shouldn't be run anyway
|
||||
pass
|
||||
|
||||
def __call__(self, result):
|
||||
return self.run(result)
|
||||
|
||||
def countTestCases(self):
|
||||
return 0
|
||||
|
||||
def _isnotsuite(test):
|
||||
"A crude way to tell apart testcases and suites with duck-typing"
|
||||
try:
|
||||
iter(test)
|
||||
except TypeError:
|
||||
return True
|
||||
return False
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue