Mercurial > p > roundup > code
comparison test/unittest.py @ 210:40d7be9708f6
Am now bundling unittest with the package so everyone can use the unit tests.
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Mon, 06 Aug 2001 23:57:20 +0000 |
| parents | |
| children | 6978960e8e06 |
comparison
equal
deleted
inserted
replaced
| 209:e1b2b0dd0fd4 | 210:40d7be9708f6 |
|---|---|
| 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://pyunit.sourceforge.net/ | |
| 29 | |
| 30 Copyright (c) 1999, 2000, 2001 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 print 'hi' | |
| 48 | |
| 49 __author__ = "Steve Purcell" | |
| 50 __email__ = "stephen_purcell at yahoo dot com" | |
| 51 __version__ = "$Revision: 1.1 $"[11:-2] | |
| 52 | |
| 53 import time | |
| 54 import sys | |
| 55 import traceback | |
| 56 import string | |
| 57 import os | |
| 58 import types | |
| 59 | |
| 60 ############################################################################## | |
| 61 # Test framework core | |
| 62 ############################################################################## | |
| 63 | |
| 64 class TestResult: | |
| 65 """Holder for test result information. | |
| 66 | |
| 67 Test results are automatically managed by the TestCase and TestSuite | |
| 68 classes, and do not need to be explicitly manipulated by writers of tests. | |
| 69 | |
| 70 Each instance holds the total number of tests run, and collections of | |
| 71 failures and errors that occurred among those test runs. The collections | |
| 72 contain tuples of (testcase, exceptioninfo), where exceptioninfo is a | |
| 73 tuple of values as returned by sys.exc_info(). | |
| 74 """ | |
| 75 def __init__(self): | |
| 76 self.failures = [] | |
| 77 self.errors = [] | |
| 78 self.testsRun = 0 | |
| 79 self.shouldStop = 0 | |
| 80 | |
| 81 def startTest(self, test): | |
| 82 "Called when the given test is about to be run" | |
| 83 self.testsRun = self.testsRun + 1 | |
| 84 | |
| 85 def stopTest(self, test): | |
| 86 "Called when the given test has been run" | |
| 87 pass | |
| 88 | |
| 89 def addError(self, test, err): | |
| 90 "Called when an error has occurred" | |
| 91 self.errors.append((test, err)) | |
| 92 | |
| 93 def addFailure(self, test, err): | |
| 94 "Called when a failure has occurred" | |
| 95 self.failures.append((test, err)) | |
| 96 | |
| 97 def addSuccess(self, test): | |
| 98 "Called when a test has completed successfully" | |
| 99 pass | |
| 100 | |
| 101 def wasSuccessful(self): | |
| 102 "Tells whether or not this result was a success" | |
| 103 return len(self.failures) == len(self.errors) == 0 | |
| 104 | |
| 105 def stop(self): | |
| 106 "Indicates that the tests should be aborted" | |
| 107 self.shouldStop = 1 | |
| 108 | |
| 109 def __repr__(self): | |
| 110 return "<%s run=%i errors=%i failures=%i>" % \ | |
| 111 (self.__class__, self.testsRun, len(self.errors), | |
| 112 len(self.failures)) | |
| 113 | |
| 114 | |
| 115 class TestCase: | |
| 116 """A class whose instances are single test cases. | |
| 117 | |
| 118 By default, the test code itself should be placed in a method named | |
| 119 'runTest'. | |
| 120 | |
| 121 If the fixture may be used for many test cases, create as | |
| 122 many test methods as are needed. When instantiating such a TestCase | |
| 123 subclass, specify in the constructor arguments the name of the test method | |
| 124 that the instance is to execute. | |
| 125 | |
| 126 Test authors should subclass TestCase for their own tests. Construction | |
| 127 and deconstruction of the test's environment ('fixture') can be | |
| 128 implemented by overriding the 'setUp' and 'tearDown' methods respectively. | |
| 129 | |
| 130 If it is necessary to override the __init__ method, the base class | |
| 131 __init__ method must always be called. It is important that subclasses | |
| 132 should not change the signature of their __init__ method, since instances | |
| 133 of the classes are instantiated automatically by parts of the framework | |
| 134 in order to be run. | |
| 135 """ | |
| 136 | |
| 137 # This attribute determines which exception will be raised when | |
| 138 # the instance's assertion methods fail; test methods raising this | |
| 139 # exception will be deemed to have 'failed' rather than 'errored' | |
| 140 | |
| 141 failureException = AssertionError | |
| 142 | |
| 143 def __init__(self, methodName='runTest'): | |
| 144 """Create an instance of the class that will use the named test | |
| 145 method when executed. Raises a ValueError if the instance does | |
| 146 not have a method with the specified name. | |
| 147 """ | |
| 148 try: | |
| 149 self.__testMethodName = methodName | |
| 150 testMethod = getattr(self, methodName) | |
| 151 self.__testMethodDoc = testMethod.__doc__ | |
| 152 except AttributeError: | |
| 153 raise ValueError, "no such test method in %s: %s" % \ | |
| 154 (self.__class__, methodName) | |
| 155 | |
| 156 def setUp(self): | |
| 157 "Hook method for setting up the test fixture before exercising it." | |
| 158 pass | |
| 159 | |
| 160 def tearDown(self): | |
| 161 "Hook method for deconstructing the test fixture after testing it." | |
| 162 pass | |
| 163 | |
| 164 def countTestCases(self): | |
| 165 return 1 | |
| 166 | |
| 167 def defaultTestResult(self): | |
| 168 return TestResult() | |
| 169 | |
| 170 def shortDescription(self): | |
| 171 """Returns a one-line description of the test, or None if no | |
| 172 description has been provided. | |
| 173 | |
| 174 The default implementation of this method returns the first line of | |
| 175 the specified test method's docstring. | |
| 176 """ | |
| 177 doc = self.__testMethodDoc | |
| 178 return doc and string.strip(string.split(doc, "\n")[0]) or None | |
| 179 | |
| 180 def id(self): | |
| 181 return "%s.%s" % (self.__class__, self.__testMethodName) | |
| 182 | |
| 183 def __str__(self): | |
| 184 return "%s (%s)" % (self.__testMethodName, self.__class__) | |
| 185 | |
| 186 def __repr__(self): | |
| 187 return "<%s testMethod=%s>" % \ | |
| 188 (self.__class__, self.__testMethodName) | |
| 189 | |
| 190 def run(self, result=None): | |
| 191 return self(result) | |
| 192 | |
| 193 def __call__(self, result=None): | |
| 194 if result is None: result = self.defaultTestResult() | |
| 195 result.startTest(self) | |
| 196 testMethod = getattr(self, self.__testMethodName) | |
| 197 try: | |
| 198 try: | |
| 199 self.setUp() | |
| 200 except: | |
| 201 result.addError(self,self.__exc_info()) | |
| 202 return | |
| 203 | |
| 204 ok = 0 | |
| 205 try: | |
| 206 testMethod() | |
| 207 ok = 1 | |
| 208 except self.failureException, e: | |
| 209 result.addFailure(self,self.__exc_info()) | |
| 210 except: | |
| 211 result.addError(self,self.__exc_info()) | |
| 212 | |
| 213 try: | |
| 214 self.tearDown() | |
| 215 except: | |
| 216 result.addError(self,self.__exc_info()) | |
| 217 ok = 0 | |
| 218 if ok: result.addSuccess(self) | |
| 219 finally: | |
| 220 result.stopTest(self) | |
| 221 | |
| 222 def debug(self): | |
| 223 """Run the test without collecting errors in a TestResult""" | |
| 224 self.setUp() | |
| 225 getattr(self, self.__testMethodName)() | |
| 226 self.tearDown() | |
| 227 | |
| 228 def __exc_info(self): | |
| 229 """Return a version of sys.exc_info() with the traceback frame | |
| 230 minimised; usually the top level of the traceback frame is not | |
| 231 needed. | |
| 232 """ | |
| 233 exctype, excvalue, tb = sys.exc_info() | |
| 234 if sys.platform[:4] == 'java': ## tracebacks look different in Jython | |
| 235 return (exctype, excvalue, tb) | |
| 236 newtb = tb.tb_next | |
| 237 if newtb is None: | |
| 238 return (exctype, excvalue, tb) | |
| 239 return (exctype, excvalue, newtb) | |
| 240 | |
| 241 def fail(self, msg=None): | |
| 242 """Fail immediately, with the given message.""" | |
| 243 raise self.failureException, msg | |
| 244 | |
| 245 def failIf(self, expr, msg=None): | |
| 246 "Fail the test if the expression is true." | |
| 247 if expr: raise self.failureException, msg | |
| 248 | |
| 249 def failUnless(self, expr, msg=None): | |
| 250 """Fail the test unless the expression is true.""" | |
| 251 if not expr: raise self.failureException, msg | |
| 252 | |
| 253 def failUnlessRaises(self, excClass, callableObj, *args, **kwargs): | |
| 254 """Fail unless an exception of class excClass is thrown | |
| 255 by callableObj when invoked with arguments args and keyword | |
| 256 arguments kwargs. If a different type of exception is | |
| 257 thrown, it will not be caught, and the test case will be | |
| 258 deemed to have suffered an error, exactly as for an | |
| 259 unexpected exception. | |
| 260 """ | |
| 261 try: | |
| 262 apply(callableObj, args, kwargs) | |
| 263 except excClass: | |
| 264 return | |
| 265 else: | |
| 266 if hasattr(excClass,'__name__'): excName = excClass.__name__ | |
| 267 else: excName = str(excClass) | |
| 268 raise self.failureException, excName | |
| 269 | |
| 270 def failUnlessEqual(self, first, second, msg=None): | |
| 271 """Fail if the two objects are unequal as determined by the '!=' | |
| 272 operator. | |
| 273 """ | |
| 274 if first != second: | |
| 275 raise self.failureException, (msg or '%s != %s' % (first, second)) | |
| 276 | |
| 277 def failIfEqual(self, first, second, msg=None): | |
| 278 """Fail if the two objects are equal as determined by the '==' | |
| 279 operator. | |
| 280 """ | |
| 281 if first == second: | |
| 282 raise self.failureException, (msg or '%s == %s' % (first, second)) | |
| 283 | |
| 284 assertEqual = assertEquals = failUnlessEqual | |
| 285 | |
| 286 assertNotEqual = assertNotEquals = failIfEqual | |
| 287 | |
| 288 assertRaises = failUnlessRaises | |
| 289 | |
| 290 assert_ = failUnless | |
| 291 | |
| 292 | |
| 293 | |
| 294 class TestSuite: | |
| 295 """A test suite is a composite test consisting of a number of TestCases. | |
| 296 | |
| 297 For use, create an instance of TestSuite, then add test case instances. | |
| 298 When all tests have been added, the suite can be passed to a test | |
| 299 runner, such as TextTestRunner. It will run the individual test cases | |
| 300 in the order in which they were added, aggregating the results. When | |
| 301 subclassing, do not forget to call the base class constructor. | |
| 302 """ | |
| 303 def __init__(self, tests=()): | |
| 304 self._tests = [] | |
| 305 self.addTests(tests) | |
| 306 | |
| 307 def __repr__(self): | |
| 308 return "<%s tests=%s>" % (self.__class__, self._tests) | |
| 309 | |
| 310 __str__ = __repr__ | |
| 311 | |
| 312 def countTestCases(self): | |
| 313 cases = 0 | |
| 314 for test in self._tests: | |
| 315 cases = cases + test.countTestCases() | |
| 316 return cases | |
| 317 | |
| 318 def addTest(self, test): | |
| 319 self._tests.append(test) | |
| 320 | |
| 321 def addTests(self, tests): | |
| 322 for test in tests: | |
| 323 self.addTest(test) | |
| 324 | |
| 325 def run(self, result): | |
| 326 return self(result) | |
| 327 | |
| 328 def __call__(self, result): | |
| 329 for test in self._tests: | |
| 330 if result.shouldStop: | |
| 331 break | |
| 332 test(result) | |
| 333 return result | |
| 334 | |
| 335 def debug(self): | |
| 336 """Run the tests without collecting errors in a TestResult""" | |
| 337 for test in self._tests: test.debug() | |
| 338 | |
| 339 | |
| 340 class FunctionTestCase(TestCase): | |
| 341 """A test case that wraps a test function. | |
| 342 | |
| 343 This is useful for slipping pre-existing test functions into the | |
| 344 PyUnit framework. Optionally, set-up and tidy-up functions can be | |
| 345 supplied. As with TestCase, the tidy-up ('tearDown') function will | |
| 346 always be called if the set-up ('setUp') function ran successfully. | |
| 347 """ | |
| 348 | |
| 349 def __init__(self, testFunc, setUp=None, tearDown=None, | |
| 350 description=None): | |
| 351 TestCase.__init__(self) | |
| 352 self.__setUpFunc = setUp | |
| 353 self.__tearDownFunc = tearDown | |
| 354 self.__testFunc = testFunc | |
| 355 self.__description = description | |
| 356 | |
| 357 def setUp(self): | |
| 358 if self.__setUpFunc is not None: | |
| 359 self.__setUpFunc() | |
| 360 | |
| 361 def tearDown(self): | |
| 362 if self.__tearDownFunc is not None: | |
| 363 self.__tearDownFunc() | |
| 364 | |
| 365 def runTest(self): | |
| 366 self.__testFunc() | |
| 367 | |
| 368 def id(self): | |
| 369 return self.__testFunc.__name__ | |
| 370 | |
| 371 def __str__(self): | |
| 372 return "%s (%s)" % (self.__class__, self.__testFunc.__name__) | |
| 373 | |
| 374 def __repr__(self): | |
| 375 return "<%s testFunc=%s>" % (self.__class__, self.__testFunc) | |
| 376 | |
| 377 def shortDescription(self): | |
| 378 if self.__description is not None: return self.__description | |
| 379 doc = self.__testFunc.__doc__ | |
| 380 return doc and string.strip(string.split(doc, "\n")[0]) or None | |
| 381 | |
| 382 | |
| 383 | |
| 384 ############################################################################## | |
| 385 # Locating and loading tests | |
| 386 ############################################################################## | |
| 387 | |
| 388 class TestLoader: | |
| 389 """This class is responsible for loading tests according to various | |
| 390 criteria and returning them wrapped in a Test | |
| 391 """ | |
| 392 testMethodPrefix = 'test' | |
| 393 sortTestMethodsUsing = cmp | |
| 394 suiteClass = TestSuite | |
| 395 | |
| 396 def loadTestsFromTestCase(self, testCaseClass): | |
| 397 """Return a suite of all tests cases contained in testCaseClass""" | |
| 398 return self.suiteClass(map(testCaseClass, | |
| 399 self.getTestCaseNames(testCaseClass))) | |
| 400 | |
| 401 def loadTestsFromModule(self, module): | |
| 402 """Return a suite of all tests cases contained in the given module""" | |
| 403 tests = [] | |
| 404 for name in dir(module): | |
| 405 obj = getattr(module, name) | |
| 406 if type(obj) == types.ClassType and issubclass(obj, TestCase): | |
| 407 tests.append(self.loadTestsFromTestCase(obj)) | |
| 408 return self.suiteClass(tests) | |
| 409 | |
| 410 def loadTestsFromName(self, name, module=None): | |
| 411 """Return a suite of all tests cases given a string specifier. | |
| 412 | |
| 413 The name may resolve either to a module, a test case class, a | |
| 414 test method within a test case class, or a callable object which | |
| 415 returns a TestCase or TestSuite instance. | |
| 416 | |
| 417 The method optionally resolves the names relative to a given module. | |
| 418 """ | |
| 419 parts = string.split(name, '.') | |
| 420 if module is None: | |
| 421 if not parts: | |
| 422 raise ValueError, "incomplete test name: %s" % name | |
| 423 else: | |
| 424 parts_copy = parts[:] | |
| 425 while parts_copy: | |
| 426 try: | |
| 427 module = __import__(string.join(parts_copy,'.')) | |
| 428 break | |
| 429 except ImportError: | |
| 430 del parts_copy[-1] | |
| 431 if not parts_copy: raise | |
| 432 parts = parts[1:] | |
| 433 obj = module | |
| 434 for part in parts: | |
| 435 obj = getattr(obj, part) | |
| 436 | |
| 437 if type(obj) == types.ModuleType: | |
| 438 return self.loadTestsFromModule(obj) | |
| 439 elif type(obj) == types.ClassType and issubclass(obj, TestCase): | |
| 440 return self.loadTestsFromTestCase(obj) | |
| 441 elif type(obj) == types.UnboundMethodType: | |
| 442 return obj.im_class(obj.__name__) | |
| 443 elif callable(obj): | |
| 444 test = obj() | |
| 445 if not isinstance(test, TestCase) and \ | |
| 446 not isinstance(test, TestSuite): | |
| 447 raise ValueError, \ | |
| 448 "calling %s returned %s, not a test" % (obj,test) | |
| 449 return test | |
| 450 else: | |
| 451 raise ValueError, "don't know how to make test from: %s" % obj | |
| 452 | |
| 453 def loadTestsFromNames(self, names, module=None): | |
| 454 """Return a suite of all tests cases found using the given sequence | |
| 455 of string specifiers. See 'loadTestsFromName()'. | |
| 456 """ | |
| 457 suites = [] | |
| 458 for name in names: | |
| 459 suites.append(self.loadTestsFromName(name, module)) | |
| 460 return self.suiteClass(suites) | |
| 461 | |
| 462 def getTestCaseNames(self, testCaseClass): | |
| 463 """Return a sorted sequence of method names found within testCaseClass | |
| 464 """ | |
| 465 testFnNames = filter(lambda n,p=self.testMethodPrefix: n[:len(p)] == p, | |
| 466 dir(testCaseClass)) | |
| 467 for baseclass in testCaseClass.__bases__: | |
| 468 for testFnName in self.getTestCaseNames(baseclass): | |
| 469 if testFnName not in testFnNames: # handle overridden methods | |
| 470 testFnNames.append(testFnName) | |
| 471 if self.sortTestMethodsUsing: | |
| 472 testFnNames.sort(self.sortTestMethodsUsing) | |
| 473 return testFnNames | |
| 474 | |
| 475 | |
| 476 | |
| 477 defaultTestLoader = TestLoader() | |
| 478 | |
| 479 | |
| 480 ############################################################################## | |
| 481 # Patches for old functions: these functions should be considered obsolete | |
| 482 ############################################################################## | |
| 483 | |
| 484 def _makeLoader(prefix, sortUsing, suiteClass=None): | |
| 485 loader = TestLoader() | |
| 486 loader.sortTestMethodsUsing = sortUsing | |
| 487 loader.testMethodPrefix = prefix | |
| 488 if suiteClass: loader.suiteClass = suiteClass | |
| 489 return loader | |
| 490 | |
| 491 def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp): | |
| 492 return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass) | |
| 493 | |
| 494 def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite): | |
| 495 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass) | |
| 496 | |
| 497 def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite): | |
| 498 return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module) | |
| 499 | |
| 500 | |
| 501 ############################################################################## | |
| 502 # Text UI | |
| 503 ############################################################################## | |
| 504 | |
| 505 class _WritelnDecorator: | |
| 506 """Used to decorate file-like objects with a handy 'writeln' method""" | |
| 507 def __init__(self,stream): | |
| 508 self.stream = stream | |
| 509 | |
| 510 def __getattr__(self, attr): | |
| 511 return getattr(self.stream,attr) | |
| 512 | |
| 513 def writeln(self, *args): | |
| 514 if args: apply(self.write, args) | |
| 515 self.write('\n') # text-mode streams translate to \r\n if needed | |
| 516 | |
| 517 | |
| 518 class _TextTestResult(TestResult): | |
| 519 """A test result class that can print formatted text results to a stream. | |
| 520 | |
| 521 Used by TextTestRunner. | |
| 522 """ | |
| 523 separator1 = '=' * 70 | |
| 524 separator2 = '-' * 70 | |
| 525 | |
| 526 def __init__(self, stream, descriptions, verbosity): | |
| 527 TestResult.__init__(self) | |
| 528 self.stream = stream | |
| 529 self.showAll = verbosity > 1 | |
| 530 self.dots = verbosity == 1 | |
| 531 self.descriptions = descriptions | |
| 532 | |
| 533 def getDescription(self, test): | |
| 534 if self.descriptions: | |
| 535 return test.shortDescription() or str(test) | |
| 536 else: | |
| 537 return str(test) | |
| 538 | |
| 539 def startTest(self, test): | |
| 540 TestResult.startTest(self, test) | |
| 541 if self.showAll: | |
| 542 self.stream.write(self.getDescription(test)) | |
| 543 self.stream.write(" ... ") | |
| 544 | |
| 545 def addSuccess(self, test): | |
| 546 TestResult.addSuccess(self, test) | |
| 547 if self.showAll: | |
| 548 self.stream.writeln("ok") | |
| 549 elif self.dots: | |
| 550 self.stream.write('.') | |
| 551 | |
| 552 def addError(self, test, err): | |
| 553 TestResult.addError(self, test, err) | |
| 554 if self.showAll: | |
| 555 self.stream.writeln("ERROR") | |
| 556 elif self.dots: | |
| 557 self.stream.write('E') | |
| 558 if err[0] is KeyboardInterrupt: | |
| 559 self.shouldStop = 1 | |
| 560 | |
| 561 def addFailure(self, test, err): | |
| 562 TestResult.addFailure(self, test, err) | |
| 563 if self.showAll: | |
| 564 self.stream.writeln("FAIL") | |
| 565 elif self.dots: | |
| 566 self.stream.write('F') | |
| 567 | |
| 568 def printErrors(self): | |
| 569 if self.dots or self.showAll: | |
| 570 self.stream.writeln() | |
| 571 self.printErrorList('ERROR', self.errors) | |
| 572 self.printErrorList('FAIL', self.failures) | |
| 573 | |
| 574 def printErrorList(self, flavour, errors): | |
| 575 for test, err in errors: | |
| 576 self.stream.writeln(self.separator1) | |
| 577 self.stream.writeln("%s: %s" % (flavour,self.getDescription(test))) | |
| 578 self.stream.writeln(self.separator2) | |
| 579 for line in apply(traceback.format_exception, err): | |
| 580 for l in string.split(line,"\n")[:-1]: | |
| 581 self.stream.writeln("%s" % l) | |
| 582 | |
| 583 | |
| 584 class TextTestRunner: | |
| 585 """A test runner class that displays results in textual form. | |
| 586 | |
| 587 It prints out the names of tests as they are run, errors as they | |
| 588 occur, and a summary of the results at the end of the test run. | |
| 589 """ | |
| 590 def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1): | |
| 591 self.stream = _WritelnDecorator(stream) | |
| 592 self.descriptions = descriptions | |
| 593 self.verbosity = verbosity | |
| 594 | |
| 595 def _makeResult(self): | |
| 596 return _TextTestResult(self.stream, self.descriptions, self.verbosity) | |
| 597 | |
| 598 def run(self, test): | |
| 599 "Run the given test case or test suite." | |
| 600 result = self._makeResult() | |
| 601 startTime = time.time() | |
| 602 test(result) | |
| 603 stopTime = time.time() | |
| 604 timeTaken = float(stopTime - startTime) | |
| 605 result.printErrors() | |
| 606 self.stream.writeln(result.separator2) | |
| 607 run = result.testsRun | |
| 608 self.stream.writeln("Ran %d test%s in %.3fs" % | |
| 609 (run, run == 1 and "" or "s", timeTaken)) | |
| 610 self.stream.writeln() | |
| 611 if not result.wasSuccessful(): | |
| 612 self.stream.write("FAILED (") | |
| 613 failed, errored = map(len, (result.failures, result.errors)) | |
| 614 if failed: | |
| 615 self.stream.write("failures=%d" % failed) | |
| 616 if errored: | |
| 617 if failed: self.stream.write(", ") | |
| 618 self.stream.write("errors=%d" % errored) | |
| 619 self.stream.writeln(")") | |
| 620 else: | |
| 621 self.stream.writeln("OK") | |
| 622 return result | |
| 623 | |
| 624 | |
| 625 | |
| 626 ############################################################################## | |
| 627 # Facilities for running tests from the command line | |
| 628 ############################################################################## | |
| 629 | |
| 630 class TestProgram: | |
| 631 """A command-line program that runs a set of tests; this is primarily | |
| 632 for making test modules conveniently executable. | |
| 633 """ | |
| 634 USAGE = """\ | |
| 635 Usage: %(progName)s [options] [test] [...] | |
| 636 | |
| 637 Options: | |
| 638 -h, --help Show this message | |
| 639 -v, --verbose Verbose output | |
| 640 -q, --quiet Minimal output | |
| 641 | |
| 642 Examples: | |
| 643 %(progName)s - run default set of tests | |
| 644 %(progName)s MyTestSuite - run suite 'MyTestSuite' | |
| 645 %(progName)s MyTestCase.testSomething - run MyTestCase.testSomething | |
| 646 %(progName)s MyTestCase - run all 'test*' test methods | |
| 647 in MyTestCase | |
| 648 """ | |
| 649 def __init__(self, module='__main__', defaultTest=None, | |
| 650 argv=None, testRunner=None, testLoader=defaultTestLoader): | |
| 651 if type(module) == type(''): | |
| 652 self.module = __import__(module) | |
| 653 for part in string.split(module,'.')[1:]: | |
| 654 self.module = getattr(self.module, part) | |
| 655 else: | |
| 656 self.module = module | |
| 657 if argv is None: | |
| 658 argv = sys.argv | |
| 659 self.verbosity = 1 | |
| 660 self.defaultTest = defaultTest | |
| 661 self.testRunner = testRunner | |
| 662 self.testLoader = testLoader | |
| 663 self.progName = os.path.basename(argv[0]) | |
| 664 self.parseArgs(argv) | |
| 665 self.runTests() | |
| 666 | |
| 667 def usageExit(self, msg=None): | |
| 668 if msg: print msg | |
| 669 print self.USAGE % self.__dict__ | |
| 670 sys.exit(2) | |
| 671 | |
| 672 def parseArgs(self, argv): | |
| 673 import getopt | |
| 674 try: | |
| 675 options, args = getopt.getopt(argv[1:], 'hHvq', | |
| 676 ['help','verbose','quiet']) | |
| 677 for opt, value in options: | |
| 678 if opt in ('-h','-H','--help'): | |
| 679 self.usageExit() | |
| 680 if opt in ('-q','--quiet'): | |
| 681 self.verbosity = 0 | |
| 682 if opt in ('-v','--verbose'): | |
| 683 self.verbosity = 2 | |
| 684 if len(args) == 0 and self.defaultTest is None: | |
| 685 self.test = self.testLoader.loadTestsFromModule(self.module) | |
| 686 return | |
| 687 if len(args) > 0: | |
| 688 self.testNames = args | |
| 689 else: | |
| 690 self.testNames = (self.defaultTest,) | |
| 691 self.createTests() | |
| 692 except getopt.error, msg: | |
| 693 self.usageExit(msg) | |
| 694 | |
| 695 def createTests(self): | |
| 696 self.test = self.testLoader.loadTestsFromNames(self.testNames, | |
| 697 self.module) | |
| 698 | |
| 699 def runTests(self): | |
| 700 if self.testRunner is None: | |
| 701 self.testRunner = TextTestRunner(verbosity=self.verbosity) | |
| 702 result = self.testRunner.run(self.test) | |
| 703 sys.exit(not result.wasSuccessful()) | |
| 704 | |
| 705 main = TestProgram | |
| 706 | |
| 707 | |
| 708 ############################################################################## | |
| 709 # Executing this module from the command line | |
| 710 ############################################################################## | |
| 711 | |
| 712 if __name__ == "__main__": | |
| 713 main(module=None) |
