用参数调用装饰器时出现问题

时间:2019-06-05 09:30:49

标签: python python-2.7

我试图测试下面的装饰器test_params进行单元测试,但是当我尝试在函数test_testparams中调用它时,出现错误:

TypeError: wrapper() takes exactly 1 argument (0 given)

是因为它期待自己吗?

import unittest
import functools


def test_params(kwargs_lst):
    '''allows to parametrise tests easily using a decorator'''

    def decorator(fn):
        @functools.wraps(fn)
        def wrapper(self):
            for kwargs in kwargs_lst:
                fn(self, **kwargs)

        return wrapper

    return decorator


TEST_PARAMS_ARGS = [
    {'arg1': 1, 'arg2': None, 'arg3': "test"},
]

RECORDER = []


class TestParamsClass(object):
    def record_args(self, arg1, arg2, arg3):
        RECORDER.append({'arg1': arg1, 'arg2': arg2, 'arg3': arg3})


class DecoratorTest(unittest.TestCase):
    def setUp(self):
        pass

    def test_testparams(self):
        obj = TestParamsClass()

        decorated_fn = test_params(TEST_PARAMS_ARGS)(obj.record_args)
        decorated_fn()
        print("RECORDED:{}".format(RECORDER))

为全面披露,我已经尝试过传递self参数:

decorated_fn(obj)

但这会给出此错误:

TypeError: record_args() got multiple values for keyword argument 'arg1'

我也尝试使用:

from functools import partial
decorated_fn = test_params(TEST_PARAMS_ARGS)(partial(obj.record_args, self=obj))

这给了我错误:

AttributeError: 'functools.partial' object has no attribute '__module__'

有一个自变量的原因是,它通常在测试函数中用于传递参数,并且测试函数位于unittest.TestCase内部。

1 个答案:

答案 0 :(得分:1)

这与what Python methods really are有关。

通常,您将装饰器应用于类声明中的函数,即:

class TestParamsClass(object):
    @test_params(TEST_PARAMS_ARGS)
    def record_args(self, arg1, arg2, arg3):
        RECORDER.append({'arg1': arg1, 'arg2': arg2, 'arg3': arg3})

在这种情况下,test_params(或更确切地说是内部decorator函数)将收到record_args 函数作为参数。但是这里:

decorated_fn = test_params(TEST_PARAMS_ARGS)(obj.record_args)

您要传递的是一个Method对象(请参见上面的链接),该__call__方法将在调用{{时将obj作为self参数注入1}}功能。

如果您的record_args()装饰器应专门用于方法(或更确切地说,仅用于方法的函数),则可以更改测试以装饰该方法的功能:

test_param

当然还有手动传递decorated_fn = test_params(TEST_PARAMS_ARGS)(obj.record_args.__func__) 作为参数:

obj

否则,如果您想同时在函数和方法上使用此修饰符,则可以将内部decorated_fn(obj) 函数签名从wrapper更改为self(并将其传递给*args,因此可以适应两种情况。