Code

Am now bundling unittest with the package so that everyone can use the unit
[roundup.git] / test / unittest.py
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.
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).
11 Simple usage:
13     import unittest
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)
23     if __name__ == '__main__':
24         unittest.main()
26 Further information is available in the bundled documentation, and from
28   http://pyunit.sourceforge.net/
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.
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.
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 '''
47 print 'hi'
49 __author__ = "Steve Purcell"
50 __email__ = "stephen_purcell at yahoo dot com"
51 __version__ = "$Revision: 1.1 $"[11:-2]
53 import time
54 import sys
55 import traceback
56 import string
57 import os
58 import types
60 ##############################################################################
61 # Test framework core
62 ##############################################################################
64 class TestResult:
65     """Holder for test result information.
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.
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
81     def startTest(self, test):
82         "Called when the given test is about to be run"
83         self.testsRun = self.testsRun + 1
85     def stopTest(self, test):
86         "Called when the given test has been run"
87         pass
89     def addError(self, test, err):
90         "Called when an error has occurred"
91         self.errors.append((test, err))
93     def addFailure(self, test, err):
94         "Called when a failure has occurred"
95         self.failures.append((test, err))
97     def addSuccess(self, test):
98         "Called when a test has completed successfully"
99         pass
101     def wasSuccessful(self):
102         "Tells whether or not this result was a success"
103         return len(self.failures) == len(self.errors) == 0
105     def stop(self):
106         "Indicates that the tests should be aborted"
107         self.shouldStop = 1
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))
115 class TestCase:
116     """A class whose instances are single test cases.
118     By default, the test code itself should be placed in a method named
119     'runTest'.
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.
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.
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     """
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'
141     failureException = AssertionError
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)
156     def setUp(self):
157         "Hook method for setting up the test fixture before exercising it."
158         pass
160     def tearDown(self):
161         "Hook method for deconstructing the test fixture after testing it."
162         pass
164     def countTestCases(self):
165         return 1
167     def defaultTestResult(self):
168         return TestResult()
170     def shortDescription(self):
171         """Returns a one-line description of the test, or None if no
172         description has been provided.
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
180     def id(self):
181         return "%s.%s" % (self.__class__, self.__testMethodName)
183     def __str__(self):
184         return "%s (%s)" % (self.__testMethodName, self.__class__)
186     def __repr__(self):
187         return "<%s testMethod=%s>" % \
188                (self.__class__, self.__testMethodName)
190     def run(self, result=None):
191         return self(result)
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
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())
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)
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()
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)
241     def fail(self, msg=None):
242         """Fail immediately, with the given message."""
243         raise self.failureException, msg
245     def failIf(self, expr, msg=None):
246         "Fail the test if the expression is true."
247         if expr: raise self.failureException, msg
249     def failUnless(self, expr, msg=None):
250         """Fail the test unless the expression is true."""
251         if not expr: raise self.failureException, msg
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
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))
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))
284     assertEqual = assertEquals = failUnlessEqual
286     assertNotEqual = assertNotEquals = failIfEqual
288     assertRaises = failUnlessRaises
290     assert_ = failUnless
294 class TestSuite:
295     """A test suite is a composite test consisting of a number of TestCases.
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)
307     def __repr__(self):
308         return "<%s tests=%s>" % (self.__class__, self._tests)
310     __str__ = __repr__
312     def countTestCases(self):
313         cases = 0
314         for test in self._tests:
315             cases = cases + test.countTestCases()
316         return cases
318     def addTest(self, test):
319         self._tests.append(test)
321     def addTests(self, tests):
322         for test in tests:
323             self.addTest(test)
325     def run(self, result):
326         return self(result)
328     def __call__(self, result):
329         for test in self._tests:
330             if result.shouldStop:
331                 break
332             test(result)
333         return result
335     def debug(self):
336         """Run the tests without collecting errors in a TestResult"""
337         for test in self._tests: test.debug()
340 class FunctionTestCase(TestCase):
341     """A test case that wraps a test function.
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     """
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
357     def setUp(self):
358         if self.__setUpFunc is not None:
359             self.__setUpFunc()
361     def tearDown(self):
362         if self.__tearDownFunc is not None:
363             self.__tearDownFunc()
365     def runTest(self):
366         self.__testFunc()
368     def id(self):
369         return self.__testFunc.__name__
371     def __str__(self):
372         return "%s (%s)" % (self.__class__, self.__testFunc.__name__)
374     def __repr__(self):
375         return "<%s testFunc=%s>" % (self.__class__, self.__testFunc)
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
384 ##############################################################################
385 # Locating and loading tests
386 ##############################################################################
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
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)))
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)
410     def loadTestsFromName(self, name, module=None):
411         """Return a suite of all tests cases given a string specifier.
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.
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)
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
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)
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
477 defaultTestLoader = TestLoader()
480 ##############################################################################
481 # Patches for old functions: these functions should be considered obsolete
482 ##############################################################################
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
491 def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp):
492     return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass)
494 def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
495     return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass)
497 def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
498     return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module)
501 ##############################################################################
502 # Text UI
503 ##############################################################################
505 class _WritelnDecorator:
506     """Used to decorate file-like objects with a handy 'writeln' method"""
507     def __init__(self,stream):
508         self.stream = stream
510     def __getattr__(self, attr):
511         return getattr(self.stream,attr)
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
518 class _TextTestResult(TestResult):
519     """A test result class that can print formatted text results to a stream.
521     Used by TextTestRunner.
522     """
523     separator1 = '=' * 70
524     separator2 = '-' * 70
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
533     def getDescription(self, test):
534         if self.descriptions:
535             return test.shortDescription() or str(test)
536         else:
537             return str(test)
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(" ... ")
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('.')
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
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')
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)
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)
584 class TextTestRunner:
585     """A test runner class that displays results in textual form.
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
595     def _makeResult(self):
596         return _TextTestResult(self.stream, self.descriptions, self.verbosity)
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
626 ##############################################################################
627 # Facilities for running tests from the command line
628 ##############################################################################
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] [...]
637 Options:
638   -h, --help       Show this message
639   -v, --verbose    Verbose output
640   -q, --quiet      Minimal output
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()
667     def usageExit(self, msg=None):
668         if msg: print msg
669         print self.USAGE % self.__dict__
670         sys.exit(2)
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)
695     def createTests(self):
696         self.test = self.testLoader.loadTestsFromNames(self.testNames,
697                                                        self.module)
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())
705 main = TestProgram
708 ##############################################################################
709 # Executing this module from the command line
710 ##############################################################################
712 if __name__ == "__main__":
713     main(module=None)