Reputation: 10113
I am trying to test the decorator test_params below for an unittest, but when I try to call it in the function test_testparams, I get an error:
TypeError: wrapper() takes exactly 1 argument (0 given)
Is it because it is expecting self ?
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))
For full disclosure, I have already tried passing the self argument:
decorated_fn(obj)
but this will give this error:
TypeError: record_args() got multiple values for keyword argument 'arg1'
I tried as well using:
from functools import partial
decorated_fn = test_params(TEST_PARAMS_ARGS)(partial(obj.record_args, self=obj))
This gives me the error:
AttributeError: 'functools.partial' object has no attribute '__module__'
The reason there is a self argument is that it is normally used in test functions to pass arguments and the test function is inside an unittest.TestCase.
Upvotes: 0
Views: 92
Reputation: 77942
This has to do with what Python methods really are.
Normally you would apply the decorator to the function in the class declaration, ie:
class TestParamsClass(object):
@test_params(TEST_PARAMS_ARGS)
def record_args(self, arg1, arg2, arg3):
RECORDER.append({'arg1': arg1, 'arg2': arg2, 'arg3': arg3})
in which case test_params
(or more exactly the inner decorator
function) will receive the record_args
function as argument. But here:
decorated_fn = test_params(TEST_PARAMS_ARGS)(obj.record_args)
what you're passing is a Method
object (cf the link above), which __call__
method will already inject obj
as the self
argument when calling record_args()
function.
If your test_param
decorator is supposed to be used exclusively on methods (or, more exactly, on functions that will only be used as methods), you can change you test to decorate the method's function instead:
decorated_fn = test_params(TEST_PARAMS_ARGS)(obj.record_args.__func__)
and of course manually pass obj
as argument:
decorated_fn(obj)
Else if you want to be able to use this decorator on both functions and methods, you can change the inner wrapper
function signature from self
to *args
(and pass it along to fn
so it can accomodate both situations.
Upvotes: 1