398 lines
13 KiB
Python
398 lines
13 KiB
Python
"""nose unittest.TestCase subclasses. It is not necessary to subclass these
|
|
classes when writing tests; they are used internally by nose.loader.TestLoader
|
|
to create test cases from test functions and methods in test classes.
|
|
"""
|
|
import logging
|
|
import sys
|
|
import unittest
|
|
from inspect import isfunction
|
|
from nose.config import Config
|
|
from nose.failure import Failure # for backwards compatibility
|
|
from nose.util import resolve_name, test_address, try_run
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
__all__ = ['Test']
|
|
|
|
|
|
class Test(unittest.TestCase):
|
|
"""The universal test case wrapper.
|
|
|
|
When a plugin sees a test, it will always see an instance of this
|
|
class. To access the actual test case that will be run, access the
|
|
test property of the nose.case.Test instance.
|
|
"""
|
|
__test__ = False # do not collect
|
|
def __init__(self, test, config=None, resultProxy=None):
|
|
# sanity check
|
|
if not callable(test):
|
|
raise TypeError("nose.case.Test called with argument %r that "
|
|
"is not callable. A callable is required."
|
|
% test)
|
|
self.test = test
|
|
if config is None:
|
|
config = Config()
|
|
self.config = config
|
|
self.tbinfo = None
|
|
self.capturedOutput = None
|
|
self.resultProxy = resultProxy
|
|
self.plugins = config.plugins
|
|
self.passed = None
|
|
unittest.TestCase.__init__(self)
|
|
|
|
def __call__(self, *arg, **kwarg):
|
|
return self.run(*arg, **kwarg)
|
|
|
|
def __str__(self):
|
|
name = self.plugins.testName(self)
|
|
if name is not None:
|
|
return name
|
|
return str(self.test)
|
|
|
|
def __repr__(self):
|
|
return "Test(%r)" % self.test
|
|
|
|
def afterTest(self, result):
|
|
"""Called after test is complete (after result.stopTest)
|
|
"""
|
|
try:
|
|
afterTest = result.afterTest
|
|
except AttributeError:
|
|
pass
|
|
else:
|
|
afterTest(self.test)
|
|
|
|
def beforeTest(self, result):
|
|
"""Called before test is run (before result.startTest)
|
|
"""
|
|
try:
|
|
beforeTest = result.beforeTest
|
|
except AttributeError:
|
|
pass
|
|
else:
|
|
beforeTest(self.test)
|
|
|
|
def exc_info(self):
|
|
"""Extract exception info.
|
|
"""
|
|
exc, exv, tb = sys.exc_info()
|
|
return (exc, exv, tb)
|
|
|
|
def id(self):
|
|
"""Get a short(er) description of the test
|
|
"""
|
|
return self.test.id()
|
|
|
|
def address(self):
|
|
"""Return a round-trip name for this test, a name that can be
|
|
fed back as input to loadTestByName and (assuming the same
|
|
plugin configuration) result in the loading of this test.
|
|
"""
|
|
if hasattr(self.test, 'address'):
|
|
return self.test.address()
|
|
else:
|
|
# not a nose case
|
|
return test_address(self.test)
|
|
|
|
def _context(self):
|
|
try:
|
|
return self.test.context
|
|
except AttributeError:
|
|
pass
|
|
try:
|
|
return self.test.__class__
|
|
except AttributeError:
|
|
pass
|
|
try:
|
|
return resolve_name(self.test.__module__)
|
|
except AttributeError:
|
|
pass
|
|
return None
|
|
context = property(_context, None, None,
|
|
"""Get the context object of this test (if any).""")
|
|
|
|
def run(self, result):
|
|
"""Modified run for the test wrapper.
|
|
|
|
From here we don't call result.startTest or stopTest or
|
|
addSuccess. The wrapper calls addError/addFailure only if its
|
|
own setup or teardown fails, or running the wrapped test fails
|
|
(eg, if the wrapped "test" is not callable).
|
|
|
|
Two additional methods are called, beforeTest and
|
|
afterTest. These give plugins a chance to modify the wrapped
|
|
test before it is called and do cleanup after it is
|
|
called. They are called unconditionally.
|
|
"""
|
|
if self.resultProxy:
|
|
result = self.resultProxy(result, self)
|
|
try:
|
|
try:
|
|
self.beforeTest(result)
|
|
self.runTest(result)
|
|
except KeyboardInterrupt:
|
|
raise
|
|
except:
|
|
err = sys.exc_info()
|
|
result.addError(self, err)
|
|
finally:
|
|
self.afterTest(result)
|
|
|
|
def runTest(self, result):
|
|
"""Run the test. Plugins may alter the test by returning a
|
|
value from prepareTestCase. The value must be callable and
|
|
must accept one argument, the result instance.
|
|
"""
|
|
test = self.test
|
|
plug_test = self.config.plugins.prepareTestCase(self)
|
|
if plug_test is not None:
|
|
test = plug_test
|
|
test(result)
|
|
|
|
def shortDescription(self):
|
|
desc = self.plugins.describeTest(self)
|
|
if desc is not None:
|
|
return desc
|
|
# work around bug in unittest.TestCase.shortDescription
|
|
# with multiline docstrings.
|
|
test = self.test
|
|
try:
|
|
test._testMethodDoc = test._testMethodDoc.strip()# 2.5
|
|
except AttributeError:
|
|
try:
|
|
# 2.4 and earlier
|
|
test._TestCase__testMethodDoc = \
|
|
test._TestCase__testMethodDoc.strip()
|
|
except AttributeError:
|
|
pass
|
|
# 2.7 compat: shortDescription() always returns something
|
|
# which is a change from 2.6 and below, and breaks the
|
|
# testName plugin call.
|
|
try:
|
|
desc = self.test.shortDescription()
|
|
except Exception:
|
|
# this is probably caused by a problem in test.__str__() and is
|
|
# only triggered by python 3.1's unittest!
|
|
pass
|
|
try:
|
|
if desc == str(self.test):
|
|
return
|
|
except Exception:
|
|
# If str() triggers an exception then ignore it.
|
|
# see issue 422
|
|
pass
|
|
return desc
|
|
|
|
|
|
class TestBase(unittest.TestCase):
|
|
"""Common functionality for FunctionTestCase and MethodTestCase.
|
|
"""
|
|
__test__ = False # do not collect
|
|
|
|
def id(self):
|
|
return str(self)
|
|
|
|
def runTest(self):
|
|
self.test(*self.arg)
|
|
|
|
def shortDescription(self):
|
|
if hasattr(self.test, 'description'):
|
|
return self.test.description
|
|
func, arg = self._descriptors()
|
|
doc = getattr(func, '__doc__', None)
|
|
if not doc:
|
|
doc = str(self)
|
|
return doc.strip().split("\n")[0].strip()
|
|
|
|
|
|
class FunctionTestCase(TestBase):
|
|
"""TestCase wrapper for test functions.
|
|
|
|
Don't use this class directly; it is used internally in nose to
|
|
create test cases for test functions.
|
|
"""
|
|
__test__ = False # do not collect
|
|
|
|
def __init__(self, test, setUp=None, tearDown=None, arg=tuple(),
|
|
descriptor=None):
|
|
"""Initialize the MethodTestCase.
|
|
|
|
Required argument:
|
|
|
|
* test -- the test function to call.
|
|
|
|
Optional arguments:
|
|
|
|
* setUp -- function to run at setup.
|
|
|
|
* tearDown -- function to run at teardown.
|
|
|
|
* arg -- arguments to pass to the test function. This is to support
|
|
generator functions that yield arguments.
|
|
|
|
* descriptor -- the function, other than the test, that should be used
|
|
to construct the test name. This is to support generator functions.
|
|
"""
|
|
|
|
self.test = test
|
|
self.setUpFunc = setUp
|
|
self.tearDownFunc = tearDown
|
|
self.arg = arg
|
|
self.descriptor = descriptor
|
|
TestBase.__init__(self)
|
|
|
|
def address(self):
|
|
"""Return a round-trip name for this test, a name that can be
|
|
fed back as input to loadTestByName and (assuming the same
|
|
plugin configuration) result in the loading of this test.
|
|
"""
|
|
if self.descriptor is not None:
|
|
return test_address(self.descriptor)
|
|
else:
|
|
return test_address(self.test)
|
|
|
|
def _context(self):
|
|
return resolve_name(self.test.__module__)
|
|
context = property(_context, None, None,
|
|
"""Get context (module) of this test""")
|
|
|
|
def setUp(self):
|
|
"""Run any setup function attached to the test function
|
|
"""
|
|
if self.setUpFunc:
|
|
self.setUpFunc()
|
|
else:
|
|
names = ('setup', 'setUp', 'setUpFunc')
|
|
try_run(self.test, names)
|
|
|
|
def tearDown(self):
|
|
"""Run any teardown function attached to the test function
|
|
"""
|
|
if self.tearDownFunc:
|
|
self.tearDownFunc()
|
|
else:
|
|
names = ('teardown', 'tearDown', 'tearDownFunc')
|
|
try_run(self.test, names)
|
|
|
|
def __str__(self):
|
|
func, arg = self._descriptors()
|
|
if hasattr(func, 'compat_func_name'):
|
|
name = func.compat_func_name
|
|
else:
|
|
name = func.__name__
|
|
name = "%s.%s" % (func.__module__, name)
|
|
if arg:
|
|
name = "%s%s" % (name, arg)
|
|
# FIXME need to include the full dir path to disambiguate
|
|
# in cases where test module of the same name was seen in
|
|
# another directory (old fromDirectory)
|
|
return name
|
|
__repr__ = __str__
|
|
|
|
def _descriptors(self):
|
|
"""Get the descriptors of the test function: the function and
|
|
arguments that will be used to construct the test name. In
|
|
most cases, this is the function itself and no arguments. For
|
|
tests generated by generator functions, the original
|
|
(generator) function and args passed to the generated function
|
|
are returned.
|
|
"""
|
|
if self.descriptor:
|
|
return self.descriptor, self.arg
|
|
else:
|
|
return self.test, self.arg
|
|
|
|
|
|
class MethodTestCase(TestBase):
|
|
"""Test case wrapper for test methods.
|
|
|
|
Don't use this class directly; it is used internally in nose to
|
|
create test cases for test methods.
|
|
"""
|
|
__test__ = False # do not collect
|
|
|
|
def __init__(self, method, test=None, arg=tuple(), descriptor=None):
|
|
"""Initialize the MethodTestCase.
|
|
|
|
Required argument:
|
|
|
|
* method -- the method to call, may be bound or unbound. In either
|
|
case, a new instance of the method's class will be instantiated to
|
|
make the call. Note: In Python 3.x, if using an unbound method, you
|
|
must wrap it using pyversion.unbound_method.
|
|
|
|
Optional arguments:
|
|
|
|
* test -- the test function to call. If this is passed, it will be
|
|
called instead of getting a new bound method of the same name as the
|
|
desired method from the test instance. This is to support generator
|
|
methods that yield inline functions.
|
|
|
|
* arg -- arguments to pass to the test function. This is to support
|
|
generator methods that yield arguments.
|
|
|
|
* descriptor -- the function, other than the test, that should be used
|
|
to construct the test name. This is to support generator methods.
|
|
"""
|
|
self.method = method
|
|
self.test = test
|
|
self.arg = arg
|
|
self.descriptor = descriptor
|
|
if isfunction(method):
|
|
raise ValueError("Unbound methods must be wrapped using pyversion.unbound_method before passing to MethodTestCase")
|
|
self.cls = method.im_class
|
|
self.inst = self.cls()
|
|
if self.test is None:
|
|
method_name = self.method.__name__
|
|
self.test = getattr(self.inst, method_name)
|
|
TestBase.__init__(self)
|
|
|
|
def __str__(self):
|
|
func, arg = self._descriptors()
|
|
if hasattr(func, 'compat_func_name'):
|
|
name = func.compat_func_name
|
|
else:
|
|
name = func.__name__
|
|
name = "%s.%s.%s" % (self.cls.__module__,
|
|
self.cls.__name__,
|
|
name)
|
|
if arg:
|
|
name = "%s%s" % (name, arg)
|
|
return name
|
|
__repr__ = __str__
|
|
|
|
def address(self):
|
|
"""Return a round-trip name for this test, a name that can be
|
|
fed back as input to loadTestByName and (assuming the same
|
|
plugin configuration) result in the loading of this test.
|
|
"""
|
|
if self.descriptor is not None:
|
|
return test_address(self.descriptor)
|
|
else:
|
|
return test_address(self.method)
|
|
|
|
def _context(self):
|
|
return self.cls
|
|
context = property(_context, None, None,
|
|
"""Get context (class) of this test""")
|
|
|
|
def setUp(self):
|
|
try_run(self.inst, ('setup', 'setUp'))
|
|
|
|
def tearDown(self):
|
|
try_run(self.inst, ('teardown', 'tearDown'))
|
|
|
|
def _descriptors(self):
|
|
"""Get the descriptors of the test method: the method and
|
|
arguments that will be used to construct the test name. In
|
|
most cases, this is the method itself and no arguments. For
|
|
tests generated by generator methods, the original
|
|
(generator) method and args passed to the generated method
|
|
or function are returned.
|
|
"""
|
|
if self.descriptor:
|
|
return self.descriptor, self.arg
|
|
else:
|
|
return self.method, self.arg
|