1 | #!/usr/bin/env python
|
---|
2 | '''
|
---|
3 | Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's
|
---|
4 | Smalltalk testing framework.
|
---|
5 |
|
---|
6 | This module contains the core framework classes that form the basis of
|
---|
7 | specific test cases and suites (TestCase, TestSuite etc.), and also a
|
---|
8 | text-based utility class for running the tests and reporting the results
|
---|
9 | (TextTestRunner).
|
---|
10 |
|
---|
11 | Simple usage:
|
---|
12 |
|
---|
13 | import unittest
|
---|
14 |
|
---|
15 | class IntegerArithmenticTestCase(unittest.TestCase):
|
---|
16 | def testAdd(self): ## test method names begin 'test*'
|
---|
17 | self.assertEquals((1 + 2), 3)
|
---|
18 | self.assertEquals(0 + 1, 1)
|
---|
19 | def testMultiply(self):
|
---|
20 | self.assertEquals((0 * 10), 0)
|
---|
21 | self.assertEquals((5 * 8), 40)
|
---|
22 |
|
---|
23 | if __name__ == '__main__':
|
---|
24 | unittest.main()
|
---|
25 |
|
---|
26 | Further information is available in the bundled documentation, and from
|
---|
27 |
|
---|
28 | http://docs.python.org/lib/module-unittest.html
|
---|
29 |
|
---|
30 | Copyright (c) 1999-2003 Steve Purcell
|
---|
31 | This module is free software, and you may redistribute it and/or modify
|
---|
32 | it under the same terms as Python itself, so long as this copyright message
|
---|
33 | and disclaimer are retained in their original form.
|
---|
34 |
|
---|
35 | IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
|
---|
36 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
|
---|
37 | THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
---|
38 | DAMAGE.
|
---|
39 |
|
---|
40 | THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
|
---|
41 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
---|
42 | PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
|
---|
43 | AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
|
---|
44 | SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
---|
45 | '''
|
---|
46 |
|
---|
47 | __author__ = "Steve Purcell"
|
---|
48 | __email__ = "stephen_purcell at yahoo dot com"
|
---|
49 | __version__ = "#Revision: 1.63 $"[11:-2]
|
---|
50 |
|
---|
51 | import time
|
---|
52 | import sys
|
---|
53 | import traceback
|
---|
54 | import os
|
---|
55 | import types
|
---|
56 |
|
---|
57 | ##############################################################################
|
---|
58 | # Exported classes and functions
|
---|
59 | ##############################################################################
|
---|
60 | __all__ = ['TestResult', 'TestCase', 'TestSuite', 'TextTestRunner',
|
---|
61 | 'TestLoader', 'FunctionTestCase', 'main', 'defaultTestLoader']
|
---|
62 |
|
---|
63 | # Expose obsolete functions for backwards compatibility
|
---|
64 | __all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])
|
---|
65 |
|
---|
66 |
|
---|
67 | ##############################################################################
|
---|
68 | # Backward compatibility
|
---|
69 | ##############################################################################
|
---|
70 | if sys.version_info[:2] < (2, 2):
|
---|
71 | def isinstance(obj, clsinfo):
|
---|
72 | import __builtin__
|
---|
73 | if type(clsinfo) in (tuple, list):
|
---|
74 | for cls in clsinfo:
|
---|
75 | if cls is type: cls = types.ClassType
|
---|
76 | if __builtin__.isinstance(obj, cls):
|
---|
77 | return 1
|
---|
78 | return 0
|
---|
79 | else: return __builtin__.isinstance(obj, clsinfo)
|
---|
80 |
|
---|
81 | def _CmpToKey(mycmp):
|
---|
82 | 'Convert a cmp= function into a key= function'
|
---|
83 | class K(object):
|
---|
84 | def __init__(self, obj):
|
---|
85 | self.obj = obj
|
---|
86 | def __lt__(self, other):
|
---|
87 | return mycmp(self.obj, other.obj) == -1
|
---|
88 | return K
|
---|
89 |
|
---|
90 | ##############################################################################
|
---|
91 | # Test framework core
|
---|
92 | ##############################################################################
|
---|
93 |
|
---|
94 | # All classes defined herein are 'new-style' classes, allowing use of 'super()'
|
---|
95 | __metaclass__ = type
|
---|
96 |
|
---|
97 | def _strclass(cls):
|
---|
98 | return "%s.%s" % (cls.__module__, cls.__name__)
|
---|
99 |
|
---|
100 | __unittest = 1
|
---|
101 |
|
---|
102 | class TestResult:
|
---|
103 | """Holder for test result information.
|
---|
104 |
|
---|
105 | Test results are automatically managed by the TestCase and TestSuite
|
---|
106 | classes, and do not need to be explicitly manipulated by writers of tests.
|
---|
107 |
|
---|
108 | Each instance holds the total number of tests run, and collections of
|
---|
109 | failures and errors that occurred among those test runs. The collections
|
---|
110 | contain tuples of (testcase, exceptioninfo), where exceptioninfo is the
|
---|
111 | formatted traceback of the error that occurred.
|
---|
112 | """
|
---|
113 | def __init__(self):
|
---|
114 | self.failures = []
|
---|
115 | self.errors = []
|
---|
116 | self.testsRun = 0
|
---|
117 | self.shouldStop = False
|
---|
118 |
|
---|
119 | def startTest(self, test):
|
---|
120 | "Called when the given test is about to be run"
|
---|
121 | self.testsRun = self.testsRun + 1
|
---|
122 |
|
---|
123 | def stopTest(self, test):
|
---|
124 | "Called when the given test has been run"
|
---|
125 | pass
|
---|
126 |
|
---|
127 | def addError(self, test, err):
|
---|
128 | """Called when an error has occurred. 'err' is a tuple of values as
|
---|
129 | returned by sys.exc_info().
|
---|
130 | """
|
---|
131 | self.errors.append((test, self._exc_info_to_string(err, test)))
|
---|
132 |
|
---|
133 | def addFailure(self, test, err):
|
---|
134 | """Called when an error has occurred. 'err' is a tuple of values as
|
---|
135 | returned by sys.exc_info()."""
|
---|
136 | self.failures.append((test, self._exc_info_to_string(err, test)))
|
---|
137 |
|
---|
138 | def addSuccess(self, test):
|
---|
139 | "Called when a test has completed successfully"
|
---|
140 | pass
|
---|
141 |
|
---|
142 | def wasSuccessful(self):
|
---|
143 | "Tells whether or not this result was a success"
|
---|
144 | return len(self.failures) == len(self.errors) == 0
|
---|
145 |
|
---|
146 | def stop(self):
|
---|
147 | "Indicates that the tests should be aborted"
|
---|
148 | self.shouldStop = True
|
---|
149 |
|
---|
150 | def _exc_info_to_string(self, err, test):
|
---|
151 | """Converts a sys.exc_info()-style tuple of values into a string."""
|
---|
152 | exctype, value, tb = err
|
---|
153 | # Skip test runner traceback levels
|
---|
154 | while tb and self._is_relevant_tb_level(tb):
|
---|
155 | tb = tb.tb_next
|
---|
156 | if exctype is test.failureException:
|
---|
157 | # Skip assert*() traceback levels
|
---|
158 | length = self._count_relevant_tb_levels(tb)
|
---|
159 | return ''.join(traceback.format_exception(exctype, value, tb, length))
|
---|
160 | return ''.join(traceback.format_exception(exctype, value, tb))
|
---|
161 |
|
---|
162 | def _is_relevant_tb_level(self, tb):
|
---|
163 | return '__unittest' in tb.tb_frame.f_globals
|
---|
164 |
|
---|
165 | def _count_relevant_tb_levels(self, tb):
|
---|
166 | length = 0
|
---|
167 | while tb and not self._is_relevant_tb_level(tb):
|
---|
168 | length += 1
|
---|
169 | tb = tb.tb_next
|
---|
170 | return length
|
---|
171 |
|
---|
172 | def __repr__(self):
|
---|
173 | return "<%s run=%i errors=%i failures=%i>" % \
|
---|
174 | (_strclass(self.__class__), self.testsRun, len(self.errors),
|
---|
175 | len(self.failures))
|
---|
176 |
|
---|
177 | class TestCase:
|
---|
178 | """A class whose instances are single test cases.
|
---|
179 |
|
---|
180 | By default, the test code itself should be placed in a method named
|
---|
181 | 'runTest'.
|
---|
182 |
|
---|
183 | If the fixture may be used for many test cases, create as
|
---|
184 | many test methods as are needed. When instantiating such a TestCase
|
---|
185 | subclass, specify in the constructor arguments the name of the test method
|
---|
186 | that the instance is to execute.
|
---|
187 |
|
---|
188 | Test authors should subclass TestCase for their own tests. Construction
|
---|
189 | and deconstruction of the test's environment ('fixture') can be
|
---|
190 | implemented by overriding the 'setUp' and 'tearDown' methods respectively.
|
---|
191 |
|
---|
192 | If it is necessary to override the __init__ method, the base class
|
---|
193 | __init__ method must always be called. It is important that subclasses
|
---|
194 | should not change the signature of their __init__ method, since instances
|
---|
195 | of the classes are instantiated automatically by parts of the framework
|
---|
196 | in order to be run.
|
---|
197 | """
|
---|
198 |
|
---|
199 | # This attribute determines which exception will be raised when
|
---|
200 | # the instance's assertion methods fail; test methods raising this
|
---|
201 | # exception will be deemed to have 'failed' rather than 'errored'
|
---|
202 |
|
---|
203 | failureException = AssertionError
|
---|
204 |
|
---|
205 | def __init__(self, methodName='runTest'):
|
---|
206 | """Create an instance of the class that will use the named test
|
---|
207 | method when executed. Raises a ValueError if the instance does
|
---|
208 | not have a method with the specified name.
|
---|
209 | """
|
---|
210 | try:
|
---|
211 | self._testMethodName = methodName
|
---|
212 | testMethod = getattr(self, methodName)
|
---|
213 | self._testMethodDoc = testMethod.__doc__
|
---|
214 | except AttributeError:
|
---|
215 | raise ValueError, "no such test method in %s: %s" % \
|
---|
216 | (self.__class__, methodName)
|
---|
217 |
|
---|
218 | def setUp(self):
|
---|
219 | "Hook method for setting up the test fixture before exercising it."
|
---|
220 | pass
|
---|
221 |
|
---|
222 | def tearDown(self):
|
---|
223 | "Hook method for deconstructing the test fixture after testing it."
|
---|
224 | pass
|
---|
225 |
|
---|
226 | def countTestCases(self):
|
---|
227 | return 1
|
---|
228 |
|
---|
229 | def defaultTestResult(self):
|
---|
230 | return TestResult()
|
---|
231 |
|
---|
232 | def shortDescription(self):
|
---|
233 | """Returns a one-line description of the test, or None if no
|
---|
234 | description has been provided.
|
---|
235 |
|
---|
236 | The default implementation of this method returns the first line of
|
---|
237 | the specified test method's docstring.
|
---|
238 | """
|
---|
239 | doc = self._testMethodDoc
|
---|
240 | return doc and doc.split("\n")[0].strip() or None
|
---|
241 |
|
---|
242 | def id(self):
|
---|
243 | return "%s.%s" % (_strclass(self.__class__), self._testMethodName)
|
---|
244 |
|
---|
245 | def __eq__(self, other):
|
---|
246 | if type(self) is not type(other):
|
---|
247 | return False
|
---|
248 |
|
---|
249 | return self._testMethodName == other._testMethodName
|
---|
250 |
|
---|
251 | def __ne__(self, other):
|
---|
252 | return not self == other
|
---|
253 |
|
---|
254 | def __hash__(self):
|
---|
255 | return hash((type(self), self._testMethodName))
|
---|
256 |
|
---|
257 | def __str__(self):
|
---|
258 | return "%s (%s)" % (self._testMethodName, _strclass(self.__class__))
|
---|
259 |
|
---|
260 | def __repr__(self):
|
---|
261 | return "<%s testMethod=%s>" % \
|
---|
262 | (_strclass(self.__class__), self._testMethodName)
|
---|
263 |
|
---|
264 | def run(self, result=None):
|
---|
265 | if result is None: result = self.defaultTestResult()
|
---|
266 | result.startTest(self)
|
---|
267 | testMethod = getattr(self, self._testMethodName)
|
---|
268 | try:
|
---|
269 | try:
|
---|
270 | self.setUp()
|
---|
271 | except KeyboardInterrupt:
|
---|
272 | raise
|
---|
273 | except:
|
---|
274 | result.addError(self, self._exc_info())
|
---|
275 | return
|
---|
276 |
|
---|
277 | ok = False
|
---|
278 | try:
|
---|
279 | testMethod()
|
---|
280 | ok = True
|
---|
281 | except self.failureException:
|
---|
282 | result.addFailure(self, self._exc_info())
|
---|
283 | except KeyboardInterrupt:
|
---|
284 | raise
|
---|
285 | except:
|
---|
286 | result.addError(self, self._exc_info())
|
---|
287 |
|
---|
288 | try:
|
---|
289 | self.tearDown()
|
---|
290 | except KeyboardInterrupt:
|
---|
291 | raise
|
---|
292 | except:
|
---|
293 | result.addError(self, self._exc_info())
|
---|
294 | ok = False
|
---|
295 | if ok: result.addSuccess(self)
|
---|
296 | finally:
|
---|
297 | result.stopTest(self)
|
---|
298 |
|
---|
299 | def __call__(self, *args, **kwds):
|
---|
300 | return self.run(*args, **kwds)
|
---|
301 |
|
---|
302 | def debug(self):
|
---|
303 | """Run the test without collecting errors in a TestResult"""
|
---|
304 | self.setUp()
|
---|
305 | getattr(self, self._testMethodName)()
|
---|
306 | self.tearDown()
|
---|
307 |
|
---|
308 | def _exc_info(self):
|
---|
309 | """Return a version of sys.exc_info() with the traceback frame
|
---|
310 | minimised; usually the top level of the traceback frame is not
|
---|
311 | needed.
|
---|
312 | """
|
---|
313 | return sys.exc_info()
|
---|
314 |
|
---|
315 | def fail(self, msg=None):
|
---|
316 | """Fail immediately, with the given message."""
|
---|
317 | raise self.failureException, msg
|
---|
318 |
|
---|
319 | def failIf(self, expr, msg=None):
|
---|
320 | "Fail the test if the expression is true."
|
---|
321 | if expr: raise self.failureException, msg
|
---|
322 |
|
---|
323 | def failUnless(self, expr, msg=None):
|
---|
324 | """Fail the test unless the expression is true."""
|
---|
325 | if not expr: raise self.failureException, msg
|
---|
326 |
|
---|
327 | def failUnlessRaises(self, excClass, callableObj, *args, **kwargs):
|
---|
328 | """Fail unless an exception of class excClass is thrown
|
---|
329 | by callableObj when invoked with arguments args and keyword
|
---|
330 | arguments kwargs. If a different type of exception is
|
---|
331 | thrown, it will not be caught, and the test case will be
|
---|
332 | deemed to have suffered an error, exactly as for an
|
---|
333 | unexpected exception.
|
---|
334 | """
|
---|
335 | try:
|
---|
336 | callableObj(*args, **kwargs)
|
---|
337 | except excClass:
|
---|
338 | return
|
---|
339 | else:
|
---|
340 | if hasattr(excClass,'__name__'): excName = excClass.__name__
|
---|
341 | else: excName = str(excClass)
|
---|
342 | raise self.failureException, "%s not raised" % excName
|
---|
343 |
|
---|
344 | def failUnlessEqual(self, first, second, msg=None):
|
---|
345 | """Fail if the two objects are unequal as determined by the '=='
|
---|
346 | operator.
|
---|
347 | """
|
---|
348 | if not first == second:
|
---|
349 | raise self.failureException, \
|
---|
350 | (msg or '%r != %r' % (first, second))
|
---|
351 |
|
---|
352 | def failIfEqual(self, first, second, msg=None):
|
---|
353 | """Fail if the two objects are equal as determined by the '=='
|
---|
354 | operator.
|
---|
355 | """
|
---|
356 | if first == second:
|
---|
357 | raise self.failureException, \
|
---|
358 | (msg or '%r == %r' % (first, second))
|
---|
359 |
|
---|
360 | def failUnlessAlmostEqual(self, first, second, places=7, msg=None):
|
---|
361 | """Fail if the two objects are unequal as determined by their
|
---|
362 | difference rounded to the given number of decimal places
|
---|
363 | (default 7) and comparing to zero.
|
---|
364 |
|
---|
365 | Note that decimal places (from zero) are usually not the same
|
---|
366 | as significant digits (measured from the most signficant digit).
|
---|
367 | """
|
---|
368 | if round(abs(second-first), places) != 0:
|
---|
369 | raise self.failureException, \
|
---|
370 | (msg or '%r != %r within %r places' % (first, second, places))
|
---|
371 |
|
---|
372 | def failIfAlmostEqual(self, first, second, places=7, msg=None):
|
---|
373 | """Fail if the two objects are equal as determined by their
|
---|
374 | difference rounded to the given number of decimal places
|
---|
375 | (default 7) and comparing to zero.
|
---|
376 |
|
---|
377 | Note that decimal places (from zero) are usually not the same
|
---|
378 | as significant digits (measured from the most signficant digit).
|
---|
379 | """
|
---|
380 | if round(abs(second-first), places) == 0:
|
---|
381 | raise self.failureException, \
|
---|
382 | (msg or '%r == %r within %r places' % (first, second, places))
|
---|
383 |
|
---|
384 | # Synonyms for assertion methods
|
---|
385 |
|
---|
386 | assertEqual = assertEquals = failUnlessEqual
|
---|
387 |
|
---|
388 | assertNotEqual = assertNotEquals = failIfEqual
|
---|
389 |
|
---|
390 | assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual
|
---|
391 |
|
---|
392 | assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual
|
---|
393 |
|
---|
394 | assertRaises = failUnlessRaises
|
---|
395 |
|
---|
396 | assert_ = assertTrue = failUnless
|
---|
397 |
|
---|
398 | assertFalse = failIf
|
---|
399 |
|
---|
400 |
|
---|
401 |
|
---|
402 | class TestSuite:
|
---|
403 | """A test suite is a composite test consisting of a number of TestCases.
|
---|
404 |
|
---|
405 | For use, create an instance of TestSuite, then add test case instances.
|
---|
406 | When all tests have been added, the suite can be passed to a test
|
---|
407 | runner, such as TextTestRunner. It will run the individual test cases
|
---|
408 | in the order in which they were added, aggregating the results. When
|
---|
409 | subclassing, do not forget to call the base class constructor.
|
---|
410 | """
|
---|
411 | def __init__(self, tests=()):
|
---|
412 | self._tests = []
|
---|
413 | self.addTests(tests)
|
---|
414 |
|
---|
415 | def __repr__(self):
|
---|
416 | return "<%s tests=%s>" % (_strclass(self.__class__), self._tests)
|
---|
417 |
|
---|
418 | __str__ = __repr__
|
---|
419 |
|
---|
420 | def __eq__(self, other):
|
---|
421 | if type(self) is not type(other):
|
---|
422 | return False
|
---|
423 | return self._tests == other._tests
|
---|
424 |
|
---|
425 | def __ne__(self, other):
|
---|
426 | return not self == other
|
---|
427 |
|
---|
428 | # Can't guarantee hash invariant, so flag as unhashable
|
---|
429 | __hash__ = None
|
---|
430 |
|
---|
431 | def __iter__(self):
|
---|
432 | return iter(self._tests)
|
---|
433 |
|
---|
434 | def countTestCases(self):
|
---|
435 | cases = 0
|
---|
436 | for test in self._tests:
|
---|
437 | cases += test.countTestCases()
|
---|
438 | return cases
|
---|
439 |
|
---|
440 | def addTest(self, test):
|
---|
441 | # sanity checks
|
---|
442 | if not hasattr(test, '__call__'):
|
---|
443 | raise TypeError("the test to add must be callable")
|
---|
444 | if (isinstance(test, (type, types.ClassType)) and
|
---|
445 | issubclass(test, (TestCase, TestSuite))):
|
---|
446 | raise TypeError("TestCases and TestSuites must be instantiated "
|
---|
447 | "before passing them to addTest()")
|
---|
448 | self._tests.append(test)
|
---|
449 |
|
---|
450 | def addTests(self, tests):
|
---|
451 | if isinstance(tests, basestring):
|
---|
452 | raise TypeError("tests must be an iterable of tests, not a string")
|
---|
453 | for test in tests:
|
---|
454 | self.addTest(test)
|
---|
455 |
|
---|
456 | def run(self, result):
|
---|
457 | for test in self._tests:
|
---|
458 | if result.shouldStop:
|
---|
459 | break
|
---|
460 | test(result)
|
---|
461 | return result
|
---|
462 |
|
---|
463 | def __call__(self, *args, **kwds):
|
---|
464 | return self.run(*args, **kwds)
|
---|
465 |
|
---|
466 | def debug(self):
|
---|
467 | """Run the tests without collecting errors in a TestResult"""
|
---|
468 | for test in self._tests: test.debug()
|
---|
469 |
|
---|
470 |
|
---|
471 | class FunctionTestCase(TestCase):
|
---|
472 | """A test case that wraps a test function.
|
---|
473 |
|
---|
474 | This is useful for slipping pre-existing test functions into the
|
---|
475 | unittest framework. Optionally, set-up and tidy-up functions can be
|
---|
476 | supplied. As with TestCase, the tidy-up ('tearDown') function will
|
---|
477 | always be called if the set-up ('setUp') function ran successfully.
|
---|
478 | """
|
---|
479 |
|
---|
480 | def __init__(self, testFunc, setUp=None, tearDown=None,
|
---|
481 | description=None):
|
---|
482 | TestCase.__init__(self)
|
---|
483 | self.__setUpFunc = setUp
|
---|
484 | self.__tearDownFunc = tearDown
|
---|
485 | self.__testFunc = testFunc
|
---|
486 | self.__description = description
|
---|
487 |
|
---|
488 | def setUp(self):
|
---|
489 | if self.__setUpFunc is not None:
|
---|
490 | self.__setUpFunc()
|
---|
491 |
|
---|
492 | def tearDown(self):
|
---|
493 | if self.__tearDownFunc is not None:
|
---|
494 | self.__tearDownFunc()
|
---|
495 |
|
---|
496 | def runTest(self):
|
---|
497 | self.__testFunc()
|
---|
498 |
|
---|
499 | def id(self):
|
---|
500 | return self.__testFunc.__name__
|
---|
501 |
|
---|
502 | def __eq__(self, other):
|
---|
503 | if type(self) is not type(other):
|
---|
504 | return False
|
---|
505 |
|
---|
506 | return self.__setUpFunc == other.__setUpFunc and \
|
---|
507 | self.__tearDownFunc == other.__tearDownFunc and \
|
---|
508 | self.__testFunc == other.__testFunc and \
|
---|
509 | self.__description == other.__description
|
---|
510 |
|
---|
511 | def __ne__(self, other):
|
---|
512 | return not self == other
|
---|
513 |
|
---|
514 | def __hash__(self):
|
---|
515 | return hash((type(self), self.__setUpFunc, self.__tearDownFunc,
|
---|
516 | self.__testFunc, self.__description))
|
---|
517 |
|
---|
518 | def __str__(self):
|
---|
519 | return "%s (%s)" % (_strclass(self.__class__), self.__testFunc.__name__)
|
---|
520 |
|
---|
521 | def __repr__(self):
|
---|
522 | return "<%s testFunc=%s>" % (_strclass(self.__class__), self.__testFunc)
|
---|
523 |
|
---|
524 | def shortDescription(self):
|
---|
525 | if self.__description is not None: return self.__description
|
---|
526 | doc = self.__testFunc.__doc__
|
---|
527 | return doc and doc.split("\n")[0].strip() or None
|
---|
528 |
|
---|
529 |
|
---|
530 |
|
---|
531 | ##############################################################################
|
---|
532 | # Locating and loading tests
|
---|
533 | ##############################################################################
|
---|
534 |
|
---|
535 | class TestLoader:
|
---|
536 | """This class is responsible for loading tests according to various
|
---|
537 | criteria and returning them wrapped in a TestSuite
|
---|
538 | """
|
---|
539 | testMethodPrefix = 'test'
|
---|
540 | sortTestMethodsUsing = cmp
|
---|
541 | suiteClass = TestSuite
|
---|
542 |
|
---|
543 | def loadTestsFromTestCase(self, testCaseClass):
|
---|
544 | """Return a suite of all tests cases contained in testCaseClass"""
|
---|
545 | if issubclass(testCaseClass, TestSuite):
|
---|
546 | raise TypeError("Test cases should not be derived from TestSuite. Maybe you meant to derive from TestCase?")
|
---|
547 | testCaseNames = self.getTestCaseNames(testCaseClass)
|
---|
548 | if not testCaseNames and hasattr(testCaseClass, 'runTest'):
|
---|
549 | testCaseNames = ['runTest']
|
---|
550 | return self.suiteClass(map(testCaseClass, testCaseNames))
|
---|
551 |
|
---|
552 | def loadTestsFromModule(self, module):
|
---|
553 | """Return a suite of all tests cases contained in the given module"""
|
---|
554 | tests = []
|
---|
555 | for name in dir(module):
|
---|
556 | obj = getattr(module, name)
|
---|
557 | if (isinstance(obj, (type, types.ClassType)) and
|
---|
558 | issubclass(obj, TestCase)):
|
---|
559 | tests.append(self.loadTestsFromTestCase(obj))
|
---|
560 | return self.suiteClass(tests)
|
---|
561 |
|
---|
562 | def loadTestsFromName(self, name, module=None):
|
---|
563 | """Return a suite of all tests cases given a string specifier.
|
---|
564 |
|
---|
565 | The name may resolve either to a module, a test case class, a
|
---|
566 | test method within a test case class, or a callable object which
|
---|
567 | returns a TestCase or TestSuite instance.
|
---|
568 |
|
---|
569 | The method optionally resolves the names relative to a given module.
|
---|
570 | """
|
---|
571 | parts = name.split('.')
|
---|
572 | if module is None:
|
---|
573 | parts_copy = parts[:]
|
---|
574 | while parts_copy:
|
---|
575 | try:
|
---|
576 | module = __import__('.'.join(parts_copy))
|
---|
577 | break
|
---|
578 | except ImportError:
|
---|
579 | del parts_copy[-1]
|
---|
580 | if not parts_copy: raise
|
---|
581 | parts = parts[1:]
|
---|
582 | obj = module
|
---|
583 | for part in parts:
|
---|
584 | parent, obj = obj, getattr(obj, part)
|
---|
585 |
|
---|
586 | if type(obj) == types.ModuleType:
|
---|
587 | return self.loadTestsFromModule(obj)
|
---|
588 | elif (isinstance(obj, (type, types.ClassType)) and
|
---|
589 | issubclass(obj, TestCase)):
|
---|
590 | return self.loadTestsFromTestCase(obj)
|
---|
591 | elif (type(obj) == types.UnboundMethodType and
|
---|
592 | isinstance(parent, (type, types.ClassType)) and
|
---|
593 | issubclass(parent, TestCase)):
|
---|
594 | return TestSuite([parent(obj.__name__)])
|
---|
595 | elif isinstance(obj, TestSuite):
|
---|
596 | return obj
|
---|
597 | elif hasattr(obj, '__call__'):
|
---|
598 | test = obj()
|
---|
599 | if isinstance(test, TestSuite):
|
---|
600 | return test
|
---|
601 | elif isinstance(test, TestCase):
|
---|
602 | return TestSuite([test])
|
---|
603 | else:
|
---|
604 | raise TypeError("calling %s returned %s, not a test" %
|
---|
605 | (obj, test))
|
---|
606 | else:
|
---|
607 | raise TypeError("don't know how to make test from: %s" % obj)
|
---|
608 |
|
---|
609 | def loadTestsFromNames(self, names, module=None):
|
---|
610 | """Return a suite of all tests cases found using the given sequence
|
---|
611 | of string specifiers. See 'loadTestsFromName()'.
|
---|
612 | """
|
---|
613 | suites = [self.loadTestsFromName(name, module) for name in names]
|
---|
614 | return self.suiteClass(suites)
|
---|
615 |
|
---|
616 | def getTestCaseNames(self, testCaseClass):
|
---|
617 | """Return a sorted sequence of method names found within testCaseClass
|
---|
618 | """
|
---|
619 | def isTestMethod(attrname, testCaseClass=testCaseClass, prefix=self.testMethodPrefix):
|
---|
620 | return attrname.startswith(prefix) and hasattr(getattr(testCaseClass, attrname), '__call__')
|
---|
621 | testFnNames = filter(isTestMethod, dir(testCaseClass))
|
---|
622 | if self.sortTestMethodsUsing:
|
---|
623 | testFnNames.sort(key=_CmpToKey(self.sortTestMethodsUsing))
|
---|
624 | return testFnNames
|
---|
625 |
|
---|
626 |
|
---|
627 |
|
---|
628 | defaultTestLoader = TestLoader()
|
---|
629 |
|
---|
630 |
|
---|
631 | ##############################################################################
|
---|
632 | # Patches for old functions: these functions should be considered obsolete
|
---|
633 | ##############################################################################
|
---|
634 |
|
---|
635 | def _makeLoader(prefix, sortUsing, suiteClass=None):
|
---|
636 | loader = TestLoader()
|
---|
637 | loader.sortTestMethodsUsing = sortUsing
|
---|
638 | loader.testMethodPrefix = prefix
|
---|
639 | if suiteClass: loader.suiteClass = suiteClass
|
---|
640 | return loader
|
---|
641 |
|
---|
642 | def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp):
|
---|
643 | return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass)
|
---|
644 |
|
---|
645 | def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
|
---|
646 | return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass)
|
---|
647 |
|
---|
648 | def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
|
---|
649 | return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module)
|
---|
650 |
|
---|
651 |
|
---|
652 | ##############################################################################
|
---|
653 | # Text UI
|
---|
654 | ##############################################################################
|
---|
655 |
|
---|
656 | class _WritelnDecorator:
|
---|
657 | """Used to decorate file-like objects with a handy 'writeln' method"""
|
---|
658 | def __init__(self,stream):
|
---|
659 | self.stream = stream
|
---|
660 |
|
---|
661 | def __getattr__(self, attr):
|
---|
662 | return getattr(self.stream,attr)
|
---|
663 |
|
---|
664 | def writeln(self, arg=None):
|
---|
665 | if arg: self.write(arg)
|
---|
666 | self.write('\n') # text-mode streams translate to \r\n if needed
|
---|
667 |
|
---|
668 |
|
---|
669 | class _TextTestResult(TestResult):
|
---|
670 | """A test result class that can print formatted text results to a stream.
|
---|
671 |
|
---|
672 | Used by TextTestRunner.
|
---|
673 | """
|
---|
674 | separator1 = '=' * 70
|
---|
675 | separator2 = '-' * 70
|
---|
676 |
|
---|
677 | def __init__(self, stream, descriptions, verbosity):
|
---|
678 | TestResult.__init__(self)
|
---|
679 | self.stream = stream
|
---|
680 | self.showAll = verbosity > 1
|
---|
681 | self.dots = verbosity == 1
|
---|
682 | self.descriptions = descriptions
|
---|
683 |
|
---|
684 | def getDescription(self, test):
|
---|
685 | if self.descriptions:
|
---|
686 | return test.shortDescription() or str(test)
|
---|
687 | else:
|
---|
688 | return str(test)
|
---|
689 |
|
---|
690 | def startTest(self, test):
|
---|
691 | TestResult.startTest(self, test)
|
---|
692 | if self.showAll:
|
---|
693 | self.stream.write(self.getDescription(test))
|
---|
694 | self.stream.write(" ... ")
|
---|
695 | self.stream.flush()
|
---|
696 |
|
---|
697 | def addSuccess(self, test):
|
---|
698 | TestResult.addSuccess(self, test)
|
---|
699 | if self.showAll:
|
---|
700 | self.stream.writeln("ok")
|
---|
701 | elif self.dots:
|
---|
702 | self.stream.write('.')
|
---|
703 | self.stream.flush()
|
---|
704 |
|
---|
705 | def addError(self, test, err):
|
---|
706 | TestResult.addError(self, test, err)
|
---|
707 | if self.showAll:
|
---|
708 | self.stream.writeln("ERROR")
|
---|
709 | elif self.dots:
|
---|
710 | self.stream.write('E')
|
---|
711 | self.stream.flush()
|
---|
712 |
|
---|
713 | def addFailure(self, test, err):
|
---|
714 | TestResult.addFailure(self, test, err)
|
---|
715 | if self.showAll:
|
---|
716 | self.stream.writeln("FAIL")
|
---|
717 | elif self.dots:
|
---|
718 | self.stream.write('F')
|
---|
719 | self.stream.flush()
|
---|
720 |
|
---|
721 | def printErrors(self):
|
---|
722 | if self.dots or self.showAll:
|
---|
723 | self.stream.writeln()
|
---|
724 | self.printErrorList('ERROR', self.errors)
|
---|
725 | self.printErrorList('FAIL', self.failures)
|
---|
726 |
|
---|
727 | def printErrorList(self, flavour, errors):
|
---|
728 | for test, err in errors:
|
---|
729 | self.stream.writeln(self.separator1)
|
---|
730 | self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
|
---|
731 | self.stream.writeln(self.separator2)
|
---|
732 | self.stream.writeln("%s" % err)
|
---|
733 |
|
---|
734 |
|
---|
735 | class TextTestRunner:
|
---|
736 | """A test runner class that displays results in textual form.
|
---|
737 |
|
---|
738 | It prints out the names of tests as they are run, errors as they
|
---|
739 | occur, and a summary of the results at the end of the test run.
|
---|
740 | """
|
---|
741 | def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1):
|
---|
742 | self.stream = _WritelnDecorator(stream)
|
---|
743 | self.descriptions = descriptions
|
---|
744 | self.verbosity = verbosity
|
---|
745 |
|
---|
746 | def _makeResult(self):
|
---|
747 | return _TextTestResult(self.stream, self.descriptions, self.verbosity)
|
---|
748 |
|
---|
749 | def run(self, test):
|
---|
750 | "Run the given test case or test suite."
|
---|
751 | result = self._makeResult()
|
---|
752 | startTime = time.time()
|
---|
753 | test(result)
|
---|
754 | stopTime = time.time()
|
---|
755 | timeTaken = stopTime - startTime
|
---|
756 | result.printErrors()
|
---|
757 | self.stream.writeln(result.separator2)
|
---|
758 | run = result.testsRun
|
---|
759 | self.stream.writeln("Ran %d test%s in %.3fs" %
|
---|
760 | (run, run != 1 and "s" or "", timeTaken))
|
---|
761 | self.stream.writeln()
|
---|
762 | if not result.wasSuccessful():
|
---|
763 | self.stream.write("FAILED (")
|
---|
764 | failed, errored = map(len, (result.failures, result.errors))
|
---|
765 | if failed:
|
---|
766 | self.stream.write("failures=%d" % failed)
|
---|
767 | if errored:
|
---|
768 | if failed: self.stream.write(", ")
|
---|
769 | self.stream.write("errors=%d" % errored)
|
---|
770 | self.stream.writeln(")")
|
---|
771 | else:
|
---|
772 | self.stream.writeln("OK")
|
---|
773 | return result
|
---|
774 |
|
---|
775 |
|
---|
776 |
|
---|
777 | ##############################################################################
|
---|
778 | # Facilities for running tests from the command line
|
---|
779 | ##############################################################################
|
---|
780 |
|
---|
781 | class TestProgram:
|
---|
782 | """A command-line program that runs a set of tests; this is primarily
|
---|
783 | for making test modules conveniently executable.
|
---|
784 | """
|
---|
785 | USAGE = """\
|
---|
786 | Usage: %(progName)s [options] [test] [...]
|
---|
787 |
|
---|
788 | Options:
|
---|
789 | -h, --help Show this message
|
---|
790 | -v, --verbose Verbose output
|
---|
791 | -q, --quiet Minimal output
|
---|
792 |
|
---|
793 | Examples:
|
---|
794 | %(progName)s - run default set of tests
|
---|
795 | %(progName)s MyTestSuite - run suite 'MyTestSuite'
|
---|
796 | %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething
|
---|
797 | %(progName)s MyTestCase - run all 'test*' test methods
|
---|
798 | in MyTestCase
|
---|
799 | """
|
---|
800 | def __init__(self, module='__main__', defaultTest=None,
|
---|
801 | argv=None, testRunner=None,
|
---|
802 | testLoader=defaultTestLoader):
|
---|
803 | if type(module) == type(''):
|
---|
804 | self.module = __import__(module)
|
---|
805 | for part in module.split('.')[1:]:
|
---|
806 | self.module = getattr(self.module, part)
|
---|
807 | else:
|
---|
808 | self.module = module
|
---|
809 | if argv is None:
|
---|
810 | argv = sys.argv
|
---|
811 | self.verbosity = 1
|
---|
812 | self.defaultTest = defaultTest
|
---|
813 | self.testRunner = testRunner
|
---|
814 | self.testLoader = testLoader
|
---|
815 | self.progName = os.path.basename(argv[0])
|
---|
816 | self.parseArgs(argv)
|
---|
817 | self.runTests()
|
---|
818 |
|
---|
819 | def usageExit(self, msg=None):
|
---|
820 | if msg: print msg
|
---|
821 | print self.USAGE % self.__dict__
|
---|
822 | sys.exit(2)
|
---|
823 |
|
---|
824 | def parseArgs(self, argv):
|
---|
825 | import getopt
|
---|
826 | try:
|
---|
827 | options, args = getopt.getopt(argv[1:], 'hHvq',
|
---|
828 | ['help','verbose','quiet'])
|
---|
829 | for opt, value in options:
|
---|
830 | if opt in ('-h','-H','--help'):
|
---|
831 | self.usageExit()
|
---|
832 | if opt in ('-q','--quiet'):
|
---|
833 | self.verbosity = 0
|
---|
834 | if opt in ('-v','--verbose'):
|
---|
835 | self.verbosity = 2
|
---|
836 | if len(args) == 0 and self.defaultTest is None:
|
---|
837 | self.test = self.testLoader.loadTestsFromModule(self.module)
|
---|
838 | return
|
---|
839 | if len(args) > 0:
|
---|
840 | self.testNames = args
|
---|
841 | else:
|
---|
842 | self.testNames = (self.defaultTest,)
|
---|
843 | self.createTests()
|
---|
844 | except getopt.error, msg:
|
---|
845 | self.usageExit(msg)
|
---|
846 |
|
---|
847 | def createTests(self):
|
---|
848 | self.test = self.testLoader.loadTestsFromNames(self.testNames,
|
---|
849 | self.module)
|
---|
850 |
|
---|
851 | def runTests(self):
|
---|
852 | if self.testRunner is None:
|
---|
853 | self.testRunner = TextTestRunner
|
---|
854 |
|
---|
855 | if isinstance(self.testRunner, (type, types.ClassType)):
|
---|
856 | try:
|
---|
857 | testRunner = self.testRunner(verbosity=self.verbosity)
|
---|
858 | except TypeError:
|
---|
859 | # didn't accept the verbosity argument
|
---|
860 | testRunner = self.testRunner()
|
---|
861 | else:
|
---|
862 | # it is assumed to be a TestRunner instance
|
---|
863 | testRunner = self.testRunner
|
---|
864 | result = testRunner.run(self.test)
|
---|
865 | sys.exit(not result.wasSuccessful())
|
---|
866 |
|
---|
867 | main = TestProgram
|
---|
868 |
|
---|
869 |
|
---|
870 | ##############################################################################
|
---|
871 | # Executing this module from the command line
|
---|
872 | ##############################################################################
|
---|
873 |
|
---|
874 | if __name__ == "__main__":
|
---|
875 | main(module=None)
|
---|