Ordering tests in a testsuite
Peter Otten
__peter__ at web.de
Thu Oct 7 14:37:57 EDT 2010
Ulrich Eckhardt wrote:
> I'm currently working on a testsuite using Python's unittest library. This
> works all good and fine, but there's one thing where I haven't seen an
> elegant solution to yet, and that is the ordering. Currently, it takes all
> classes and orders them alphabetically and then takes all test functions
> therein and runs those alphabetically, too. However, sometimes it doesn't
> make sense to run test_bar() if test_foo() already failed, because they
> basically build upon each other. However, test_bar() is still run first,
> and test_foo() second.
>
> What I sometimes do is to simply number them, like test_1_foo() and
> test_2_bar(), but that seems ugly. I'm not even looking for a way to
> express complicated relations between tests, but is there a less ugly way
> to explicitly order them?
You can pass unittest.main() a custom test loader which in turn can define a
custom sortTestMethodsUsing() method. Here's a fancy example:
import unittest
def _toposort(data):
# adapted from http://code.activestate.com/recipes/577413-topological-
sort/
for k, v in data.items():
v.discard(k) # Ignore self dependencies
extra_items_in_deps = reduce(set.union, data.values()) -
set(data.keys())
data.update((item, set()) for item in extra_items_in_deps)
while True:
ordered = set(item for item,dep in data.items() if not dep)
if not ordered:
break
for item in sorted(ordered):
yield item
data = dict((item, (dep - ordered)) for item,dep in data.items()
if item not in ordered)
def _get_name(func):
try:
return func.__name__
except AttributeError:
pass
assert isinstance(func, str)
return func
class Cmp(object):
def __init__(self):
self._deps = {}
def depends(self, f, names):
k = _get_name(f)
assert k not in self._deps
self._deps[k] = set(_get_name(n) for n in names)
return f
def make_loader(self):
lookup = dict((name, index) for index, name
in enumerate(_toposort(self._deps)))
def compare(a, b):
return cmp(lookup[a], lookup[b])
class TestLoader(unittest.TestLoader):
pass
loader = TestLoader()
loader.sortTestMethodsUsing = compare
return loader
def depends_on(self, *names):
def d(f):
return self.depends(f, names)
return d
c = Cmp()
depends_on = c.depends_on
class A(unittest.TestCase):
@depends_on("test_beta")
def test_alpha(self):
pass
@depends_on("test_gamma")
def test_beta(self):
pass
def test_gamma(self):
pass
@depends_on(test_gamma)
def test_delta(self):
pass
@depends_on(test_alpha, test_beta)
def test_epsilon(self):
pass
if __name__ == "__main__":
unittest.main(testLoader=c.make_loader())
Peter
More information about the Python-list
mailing list