- Fixed loading of tests by name when name refers to unbound

method (PyUnit issue 563882, thanks to Alexandre Fayolle)
- Ignore non-callable attributes of classes when searching for test
  method names (PyUnit issue 769338, thanks to Seth Falcon)
- New assertTrue and assertFalse aliases for comfort of JUnit users
- Automatically discover 'runTest()' test methods (PyUnit issue 469444,
  thanks to Roeland Rengelink)
- Dropped Python 1.5.2 compatibility, merged appropriate shortcuts from
  Python CVS; should work with Python >= 2.1.
- Removed all references to string module by using string methods instead
This commit is contained in:
Steve Purcell 2003-09-22 11:08:12 +00:00
parent 1e80359733
commit 7e74384af5
1 changed files with 58 additions and 40 deletions

View File

@ -27,7 +27,7 @@ Further information is available in the bundled documentation, and from
http://pyunit.sourceforge.net/
Copyright (c) 1999, 2000, 2001 Steve Purcell
Copyright (c) 1999-2003 Steve Purcell
This module is free software, and you may redistribute it and/or modify
it under the same terms as Python itself, so long as this copyright message
and disclaimer are retained in their original form.
@ -46,12 +46,11 @@ SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
__author__ = "Steve Purcell"
__email__ = "stephen_purcell at yahoo dot com"
__version__ = "#Revision: 1.46 $"[11:-2]
__version__ = "#Revision: 1.56 $"[11:-2]
import time
import sys
import traceback
import string
import os
import types
@ -61,10 +60,26 @@ import types
__all__ = ['TestResult', 'TestCase', 'TestSuite', 'TextTestRunner',
'TestLoader', 'FunctionTestCase', 'main', 'defaultTestLoader']
# Expose obsolete functions for backwards compatability
# Expose obsolete functions for backwards compatibility
__all__.extend(['getTestCaseNames', 'makeSuite', 'findTestCases'])
##############################################################################
# Backward compatibility
##############################################################################
if sys.version_info[:2] < (2, 2):
False, True = 0, 1
def isinstance(obj, clsinfo):
import __builtin__
if type(clsinfo) in (types.TupleType, types.ListType):
for cls in clsinfo:
if cls is type: cls = types.ClassType
if __builtin__.isinstance(obj, cls):
return 1
return 0
else: return __builtin__.isinstance(obj, clsinfo)
##############################################################################
# Test framework core
##############################################################################
@ -121,11 +136,11 @@ class TestResult:
def stop(self):
"Indicates that the tests should be aborted"
self.shouldStop = 1
self.shouldStop = True
def _exc_info_to_string(self, err):
"""Converts a sys.exc_info()-style tuple of values into a string."""
return string.join(traceback.format_exception(*err), '')
return ''.join(traceback.format_exception(*err))
def __repr__(self):
return "<%s run=%i errors=%i failures=%i>" % \
@ -196,7 +211,7 @@ class TestCase:
the specified test method's docstring.
"""
doc = self.__testMethodDoc
return doc and string.strip(string.split(doc, "\n")[0]) or None
return doc and doc.split("\n")[0].strip() or None
def id(self):
return "%s.%s" % (_strclass(self.__class__), self.__testMethodName)
@ -209,9 +224,6 @@ class TestCase:
(_strclass(self.__class__), self.__testMethodName)
def run(self, result=None):
return self(result)
def __call__(self, result=None):
if result is None: result = self.defaultTestResult()
result.startTest(self)
testMethod = getattr(self, self.__testMethodName)
@ -224,10 +236,10 @@ class TestCase:
result.addError(self, self.__exc_info())
return
ok = 0
ok = False
try:
testMethod()
ok = 1
ok = True
except self.failureException:
result.addFailure(self, self.__exc_info())
except KeyboardInterrupt:
@ -241,11 +253,13 @@ class TestCase:
raise
except:
result.addError(self, self.__exc_info())
ok = 0
ok = False
if ok: result.addSuccess(self)
finally:
result.stopTest(self)
__call__ = run
def debug(self):
"""Run the test without collecting errors in a TestResult"""
self.setUp()
@ -292,7 +306,7 @@ class TestCase:
else:
if hasattr(excClass,'__name__'): excName = excClass.__name__
else: excName = str(excClass)
raise self.failureException, excName
raise self.failureException, "%s not raised" % excName
def failUnlessEqual(self, first, second, msg=None):
"""Fail if the two objects are unequal as determined by the '=='
@ -334,6 +348,8 @@ class TestCase:
raise self.failureException, \
(msg or '%s == %s within %s places' % (`first`, `second`, `places`))
# Synonyms for assertion methods
assertEqual = assertEquals = failUnlessEqual
assertNotEqual = assertNotEquals = failIfEqual
@ -344,7 +360,9 @@ class TestCase:
assertRaises = failUnlessRaises
assert_ = failUnless
assert_ = assertTrue = failUnless
assertFalse = failIf
@ -369,7 +387,7 @@ class TestSuite:
def countTestCases(self):
cases = 0
for test in self._tests:
cases = cases + test.countTestCases()
cases += test.countTestCases()
return cases
def addTest(self, test):
@ -434,7 +452,7 @@ class FunctionTestCase(TestCase):
def shortDescription(self):
if self.__description is not None: return self.__description
doc = self.__testFunc.__doc__
return doc and string.strip(string.split(doc, "\n")[0]) or None
return doc and doc.split("\n")[0].strip() or None
@ -452,8 +470,10 @@ class TestLoader:
def loadTestsFromTestCase(self, testCaseClass):
"""Return a suite of all tests cases contained in testCaseClass"""
return self.suiteClass(map(testCaseClass,
self.getTestCaseNames(testCaseClass)))
testCaseNames = self.getTestCaseNames(testCaseClass)
if not testCaseNames and hasattr(testCaseClass, 'runTest'):
testCaseNames = ['runTest']
return self.suiteClass(map(testCaseClass, testCaseNames))
def loadTestsFromModule(self, module):
"""Return a suite of all tests cases contained in the given module"""
@ -474,23 +494,20 @@ class TestLoader:
The method optionally resolves the names relative to a given module.
"""
parts = string.split(name, '.')
parts = name.split('.')
if module is None:
if not parts:
raise ValueError, "incomplete test name: %s" % name
else:
parts_copy = parts[:]
while parts_copy:
try:
module = __import__(string.join(parts_copy,'.'))
break
except ImportError:
del parts_copy[-1]
if not parts_copy: raise
parts_copy = parts[:]
while parts_copy:
try:
module = __import__('.'.join(parts_copy))
break
except ImportError:
del parts_copy[-1]
if not parts_copy: raise
parts = parts[1:]
obj = module
for part in parts:
obj = getattr(obj, part)
parent, obj = obj, getattr(obj, part)
import unittest
if type(obj) == types.ModuleType:
@ -499,11 +516,13 @@ class TestLoader:
issubclass(obj, unittest.TestCase)):
return self.loadTestsFromTestCase(obj)
elif type(obj) == types.UnboundMethodType:
return parent(obj.__name__)
return obj.im_class(obj.__name__)
elif isinstance(obj, unittest.TestSuite):
return obj
elif callable(obj):
test = obj()
if not isinstance(test, unittest.TestCase) and \
not isinstance(test, unittest.TestSuite):
if not isinstance(test, (unittest.TestCase, unittest.TestSuite)):
raise ValueError, \
"calling %s returned %s, not a test" % (obj,test)
return test
@ -514,16 +533,15 @@ class TestLoader:
"""Return a suite of all tests cases found using the given sequence
of string specifiers. See 'loadTestsFromName()'.
"""
suites = []
for name in names:
suites.append(self.loadTestsFromName(name, module))
suites = [self.loadTestsFromName(name, module) for name in names]
return self.suiteClass(suites)
def getTestCaseNames(self, testCaseClass):
"""Return a sorted sequence of method names found within testCaseClass
"""
testFnNames = filter(lambda n,p=self.testMethodPrefix: n[:len(p)] == p,
dir(testCaseClass))
def isTestMethod(attrname, testCaseClass=testCaseClass, prefix=self.testMethodPrefix):
return attrname[:len(prefix)] == prefix and callable(getattr(testCaseClass, attrname))
testFnNames = filter(isTestMethod, dir(testCaseClass))
for baseclass in testCaseClass.__bases__:
for testFnName in self.getTestCaseNames(baseclass):
if testFnName not in testFnNames: # handle overridden methods
@ -706,7 +724,7 @@ Examples:
argv=None, testRunner=None, testLoader=defaultTestLoader):
if type(module) == type(''):
self.module = __import__(module)
for part in string.split(module,'.')[1:]:
for part in module.split('.')[1:]:
self.module = getattr(self.module, part)
else:
self.module = module