decorators to add test* TestCase methods

Bruce Cropley brucedecoy-post at yahoo.com.au
Fri Jan 27 07:07:54 EST 2006


Hi all

I'm trying to generate test methods in a unittest TestCase
subclass, using decorators.  I'd like to be able to say:

class MyTestCase(unittest.TestCase):
    @genTests(["Buy", "Sell"], [1,2,3], [True, False])
    def something(self, side, price, someFlag):
        # etc...

And have it generate functions called:
    test_Buy_1_True_something
    test_Buy_1_False_something
through to...
    test_Sell_3_False_something

Each of these would call the something function with the
expected parameters. I guess that the new 2.5 partial()
could help with this.

There are two problems:
- Decorators don't seem to be able to add functions to the
  class because it is still being defined. I know decorators
  aren't really intended for this, but it would be nice to keep
  the expansion call near the generic test method source.
- There seems to be a bug with creating functions in a loop,
  see below...

Here's my attempt so far:

---------8<-----------
#!/usr/bin/env python

# Generate all combinations of values of args.
def product(*args):
    if len(args) == 0:
        return [[]]
    return [[val] + rest
            for val in args[0]
            for rest in product(*args[1:])]

class Test:
    # Making this a classmethod doesn't seem to have gained me much.
    # perhaps it should be just a function, and use func.im_class?
    @classmethod
    def genTests(cls, func, *args):
        """ Generate all test functions of the form:
        test_B_Harry_{func.func_name}
        test_B_Sally_{func.func_name}
        test_S_Harry_{func.func_name}
        test_S_Sally_{func.func_name}
        ... which call self.func with the corresponding parameters.
        """
        for param_combo in product(*args):
            #print "In loop, generating %s" % param_combo
            def testFunc(self):
                #print "In test func: %s" % param_combo
                func(self, *param_combo)
            func_name = "_".join(["test"] + param_combo +
[func.func_name])
            setattr(cls, "%s" % func_name, testFunc)

class Derived(Test):
    # I want to be able to do:
    # @genTests(["B", "S"], ["Harry", "Sally"])
    def blah(self, side, name):
        print "blah: %s %s" % (side, name)

t = Derived()

# Hack
Derived.genTests(Derived.blah, ["B", "S"], ["Harry", "Sally"])

t.test_S_Sally_blah()

# This is failing (generates test_S_Sally_blah),
# and appears to be a python bug:
t.test_B_Harry_blah()
---------8<----------

Does anyone have any suggestions?

Thanks,
Bruce




More information about the Python-list mailing list