11
def _call_if_exists(parent, attr):
12
func = getattr(parent, attr, lambda: None)
16
class BaseTestSuite(object):
17
"""A simple test suite that doesn't provide class or module shared fixtures.
21
def __init__(self, tests=()):
26
return "<%s tests=%s>" % (util.strclass(self.__class__), list(self))
28
def __eq__(self, other):
29
if not isinstance(other, self.__class__):
31
return list(self) == list(other)
33
def __ne__(self, other):
34
return not self == other
37
return iter(self._tests)
39
def countTestCases(self):
42
cases += test.countTestCases()
45
def addTest(self, test):
47
if not callable(test):
48
raise TypeError("{} is not callable".format(repr(test)))
49
if isinstance(test, type) and issubclass(test,
50
(case.TestCase, TestSuite)):
51
raise TypeError("TestCases and TestSuites must be instantiated "
52
"before passing them to addTest()")
53
self._tests.append(test)
55
def addTests(self, tests):
56
if isinstance(tests, str):
57
raise TypeError("tests must be an iterable of tests, not a string")
61
def run(self, result):
62
for index, test in enumerate(self):
67
self._removeTestAtIndex(index)
70
def _removeTestAtIndex(self, index):
71
"""Stop holding a reference to the TestCase at index."""
73
self._tests[index] = None
75
# support for suite implementations that have overriden self._test
78
def __call__(self, *args, **kwds):
79
return self.run(*args, **kwds)
82
"""Run the tests without collecting errors in a TestResult"""
87
class TestSuite(BaseTestSuite):
88
"""A test suite is a composite test consisting of a number of TestCases.
90
For use, create an instance of TestSuite, then add test case instances.
91
When all tests have been added, the suite can be passed to a test
92
runner, such as TextTestRunner. It will run the individual test cases
93
in the order in which they were added, aggregating the results. When
94
subclassing, do not forget to call the base class constructor.
97
def run(self, result, debug=False):
99
if getattr(result, '_testRunEntered', False) is False:
100
result._testRunEntered = topLevel = True
102
for index, test in enumerate(self):
103
if result.shouldStop:
106
if _isnotsuite(test):
107
self._tearDownPreviousClass(test, result)
108
self._handleModuleFixture(test, result)
109
self._handleClassSetUp(test, result)
110
result._previousTestClass = test.__class__
112
if (getattr(test.__class__, '_classSetupFailed', False) or
113
getattr(result, '_moduleSetUpFailed', False)):
122
self._removeTestAtIndex(index)
125
self._tearDownPreviousClass(None, result)
126
self._handleModuleTearDown(result)
127
result._testRunEntered = False
131
"""Run the tests without collecting errors in a TestResult"""
132
debug = _DebugResult()
133
self.run(debug, True)
135
################################
137
def _handleClassSetUp(self, test, result):
138
previousClass = getattr(result, '_previousTestClass', None)
139
currentClass = test.__class__
140
if currentClass == previousClass:
142
if result._moduleSetUpFailed:
144
if getattr(currentClass, "__unittest_skip__", False):
148
currentClass._classSetupFailed = False
150
# test may actually be a function
151
# so its class will be a builtin-type
154
setUpClass = getattr(currentClass, 'setUpClass', None)
155
if setUpClass is not None:
156
_call_if_exists(result, '_setupStdout')
159
except Exception as e:
160
if isinstance(result, _DebugResult):
162
currentClass._classSetupFailed = True
163
className = util.strclass(currentClass)
164
errorName = 'setUpClass (%s)' % className
165
self._addClassOrModuleLevelException(result, e, errorName)
167
_call_if_exists(result, '_restoreStdout')
169
def _get_previous_module(self, result):
170
previousModule = None
171
previousClass = getattr(result, '_previousTestClass', None)
172
if previousClass is not None:
173
previousModule = previousClass.__module__
174
return previousModule
177
def _handleModuleFixture(self, test, result):
178
previousModule = self._get_previous_module(result)
179
currentModule = test.__class__.__module__
180
if currentModule == previousModule:
183
self._handleModuleTearDown(result)
186
result._moduleSetUpFailed = False
188
module = sys.modules[currentModule]
191
setUpModule = getattr(module, 'setUpModule', None)
192
if setUpModule is not None:
193
_call_if_exists(result, '_setupStdout')
196
except Exception as e:
197
if isinstance(result, _DebugResult):
199
result._moduleSetUpFailed = True
200
errorName = 'setUpModule (%s)' % currentModule
201
self._addClassOrModuleLevelException(result, e, errorName)
203
_call_if_exists(result, '_restoreStdout')
205
def _addClassOrModuleLevelException(self, result, exception, errorName):
206
error = _ErrorHolder(errorName)
207
addSkip = getattr(result, 'addSkip', None)
208
if addSkip is not None and isinstance(exception, case.SkipTest):
209
addSkip(error, str(exception))
211
result.addError(error, sys.exc_info())
213
def _handleModuleTearDown(self, result):
214
previousModule = self._get_previous_module(result)
215
if previousModule is None:
217
if result._moduleSetUpFailed:
221
module = sys.modules[previousModule]
225
tearDownModule = getattr(module, 'tearDownModule', None)
226
if tearDownModule is not None:
227
_call_if_exists(result, '_setupStdout')
230
except Exception as e:
231
if isinstance(result, _DebugResult):
233
errorName = 'tearDownModule (%s)' % previousModule
234
self._addClassOrModuleLevelException(result, e, errorName)
236
_call_if_exists(result, '_restoreStdout')
238
def _tearDownPreviousClass(self, test, result):
239
previousClass = getattr(result, '_previousTestClass', None)
240
currentClass = test.__class__
241
if currentClass == previousClass:
243
if getattr(previousClass, '_classSetupFailed', False):
245
if getattr(result, '_moduleSetUpFailed', False):
247
if getattr(previousClass, "__unittest_skip__", False):
250
tearDownClass = getattr(previousClass, 'tearDownClass', None)
251
if tearDownClass is not None:
252
_call_if_exists(result, '_setupStdout')
255
except Exception as e:
256
if isinstance(result, _DebugResult):
258
className = util.strclass(previousClass)
259
errorName = 'tearDownClass (%s)' % className
260
self._addClassOrModuleLevelException(result, e, errorName)
262
_call_if_exists(result, '_restoreStdout')
265
class _ErrorHolder(object):
267
Placeholder for a TestCase inside a result. As far as a TestResult
268
is concerned, this looks exactly like a unit test. Used to insert
269
arbitrary errors into a test suite run.
271
# Inspired by the ErrorHolder from Twisted:
272
# http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
274
# attribute used by TestResult._exc_info_to_string
275
failureException = None
277
def __init__(self, description):
278
self.description = description
281
return self.description
283
def shortDescription(self):
287
return "<ErrorHolder description=%r>" % (self.description,)
292
def run(self, result):
293
# could call result.addError(...) - but this test-like object
294
# shouldn't be run anyway
297
def __call__(self, result):
298
return self.run(result)
300
def countTestCases(self):
303
def _isnotsuite(test):
304
"A crude way to tell apart testcases and suites with duck-typing"
312
class _DebugResult(object):
313
"Used by the TestSuite to hold previous class when running in debug."
314
_previousTestClass = None
315
_moduleSetUpFailed = False