3 Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's
4 Smalltalk testing framework.
6 Further information is available in the bundled documentation, and from
8 http://pyunit.sourceforge.net/
10 This module contains the core framework classes that form the basis of
11 specific test cases and suites (TestCase, TestSuite etc.), and also a
12 text-based utility class for running the tests and reporting the results
15 Copyright (c) 1999, 2000, 2001 Steve Purcell
16 This module is free software, and you may redistribute it and/or modify
17 it under the same terms as Python itself, so long as this copyright message
18 and disclaimer are retained in their original form.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
21 SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
22 THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
25 THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27 PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
28 AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
29 SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
31 from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS
33 __author__ = "Steve Purcell (stephen_purcell@yahoo.com)"
34 __version__ = "$ Revision: 1.23 $"[11:-2]
41 ##############################################################################
42 # A platform-specific concession to help the code work for JPython users
43 ##############################################################################
45 plat = sys.platform.lower()
46 _isJPython = plat.find('java') >= 0 or plat.find('jdk') >= 0
50 ##############################################################################
52 ##############################################################################
55 """Holder for test result information.
57 Test results are automatically managed by the TestCase and TestSuite
58 classes, and do not need to be explicitly manipulated by writers of tests.
60 Each instance holds the total number of tests run, and collections of
61 failures and errors that occurred among those test runs. The collections
62 contain tuples of (testcase, exceptioninfo), where exceptioninfo is a
63 tuple of values as returned by sys.exc_info().
71 def startTest(self, test):
72 "Called when the given test is about to be run"
73 self.testsRun = self.testsRun + 1
75 def stopTest(self, test):
76 "Called when the given test has been run"
79 def addError(self, test, err):
80 "Called when an error has occurred"
81 self.errors.append((test, err))
83 def addFailure(self, test, err):
84 "Called when a failure has occurred"
85 self.failures.append((test, err))
87 def wasSuccessful(self):
88 "Tells whether or not this result was a success"
89 return len(self.failures) == len(self.errors) == 0
92 "Indicates that the tests should be aborted"
96 return "<%s run=%i errors=%i failures=%i>" % \
97 (self.__class__, self.testsRun, len(self.errors),
102 """A class whose instances are single test cases.
104 Test authors should subclass TestCase for their own tests. Construction
105 and deconstruction of the test's environment ('fixture') can be
106 implemented by overriding the 'setUp' and 'tearDown' methods respectively.
108 By default, the test code itself should be placed in a method named
111 If the fixture may be used for many test cases, create as
112 many test methods as are needed. When instantiating such a TestCase
113 subclass, specify in the constructor arguments the name of the test method
114 that the instance is to execute.
116 If it is necessary to override the __init__ method, the base class
117 __init__ method must always be called.
119 def __init__(self, methodName='runTest'):
120 """Create an instance of the class that will use the named test
121 method when executed. Raises a ValueError if the instance does
122 not have a method with the specified name.
125 self.__testMethod = getattr(self,methodName)
126 except AttributeError:
127 raise ValueError, "no such test method in %s: %s" % \
128 (self.__class__, methodName)
131 "Hook method for setting up the test fixture before exercising it."
135 "Hook method for deconstructing the test fixture after testing it."
138 def countTestCases(self):
141 def defaultTestResult(self):
144 def shortDescription(self):
145 """Returns a one-line description of the test, or None if no
146 description has been provided.
148 The default implementation of this method returns the first line of
149 the specified test method's docstring.
151 doc = self.__testMethod.__doc__
152 return doc and doc.split("\n")[0].strip() or None
155 return "%s.%s" % (self.__class__, self.__testMethod.__name__)
158 return "%s (%s)" % (self.__testMethod.__name__, self.__class__)
161 return "<%s testMethod=%s>" % \
162 (self.__class__, self.__testMethod.__name__)
164 def run(self, result=None):
167 def __call__(self, result=None):
168 if result is None: result = self.defaultTestResult()
169 result.startTest(self)
174 result.addError(self,self.__exc_info())
179 except AssertionError, e:
180 result.addFailure(self,self.__exc_info())
182 result.addError(self,self.__exc_info())
187 result.addError(self,self.__exc_info())
189 result.stopTest(self)
192 """Run the test without collecting errors in a TestResult"""
197 def assert_(self, expr, msg=None):
198 """Equivalent of built-in 'assert', but is not optimised out when
202 raise AssertionError, msg
206 def failIf(self, expr, msg=None):
207 "Fail the test if the expression is true."
208 self.assert_(not expr,msg)
210 def assertRaises(self, excClass, callableObj, *args, **kwargs):
211 """Assert that an exception of class excClass is thrown
212 by callableObj when invoked with arguments args and keyword
213 arguments kwargs. If a different type of exception is
214 thrown, it will not be caught, and the test case will be
215 deemed to have suffered an error, exactly as for an
216 unexpected exception.
219 callableObj(*args, **kwargs)
223 if hasattr(excClass,'__name__'): excName = excClass.__name__
224 else: excName = str(excClass)
225 raise AssertionError, excName
227 def fail(self, msg=None):
228 """Fail immediately, with the given message."""
229 raise AssertionError, msg
231 def __exc_info(self):
232 """Return a version of sys.exc_info() with the traceback frame
233 minimised; usually the top level of the traceback frame is not
236 exctype, excvalue, tb = sys.exc_info()
239 return (exctype, excvalue, tb)
240 return (exctype, excvalue, newtb)
244 """A test suite is a composite test consisting of a number of TestCases.
246 For use, create an instance of TestSuite, then add test case instances.
247 When all tests have been added, the suite can be passed to a test
248 runner, such as TextTestRunner. It will run the individual test cases
249 in the order in which they were added, aggregating the results. When
250 subclassing, do not forget to call the base class constructor.
252 def __init__(self, tests=()):
257 return "<%s tests=%s>" % (self.__class__, self._tests)
261 def countTestCases(self):
263 for test in self._tests:
264 cases = cases + test.countTestCases()
267 def addTest(self, test):
268 self._tests.append(test)
270 def addTests(self, tests):
274 def run(self, result):
277 def __call__(self, result):
278 for test in self._tests:
279 if result.shouldStop:
285 """Run the tests without collecting errors in a TestResult"""
286 for test in self._tests: test.debug()
289 class FunctionTestCase(TestCase):
290 """A test case that wraps a test function.
292 This is useful for slipping pre-existing test functions into the
293 PyUnit framework. Optionally, set-up and tidy-up functions can be
294 supplied. As with TestCase, the tidy-up ('tearDown') function will
295 always be called if the set-up ('setUp') function ran successfully.
298 def __init__(self, testFunc, setUp=None, tearDown=None,
300 TestCase.__init__(self)
301 self.__setUpFunc = setUp
302 self.__tearDownFunc = tearDown
303 self.__testFunc = testFunc
304 self.__description = description
307 if self.__setUpFunc is not None:
311 if self.__tearDownFunc is not None:
312 self.__tearDownFunc()
318 return self.__testFunc.__name__
321 return "%s (%s)" % (self.__class__, self.__testFunc.__name__)
324 return "<%s testFunc=%s>" % (self.__class__, self.__testFunc)
326 def shortDescription(self):
327 if self.__description is not None: return self.__description
328 doc = self.__testFunc.__doc__
329 return doc and doc.split("\n")[0].strip() or None
333 ##############################################################################
334 # Convenience functions
335 ##############################################################################
337 def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp):
338 """Extracts all the names of functions in the given test case class
339 and its base classes that start with the given prefix. This is used
342 testFnNames = [n for n in dir(testCaseClass) if n[:len(prefix)] == prefix]
343 for baseclass in testCaseClass.__bases__:
344 testFnNames = testFnNames + \
345 getTestCaseNames(baseclass, prefix, sortUsing=None)
347 testFnNames.sort(sortUsing)
351 def makeSuite(testCaseClass, prefix='test', sortUsing=cmp):
352 """Returns a TestSuite instance built from all of the test functions
353 in the given test case class whose names begin with the given
354 prefix. The cases are sorted by their function names
355 using the supplied comparison function, which defaults to 'cmp'.
357 cases = list(map(testCaseClass,
358 getTestCaseNames(testCaseClass, prefix, sortUsing)))
359 return TestSuite(cases)
362 def createTestInstance(name, module=None):
363 """Finds tests by their name, optionally only within the given module.
365 Return the newly-constructed test, ready to run. If the name contains a ':'
366 then the portion of the name after the colon is used to find a specific
367 test case within the test case class named before the colon.
370 findTest('examples.listtests.suite')
371 -- returns result of calling 'suite'
372 findTest('examples.listtests.ListTestCase:checkAppend')
373 -- returns result of calling ListTestCase('checkAppend')
374 findTest('examples.listtests.ListTestCase:check-')
375 -- returns result of calling makeSuite(ListTestCase, prefix="check")
378 spec = name.split(':')
379 if len(spec) > 2: raise ValueError, "illegal test name: %s" % name
384 testName, caseName = spec
385 parts = testName.split('.')
388 raise ValueError, "incomplete test name: %s" % name
389 constructor = __import__('.'.join(parts[:-1]))
394 constructor = getattr(constructor, part)
395 if not callable(constructor):
396 raise ValueError, "%s is not a callable object" % constructor
398 if caseName[-1] == '-':
399 prefix = caseName[:-1]
401 raise ValueError, "prefix too short: %s" % name
402 test = makeSuite(constructor, prefix=prefix)
404 test = constructor(caseName)
407 if not hasattr(test,"countTestCases"):
409 "object %s found with spec %s is not a test" % (test, name)
413 ##############################################################################
415 ##############################################################################
417 class _WritelnDecorator:
418 """Used to decorate file-like objects with a handy 'writeln' method"""
419 def __init__(self,stream):
422 import java.lang.System
423 self.linesep = java.lang.System.getProperty("line.separator")
425 self.linesep = os.linesep
427 def __getattr__(self, attr):
428 return getattr(self.stream,attr)
430 def writeln(self, *args):
431 if args: self.write(*args)
432 self.write(self.linesep)
435 class _JUnitTextTestResult(TestResult):
436 """A test result class that can print formatted text results to a stream.
438 Used by JUnitTextTestRunner.
440 def __init__(self, stream):
442 TestResult.__init__(self)
444 def addError(self, test, error):
445 TestResult.addError(self,test,error)
446 self.stream.write('E')
448 if error[0] is KeyboardInterrupt:
451 def addFailure(self, test, error):
452 TestResult.addFailure(self,test,error)
453 self.stream.write('F')
456 def startTest(self, test):
457 TestResult.startTest(self,test)
458 self.stream.write('.')
461 def printNumberedErrors(self,errFlavour,errors):
462 if not errors: return
464 self.stream.writeln("There was 1 %s:" % errFlavour)
466 self.stream.writeln("There were %i %ss:" %
467 (len(errors), errFlavour))
469 for test,error in errors:
470 errString = "".join(traceback.format_exception(*error))
471 self.stream.writeln("%i) %s" % (i, test))
472 self.stream.writeln(errString)
475 def printErrors(self):
476 self.printNumberedErrors("error",self.errors)
478 def printFailures(self):
479 self.printNumberedErrors("failure",self.failures)
481 def printHeader(self):
482 self.stream.writeln()
483 if self.wasSuccessful():
484 self.stream.writeln("OK (%i tests)" % self.testsRun)
486 self.stream.writeln("!!!FAILURES!!!")
487 self.stream.writeln("Test Results")
488 self.stream.writeln()
489 self.stream.writeln("Run: %i ; Failures: %i ; Errors: %i" %
490 (self.testsRun, len(self.failures),
493 def printResult(self):
499 class JUnitTextTestRunner:
500 """A test runner class that displays results in textual form.
502 The display format approximates that of JUnit's 'textui' test runner.
503 This test runner may be removed in a future version of PyUnit.
505 def __init__(self, stream=sys.stderr):
506 self.stream = _WritelnDecorator(stream)
509 "Run the given test case or test suite."
510 result = _JUnitTextTestResult(self.stream)
511 startTime = time.time()
513 stopTime = time.time()
514 self.stream.writeln()
515 self.stream.writeln("Time: %.3fs" % float(stopTime - startTime))
520 ##############################################################################
522 ##############################################################################
524 class _VerboseTextTestResult(TestResult):
525 """A test result class that can print formatted text results to a stream.
527 Used by VerboseTextTestRunner.
529 def __init__(self, stream, descriptions):
530 TestResult.__init__(self)
532 self.lastFailure = None
533 self.descriptions = descriptions
535 def startTest(self, test):
536 TestResult.startTest(self, test)
537 if self.descriptions:
538 self.stream.write(test.shortDescription() or str(test))
540 self.stream.write(str(test))
541 self.stream.write(" ... ")
543 def stopTest(self, test):
544 TestResult.stopTest(self, test)
545 if self.lastFailure is not test:
546 self.stream.writeln("ok")
548 def addError(self, test, err):
549 TestResult.addError(self, test, err)
550 self._printError("ERROR", test, err)
551 self.lastFailure = test
552 if err[0] is KeyboardInterrupt:
555 def addFailure(self, test, err):
556 TestResult.addFailure(self, test, err)
557 self._printError("FAIL", test, err)
558 self.lastFailure = test
560 def _printError(self, flavour, test, err):
562 separator1 = "\t" + '=' * 70
563 separator2 = "\t" + '-' * 70
564 if not self.lastFailure is test:
565 self.stream.writeln()
566 self.stream.writeln(separator1)
567 self.stream.writeln("\t%s" % flavour)
568 self.stream.writeln(separator2)
569 for line in traceback.format_exception(*err):
570 for l in line.split("\n")[:-1]:
571 self.stream.writeln("\t%s" % l)
572 self.stream.writeln(separator1)
575 class VerboseTextTestRunner:
576 """A test runner class that displays results in textual form.
578 It prints out the names of tests as they are run, errors as they
579 occur, and a summary of the results at the end of the test run.
581 def __init__(self, stream=sys.stderr, descriptions=1):
582 self.stream = _WritelnDecorator(stream)
583 self.descriptions = descriptions
586 "Run the given test case or test suite."
587 result = _VerboseTextTestResult(self.stream, self.descriptions)
588 startTime = time.time()
590 stopTime = time.time()
591 timeTaken = float(stopTime - startTime)
592 self.stream.writeln("-" * 78)
593 run = result.testsRun
594 self.stream.writeln("Ran %d test%s in %.3fs" %
595 (run, run > 1 and "s" or "", timeTaken))
596 self.stream.writeln()
597 if not result.wasSuccessful():
598 self.stream.write("FAILED (")
599 failed = len(result.failures)
601 self.stream.write("failures=%d" % failed)
602 errored = len(result.errors)
604 if failed: self.stream.write(", ")
605 self.stream.write("errors=%d" % errored)
606 self.stream.writeln(")")
608 self.stream.writeln("OK")
612 # Which flavour of TextTestRunner is the default?
613 TextTestRunner = VerboseTextTestRunner
616 ##############################################################################
617 # Facilities for running tests from the command line
618 ##############################################################################
621 """A command-line program that runs a set of tests; this is primarily
622 for making test modules conveniently executable.
625 Usage: %(progName)s [-h|--help] [test[:(casename|prefix-)]] [...]
628 %(progName)s - run default set of tests
629 %(progName)s MyTestSuite - run suite 'MyTestSuite'
630 %(progName)s MyTestCase:checkSomething - run MyTestCase.checkSomething
631 %(progName)s MyTestCase:check- - run all 'check*' test methods
634 def __init__(self, module='__main__', defaultTest=None,
635 argv=None, testRunner=None):
636 if isinstance(module, str):
637 self.module = __import__(module)
638 for part in module.split('.')[1:]:
639 self.module = getattr(self.module, part)
644 self.defaultTest = defaultTest
645 self.testRunner = testRunner
646 self.progName = os.path.basename(argv[0])
651 def usageExit(self, msg=None):
653 print self.USAGE % self.__dict__
656 def parseArgs(self, argv):
659 options, args = getopt.getopt(argv[1:], 'hH', ['help'])
661 for opt, value in options:
662 if opt in ('-h','-H','--help'):
664 if len(args) == 0 and self.defaultTest is None:
665 raise getopt.error, "No default test is defined."
667 self.testNames = args
669 self.testNames = (self.defaultTest,)
670 except getopt.error, msg:
673 def createTests(self):
675 for testName in self.testNames:
676 tests.append(createTestInstance(testName, self.module))
677 self.test = TestSuite(tests)
680 if self.testRunner is None:
681 self.testRunner = TextTestRunner()
682 result = self.testRunner.run(self.test)
683 sys.exit(not result.wasSuccessful())
688 ##############################################################################
689 # Executing this module from the command line
690 ##############################################################################
692 if __name__ == "__main__":
697 # indent-tabs-mode:nil
699 # vim: set expandtab tabstop=4 shiftwidth=4: