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
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__':
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
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.
47
__author__ = "Steve Purcell"
48
__email__ = "stephen_purcell at yahoo dot com"
49
__version__ = "Revision: 1.40"
58
##############################################################################
60
##############################################################################
63
"""Holder for test result information.
65
Test results are automatically managed by the TestCase and TestSuite
66
classes, and do not need to be explicitly manipulated by writers of tests.
68
Each instance holds the total number of tests run, and collections of
69
failures and errors that occurred among those test runs. The collections
70
contain tuples of (testcase, exceptioninfo), where exceptioninfo is a
71
tuple of values as returned by sys.exc_info().
79
def startTest(self, test):
80
"Called when the given test is about to be run"
81
self.testsRun = self.testsRun + 1
83
def stopTest(self, test):
84
"Called when the given test has been run"
87
def addError(self, test, err):
88
"Called when an error has occurred"
89
self.errors.append((test, err))
91
def addFailure(self, test, err):
92
"Called when a failure has occurred"
93
self.failures.append((test, err))
95
def addSuccess(self, test):
96
"Called when a test has completed successfully"
99
def wasSuccessful(self):
100
"Tells whether or not this result was a success"
101
return len(self.failures) == len(self.errors) == 0
104
"Indicates that the tests should be aborted"
108
return "<%s run=%i errors=%i failures=%i>" % \
109
(self.__class__, self.testsRun, len(self.errors),
114
"""A class whose instances are single test cases.
116
By default, the test code itself should be placed in a method named
119
If the fixture may be used for many test cases, create as
120
many test methods as are needed. When instantiating such a TestCase
121
subclass, specify in the constructor arguments the name of the test method
122
that the instance is to execute.
124
Test authors should subclass TestCase for their own tests. Construction
125
and deconstruction of the test's environment ('fixture') can be
126
implemented by overriding the 'setUp' and 'tearDown' methods respectively.
128
If it is necessary to override the __init__ method, the base class
129
__init__ method must always be called. It is important that subclasses
130
should not change the signature of their __init__ method, since instances
131
of the classes are instantiated automatically by parts of the framework
135
# This attribute determines which exception will be raised when
136
# the instance's assertion methods fail; test methods raising this
137
# exception will be deemed to have 'failed' rather than 'errored'
139
failureException = AssertionError
141
def __init__(self, methodName='runTest'):
142
"""Create an instance of the class that will use the named test
143
method when executed. Raises a ValueError if the instance does
144
not have a method with the specified name.
147
self.__testMethodName = methodName
148
testMethod = getattr(self, methodName)
149
self.__testMethodDoc = testMethod.__doc__
150
except AttributeError:
151
raise ValueError, "no such test method in %s: %s" % \
152
(self.__class__, methodName)
155
"Hook method for setting up the test fixture before exercising it."
159
"Hook method for deconstructing the test fixture after testing it."
162
def countTestCases(self):
165
def defaultTestResult(self):
168
def shortDescription(self):
169
"""Returns a one-line description of the test, or None if no
170
description has been provided.
172
The default implementation of this method returns the first line of
173
the specified test method's docstring.
175
doc = self.__testMethodDoc
176
return doc and string.strip(string.split(doc, "\n")[0]) or None
179
return "%s.%s" % (self.__class__, self.__testMethodName)
182
return "%s (%s)" % (self.__testMethodName, self.__class__)
185
return "<%s testMethod=%s>" % \
186
(self.__class__, self.__testMethodName)
188
def run(self, result=None):
191
def __call__(self, result=None):
192
if result is None: result = self.defaultTestResult()
193
result.startTest(self)
194
testMethod = getattr(self, self.__testMethodName)
199
result.addError(self,self.__exc_info())
206
except self.failureException, e:
207
result.addFailure(self,self.__exc_info())
209
result.addError(self,self.__exc_info())
214
result.addError(self,self.__exc_info())
216
if ok: result.addSuccess(self)
218
result.stopTest(self)
221
"""Run the test without collecting errors in a TestResult"""
223
getattr(self, self.__testMethodName)()
226
def __exc_info(self):
227
"""Return a version of sys.exc_info() with the traceback frame
228
minimised; usually the top level of the traceback frame is not
231
exctype, excvalue, tb = sys.exc_info()
232
if sys.platform[:4] == 'java': ## tracebacks look different in Jython
233
return (exctype, excvalue, tb)
236
return (exctype, excvalue, tb)
237
return (exctype, excvalue, newtb)
239
def fail(self, msg=None):
240
"""Fail immediately, with the given message."""
241
raise self.failureException, msg
243
def failIf(self, expr, msg=None):
244
"Fail the test if the expression is true."
245
if expr: raise self.failureException, msg
247
def failUnless(self, expr, msg=None):
248
"""Fail the test unless the expression is true."""
249
if not expr: raise self.failureException, msg
251
def failUnlessRaises(self, excClass, callableObj, *args, **kwargs):
252
"""Fail unless an exception of class excClass is thrown
253
by callableObj when invoked with arguments args and keyword
254
arguments kwargs. If a different type of exception is
255
thrown, it will not be caught, and the test case will be
256
deemed to have suffered an error, exactly as for an
257
unexpected exception.
260
apply(callableObj, args, kwargs)
264
if hasattr(excClass,'__name__'): excName = excClass.__name__
265
else: excName = str(excClass)
266
raise self.failureException, excName
268
def failUnlessEqual(self, first, second, msg=None):
269
"""Fail if the two objects are unequal as determined by the '!='
273
raise self.failureException, (msg or '%s != %s' % (first, second))
275
def failIfEqual(self, first, second, msg=None):
276
"""Fail if the two objects are equal as determined by the '=='
280
raise self.failureException, (msg or '%s == %s' % (first, second))
282
assertEqual = assertEquals = failUnlessEqual
284
assertNotEqual = assertNotEquals = failIfEqual
286
assertRaises = failUnlessRaises
293
"""A test suite is a composite test consisting of a number of TestCases.
295
For use, create an instance of TestSuite, then add test case instances.
296
When all tests have been added, the suite can be passed to a test
297
runner, such as TextTestRunner. It will run the individual test cases
298
in the order in which they were added, aggregating the results. When
299
subclassing, do not forget to call the base class constructor.
301
def __init__(self, tests=()):
306
return "<%s tests=%s>" % (self.__class__, self._tests)
310
def countTestCases(self):
312
for test in self._tests:
313
cases = cases + test.countTestCases()
316
def addTest(self, test):
317
self._tests.append(test)
319
def addTests(self, tests):
323
def run(self, result):
326
def __call__(self, result):
327
for test in self._tests:
328
if result.shouldStop:
334
"""Run the tests without collecting errors in a TestResult"""
335
for test in self._tests: test.debug()
338
class FunctionTestCase(TestCase):
339
"""A test case that wraps a test function.
341
This is useful for slipping pre-existing test functions into the
342
PyUnit framework. Optionally, set-up and tidy-up functions can be
343
supplied. As with TestCase, the tidy-up ('tearDown') function will
344
always be called if the set-up ('setUp') function ran successfully.
347
def __init__(self, testFunc, setUp=None, tearDown=None,
349
TestCase.__init__(self)
350
self.__setUpFunc = setUp
351
self.__tearDownFunc = tearDown
352
self.__testFunc = testFunc
353
self.__description = description
356
if self.__setUpFunc is not None:
360
if self.__tearDownFunc is not None:
361
self.__tearDownFunc()
367
return self.__testFunc.__name__
370
return "%s (%s)" % (self.__class__, self.__testFunc.__name__)
373
return "<%s testFunc=%s>" % (self.__class__, self.__testFunc)
375
def shortDescription(self):
376
if self.__description is not None: return self.__description
377
doc = self.__testFunc.__doc__
378
return doc and string.strip(string.split(doc, "\n")[0]) or None
382
##############################################################################
383
# Locating and loading tests
384
##############################################################################
387
"""This class is responsible for loading tests according to various
388
criteria and returning them wrapped in a Test
390
testMethodPrefix = 'test'
391
sortTestMethodsUsing = cmp
392
suiteClass = TestSuite
394
def loadTestsFromTestCase(self, testCaseClass):
395
"""Return a suite of all tests cases contained in testCaseClass"""
396
return self.suiteClass(map(testCaseClass,
397
self.getTestCaseNames(testCaseClass)))
399
def loadTestsFromModule(self, module):
400
"""Return a suite of all tests cases contained in the given module"""
402
for name in dir(module):
403
obj = getattr(module, name)
404
if type(obj) == types.ClassType and issubclass(obj, TestCase):
405
tests.append(self.loadTestsFromTestCase(obj))
406
return self.suiteClass(tests)
408
def loadTestsFromName(self, name, module=None):
409
"""Return a suite of all tests cases given a string specifier.
411
The name may resolve either to a module, a test case class, a
412
test method within a test case class, or a callable object which
413
returns a TestCase or TestSuite instance.
415
The method optionally resolves the names relative to a given module.
417
parts = string.split(name, '.')
420
raise ValueError, "incomplete test name: %s" % name
422
parts_copy = parts[:]
425
module = __import__(string.join(parts_copy,'.'))
429
if not parts_copy: raise
433
obj = getattr(obj, part)
436
if type(obj) == types.ModuleType:
437
return self.loadTestsFromModule(obj)
438
elif type(obj) == types.ClassType and issubclass(obj, unittest.TestCase):
439
return self.loadTestsFromTestCase(obj)
440
elif type(obj) == types.UnboundMethodType:
441
return obj.im_class(obj.__name__)
444
if not isinstance(test, unittest.TestCase) and \
445
not isinstance(test, unittest.TestSuite):
447
"calling %s returned %s, not a test" % (obj,test)
450
raise ValueError, "don't know how to make test from: %s" % obj
452
def loadTestsFromNames(self, names, module=None):
453
"""Return a suite of all tests cases found using the given sequence
454
of string specifiers. See 'loadTestsFromName()'.
458
suites.append(self.loadTestsFromName(name, module))
459
return self.suiteClass(suites)
461
def getTestCaseNames(self, testCaseClass):
462
"""Return a sorted sequence of method names found within testCaseClass
464
testFnNames = filter(lambda n,p=self.testMethodPrefix: n[:len(p)] == p,
466
for baseclass in testCaseClass.__bases__:
467
for testFnName in self.getTestCaseNames(baseclass):
468
if testFnName not in testFnNames: # handle overridden methods
469
testFnNames.append(testFnName)
470
if self.sortTestMethodsUsing:
471
testFnNames.sort(self.sortTestMethodsUsing)
476
defaultTestLoader = TestLoader()
479
##############################################################################
480
# Patches for old functions: these functions should be considered obsolete
481
##############################################################################
483
def _makeLoader(prefix, sortUsing, suiteClass=None):
484
loader = TestLoader()
485
loader.sortTestMethodsUsing = sortUsing
486
loader.testMethodPrefix = prefix
487
if suiteClass: loader.suiteClass = suiteClass
490
def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp):
491
return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass)
493
def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
494
return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass)
496
def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
497
return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module)
500
##############################################################################
502
##############################################################################
504
class _WritelnDecorator:
505
"""Used to decorate file-like objects with a handy 'writeln' method"""
506
def __init__(self,stream):
509
def __getattr__(self, attr):
510
return getattr(self.stream,attr)
512
def writeln(self, *args):
513
if args: apply(self.write, args)
514
self.write('\n') # text-mode streams translate to \r\n if needed
517
class _TextTestResult(TestResult):
518
"""A test result class that can print formatted text results to a stream.
520
Used by TextTestRunner.
522
separator1 = '=' * 70
523
separator2 = '-' * 70
525
def __init__(self, stream, descriptions, verbosity):
526
TestResult.__init__(self)
528
self.showAll = verbosity > 1
529
self.dots = verbosity == 1
530
self.descriptions = descriptions
532
def getDescription(self, test):
533
if self.descriptions:
534
return test.shortDescription() or str(test)
538
def startTest(self, test):
539
TestResult.startTest(self, test)
541
self.stream.write(self.getDescription(test))
542
self.stream.write(" ... ")
544
def addSuccess(self, test):
545
TestResult.addSuccess(self, test)
547
self.stream.writeln("ok")
549
self.stream.write('.')
551
def addError(self, test, err):
552
TestResult.addError(self, test, err)
554
self.stream.writeln("ERROR")
556
self.stream.write('E')
557
if err[0] is KeyboardInterrupt:
560
def addFailure(self, test, err):
561
TestResult.addFailure(self, test, err)
563
self.stream.writeln("FAIL")
565
self.stream.write('F')
567
def printErrors(self):
568
if self.dots or self.showAll:
569
self.stream.writeln()
570
self.printErrorList('ERROR', self.errors)
571
self.printErrorList('FAIL', self.failures)
573
def printErrorList(self, flavour, errors):
574
for test, err in errors:
575
self.stream.writeln(self.separator1)
576
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
577
self.stream.writeln(self.separator2)
578
for line in apply(traceback.format_exception, err):
579
for l in string.split(line,"\n")[:-1]:
580
self.stream.writeln("%s" % l)
583
class TextTestRunner:
584
"""A test runner class that displays results in textual form.
586
It prints out the names of tests as they are run, errors as they
587
occur, and a summary of the results at the end of the test run.
589
def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1):
590
self.stream = _WritelnDecorator(stream)
591
self.descriptions = descriptions
592
self.verbosity = verbosity
594
def _makeResult(self):
595
return _TextTestResult(self.stream, self.descriptions, self.verbosity)
598
"Run the given test case or test suite."
599
result = self._makeResult()
600
startTime = time.time()
602
stopTime = time.time()
603
timeTaken = float(stopTime - startTime)
605
self.stream.writeln(result.separator2)
606
run = result.testsRun
607
self.stream.writeln("Ran %d test%s in %.3fs" %
608
(run, run == 1 and "" or "s", timeTaken))
609
self.stream.writeln()
610
if not result.wasSuccessful():
611
self.stream.write("FAILED (")
612
failed, errored = map(len, (result.failures, result.errors))
614
self.stream.write("failures=%d" % failed)
616
if failed: self.stream.write(", ")
617
self.stream.write("errors=%d" % errored)
618
self.stream.writeln(")")
620
self.stream.writeln("OK")
625
##############################################################################
626
# Facilities for running tests from the command line
627
##############################################################################
630
"""A command-line program that runs a set of tests; this is primarily
631
for making test modules conveniently executable.
634
Usage: %(progName)s [options] [test] [...]
637
-h, --help Show this message
638
-v, --verbose Verbose output
639
-q, --quiet Minimal output
642
%(progName)s - run default set of tests
643
%(progName)s MyTestSuite - run suite 'MyTestSuite'
644
%(progName)s MyTestCase.testSomething - run MyTestCase.testSomething
645
%(progName)s MyTestCase - run all 'test*' test methods
648
def __init__(self, module='__main__', defaultTest=None,
649
argv=None, testRunner=None, testLoader=defaultTestLoader):
650
if type(module) == type(''):
651
self.module = __import__(module)
652
for part in string.split(module,'.')[1:]:
653
self.module = getattr(self.module, part)
659
self.defaultTest = defaultTest
660
self.testRunner = testRunner
661
self.testLoader = testLoader
662
self.progName = os.path.basename(argv[0])
666
def usageExit(self, msg=None):
668
print self.USAGE % self.__dict__
671
def parseArgs(self, argv):
674
options, args = getopt.getopt(argv[1:], 'hHvq',
675
['help','verbose','quiet'])
676
for opt, value in options:
677
if opt in ('-h','-H','--help'):
679
if opt in ('-q','--quiet'):
681
if opt in ('-v','--verbose'):
683
if len(args) == 0 and self.defaultTest is None:
684
self.test = self.testLoader.loadTestsFromModule(self.module)
687
self.testNames = args
689
self.testNames = (self.defaultTest,)
691
except getopt.error, msg:
694
def createTests(self):
695
self.test = self.testLoader.loadTestsFromNames(self.testNames,
699
if self.testRunner is None:
700
self.testRunner = TextTestRunner(verbosity=self.verbosity)
701
result = self.testRunner.run(self.test)
702
sys.exit(not result.wasSuccessful())
707
##############################################################################
708
# Executing this module from the command line
709
##############################################################################
711
if __name__ == "__main__":