Two days ago, I asked how to generates tests from tables of fixtures using Nose:
...does Nose already have a tool for running through a table of fixtures and expected results? My hand-rolled version is:
Tests = ( # R1 R2 Expected ( ((0, 0), (0, 0)), ((0, 0), (0, 0)), None ), ( ((0, 0), (0, 0)), ((0, 0), (1, 1)), None ), ( ((0, 0), (1, 1)), ((0, 0), (1, 1)), ((0, 0), (1, 1)) ), ( ((0, 3), (2, 5)), ((1, 0), (2, 4)), ((1, 3), (2, 4)) ) ) def test_table(): for (R1, R2, expected) in Tests: yield run_it, R1, R2, expected def run_it(R1, R2, expected): assert overlap(R1, R2) == expectedwhich is simple enough if students already understand generators and function application, but hell to explain if they don't—and they won't.
After some back and forth, Jacob Kaplan-Moss (of Django fame) came up with this:
def tabletest(table): def decorator(func): def _inner(): for args in table: yield tuple([func] + list(args)) _inner.__name__ = 'test_'+func.__name__ return _inner return decorator table = [(1, 2), (3, 4)] @tabletest(table) def check_pair(left, right): assert left > right
The outer function tabletest
takes the table of fixtures as an argument, and produces a function of one argument. That argument is supposed to be the function that is being wrapped up by the decorator, so:
@tabletest(table) def check_pair(...): ...
means:
decorator = tabletest(table) check_pair = ...what the 'def' creates... check_pair = decorator(check_pair)
With me so far? Now, what decorator
does is take a function F as an argument, and create a new function F' that produces each combination of the original F with the entries in the table: in jargon, it creates a generator that yields F and the arguments that F should be applied to.
But what's that inner_.__name__
stuff? That's to make sure that the wrapped function's name starts with the letters "test_", because that's how Nose knows to run it.
This does exactly what I wanted, but sparks three comments:
#3 is what many advocates of new technology (functional languages! GPUs! functional languages on GPUs!) consistently overlook. What Jacob did here is really quite elegant, but in the same way that the classic proof of Euler's theorem is elegant: you have to know quite a lot to understand it, and even more to understand its grace. People who have that understanding often forget what the world looks like to people who don't; we're trying hard not to, and would be grateful if readers and viewers could tell us when we slip up.
Originally posted 2010-08-05 by Greg Wilson in Community, Opinion.
comments powered by Disqus