pygo/gomill/gomill_tests/test_framework.py

159 lines
4.3 KiB
Python

"""Generic (non-gomill-specific) test framework code."""
import sys
if sys.version_info >= (2, 7):
import unittest as unittest2
else:
try:
import unittest2
except ImportError, e:
e.unittest2_missing = True
raise
# This makes TestResult ignore lines from this module in tracebacks
__unittest = True
class SupporterError(StandardError):
"""Exception raised by support objects when something goes wrong.
This is raised to indicate things like sequencing errors detected by mock
objects.
"""
class FrameworkTestCase(unittest2.TestCase):
"""unittest2-style TestCase implementation with a few tweaks."""
# This is default in unittest2 but not python 2.7 unittest, so force it on.
longMessage = True
def assertItemsEqual(self, expected_seq, actual_seq, msg=None):
"""Variant implementation of standard assertItemsEqual.
This uses the unorderable_list_difference check even if the lists are
sortable: I prefer its output.
"""
expected = list(expected_seq)
actual = list(actual_seq)
missing, unexpected = unittest2.util.unorderable_list_difference(
expected, actual, ignore_duplicate=False
)
errors = []
if missing:
errors.append('Expected, but missing:\n %s' %
unittest2.util.safe_repr(missing))
if unexpected:
errors.append('Unexpected, but present:\n %s' %
unittest2.util.safe_repr(unexpected))
if errors:
standardMsg = '\n'.join(errors)
self.fail(self._formatMessage(msg, standardMsg))
class SimpleTestCase(FrameworkTestCase):
"""TestCase which runs a single function.
Instantiate with the test function, which takes a TestCase parameter, eg:
def test_xxx(tc):
tc.assertEqual(2+2, 4)
"""
def __init__(self, fn):
FrameworkTestCase.__init__(self)
self.fn = fn
try:
self.name = fn.__module__.split(".", 1)[-1] + "." + fn.__name__
except AttributeError:
self.name = str(fn)
def runTest(self):
self.fn(self)
def id(self):
return self.name
def shortDescription(self):
return None
def __str__(self):
return self.name
def __repr__(self):
return "<SimpleTestCase: %s>" % self.name
class ParameterisedTestCase(FrameworkTestCase):
"""Parameterised testcase.
Subclasses should define:
test_name -- short string
parameter_names -- list of identifiers
runTest
"""
def __init__(self, code, *parameters):
FrameworkTestCase.__init__(self)
self.code = code
self.name = "%s.%s:%s" % (self.__class__.__module__.split(".", 1)[-1],
self.test_name, code)
for name, value in zip(self.parameter_names, parameters):
setattr(self, name, value)
def runTest(self):
raise NotImplementedError
def id(self):
return self.name
def shortDescription(self):
return None
def __str__(self):
return self.name
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.name)
def _function_sort_key(fn):
try:
return fn.__code__.co_firstlineno
except AttributeError:
return str(fn)
def make_simple_tests(source, prefix="test_", testcase_class=SimpleTestCase):
"""Make test cases from a module's test_xxx functions.
source -- dict (usually a module's globals()).
prefix -- string (default "test_")
testcase_class -- SimpleTestCase subclass to use
Returns a list of TestCase objects.
This makes a TestCase for each function in the values of 'source' whose
name begins with 'prefix'.
The list is in the order of function definition (using the line number
attribute).
"""
functions = [value for name, value in source.iteritems()
if name.startswith(prefix) and callable(value)]
functions.sort(key=_function_sort_key)
return [testcase_class(fn) for fn in functions]
class Fixture(object):
"""A testing fixture.
Instantiate fixture objects with a TestCase parameter.
The fixture arranges for any necessary cleanup to be performed by calling
TestCase.addCleanUp.
"""