7

I have a pretty large test suite and I decorated some of the test_* functions. Now I can't call them by ./test.py MySqlTestCase.test_foo_double, python3.2 complains that: ValueError: no such test method in <class '__main__.MySqlTestCase'>: result. My decorator code looks like this:

def procedure_test(procedure_name, arguments_count, returns):

    '''Decorator for procedure tests, that simplifies testing whether procedure
    with given name is available, whether it has given number of arguments
    and returns given value.'''

    def decorator(test):
        def result(self):
            procedure = self.db.procedures[self.case(procedure_name)]
            self.assertEqual(len(procedure.arguments), arguments_count)
            self.assertEqual(procedure.returns, 
                             None if returns is None else self.case(returns))
            test(self, procedure)
        return result
    return decorator

and the test method:

@procedure_test('foo_double', 0, 'integer')
def test_foo_double(self, procedure):
    self.assertEqual(procedure.database, self.db)
    self.assertEqual(procedure.sql, 'RETURN 2 * value')
    self.assertArguments(procedure, [('value', 'int4')])
Martin Flucka
  • 3,125
  • 5
  • 28
  • 44
gruszczy
  • 40,948
  • 31
  • 128
  • 181

4 Answers4

7

I think the problem is that the decorated function doesn't have the same name and, also, it doesn't satisfy the pattern to be considered a test method.

Using functools.wrap to decorate decorator should fix your problem. More information here.

jcollado
  • 39,419
  • 8
  • 102
  • 133
2

This is a simple way of solving this issue.

from functools import wraps

def your_decorator(func):
    @wraps(func)
    def wrapper(self):
        #Do Whatever
        return func(self)
    return wrapper

class YourTest(APITestCase):
    @your_decorator
    def example(self):
        #Do your thing
Reez0
  • 2,512
  • 2
  • 17
  • 39
1

This help me:

from functools import wraps

...

@wraps(procedure_name)
def decorator(test):
Dmitry Dubovitsky
  • 2,186
  • 1
  • 16
  • 24
1

Based on this post:

You can do it this way:

def decorator(test):
    def wrapper(self):
        # do something interesting
        test(self)
        # do something interesting
    wrapper.__name__ = test.__name__
    return wrapper

This solution has two advantages over method with @functools.wrap:

  • doesn't need anything to import
  • doesn't need to know test name when decorator is created

Thanks to the second feature of this solution, it is possible to create decorators for many tests.

Community
  • 1
  • 1
pt12lol
  • 2,332
  • 1
  • 22
  • 48
  • 1
    This is the head-shot that kill the issue by 1 single blow. Must be the accepted one to me. – Nam G VU Jul 31 '18 at 09:51
  • Note that functools.wraps does more than just setting name, such as docstring preservation. And the normal use of `wraps` in a decorator is ``` def decorator(func): @wraps(func) wrapper(*args) ... ```` Which can similarly wrap arbitrarily-named test cases. – isturdy Dec 18 '18 at 23:02