adam.pre...@gmail.com wrote: > We were coming into Python's unittest module from backgrounds in nunit, > where they use a decorate to identify tests. So I was hoping to avoid the > convention of prepending "test" to the TestClass methods that are to be > actually run. I'm sure this comes up all the time, but I mean not to have > to do: > > class Test(unittest.TestCase): > def testBlablabla(self): > self.assertEqual(True, True) > > But instead: > class Test(unittest.TestCase): > @test > def Blablabla(self): > self.assertEqual(True, True) > > This is admittedly a petty thing. I have just about given up trying to > actually deploy a decorator, but I haven't necessarily given up on trying > to do it for the sake of knowing if it's possible. > > Superficially, you'd think changing a function's __name__ should do the > trick, but it looks like test discovery happens without looking at the > transformed function. I tried a decorator like this: > > def prepend_test(func): > print "running prepend_test" > func.__name__ = "test" + func.__name__ > > def decorator(*args, **kwargs): > return func(args, kwargs) > > return decorator > > When running unit tests, I'll see "running prepend_test" show up, but a > dir on the class being tested doesn't show a renamed function. I assume > it only works with instances. Are there any other tricks I could > consider?
I think you are misunderstanding what a decorator does. You can think of def f(...): ... as syntactic sugar for an assignment f = make_function(...) A decorator intercepts that f = decorator(make_function(...)) and therefore can modify or replace the function object, but has no influence on the name binding. For unittest to allow methods bound to a name not starting with "test" you have to write a custom test loader. import functools import unittest.loader import unittest def test(method): method.unittest_method = True return method class MyLoader(unittest.TestLoader): def getTestCaseNames(self, testCaseClass): def isTestMethod(attrname, testCaseClass=testCaseClass, prefix=self.testMethodPrefix): attr = getattr(testCaseClass, attrname) if getattr(attr, "unittest_method", False): return True return attrname.startswith(prefix) and callable(attr) testFnNames = list(filter(isTestMethod, dir(testCaseClass))) if self.sortTestMethodsUsing: testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing)) return testFnNames class A(unittest.TestCase): def test_one(self): pass @test def two(self): pass if __name__ == "__main__": unittest.main(testLoader=MyLoader()) Alternatively you can write a metaclass that *can* intercept the name binding process: $ cat mytestcase.py import unittest __UNITTEST = True PREFIX = "test_" class Type(type): def __new__(class_, name, bases, classdict): newclassdict = {} for name, attr in classdict.items(): if getattr(attr, "test", False): assert not name.startswith(PREFIX) name = PREFIX + name assert name not in newclassdict newclassdict[name] = attr return type.__new__(class_, name, bases, newclassdict) class MyTestCase(unittest.TestCase, metaclass=Type): pass def test(method): method.test = True return method $ cat mytestcase_demo.py import unittest from mytestcase import MyTestCase, test class T(MyTestCase): def test_one(self): pass @test def two(self): pass if __name__ == "__main__": unittest.main() $ python3 mytestcase_demo.py -v test_one (__main__.test_two) ... ok test_two (__main__.test_two) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK -- http://mail.python.org/mailman/listinfo/python-list