【发布时间】:2014-10-02 04:32:21
【问题描述】:
我的目标是创建一个简单的单元测试装饰器,它执行一个函数,如果成功,则什么也不做,如果不成功,则打印“FAILURE”及其所有参数。我确实知道内置的 unittest 包。我这样做是为了学习装饰器。我没有比“如果实际等于预期,什么也不做,否则打印参数”更进一步。
我找到了this function,它打印出一个函数的所有参数:
def dumpArgs(func):
'''Decorator to print function call details - parameters names and effective values'''
def wrapper(*func_args, **func_kwargs):
arg_names = func.__code__.co_varnames[:func.__code__.co_argcount]
args = func_args[:len(arg_names)]
defaults = func.__defaults__ or ()
args = args + defaults[len(defaults) - (func.__code__.co_argcount - len(args)):]
params = list(zip(arg_names, args))
args = func_args[len(arg_names):]
if args: params.append(('args', args))
if func_kwargs: params.append(('kwargs', func_kwargs))
print(func.__name__ + ' (' + ', '.join('%s = %r' % p for p in params) + ' )')
return func(*func_args, **func_kwargs)
return wrapper
@dumpArgs
def test(a, b = 4, c = 'blah-blah', *args, **kwargs):
pass
test(1)
test(1, 3)
test(1, d = 5)
test(1, 2, 3, 4, 5, d = 6, g = 12.9)
输出:
test (a = 1, b = 4, c = 'blah-blah' )
test (a = 1, b = 3, c = 'blah-blah' )
test (a = 1, b = 4, c = 'blah-blah', kwargs = {'d': 5} )
test (a = 1, b = 2, c = 3, args = (4, 5), kwargs = {'g': 12.9, 'd': 6} )
我把它改成了这个,只有当函数不等于4时才打印出参数(在没有装饰器参数的情况下实现):
def get_all_func_param_name_values(func, *func_args, **func_kwargs):
arg_names = func.__code__.co_varnames[:func.__code__.co_argcount]
args = func_args[:len(arg_names)]
defaults = func.__defaults__ or ()
args = args + defaults[len(defaults) - (func.__code__.co_argcount - len(args)):]
params = list(zip(arg_names, args))
args = func_args[len(arg_names):]
if args: params.append(('args', args))
if func_kwargs: params.append(('kwargs', func_kwargs))
return '(' + ', '.join('%s = %r' % p for p in params) + ')'
def dumpArgs(func):
'''Decorator to print function call details - parameters names and effective values'''
def wrapper(*func_args, **func_kwargs):
a = func(*func_args, **func_kwargs)
if(a != 4):
return a
print("FAILURE: " + func.__name__ + get_all_func_param_name_values(func, *func_args, **func_kwargs))
return a
return wrapper
@dumpArgs
def getA(a, b = 4, c = 'blah-blah', *args, **kwargs):
return a
getA(1)
getA(1, 3)
getA(4, d = 5)
getA(1, 2, 3, 4, 5, d = 6, g = 12.9)
输出:
FAILURE: getA(a = 4, b = 4, c = 'blah-blah', kwargs = {'d': 5})
Out[21]: 1
(我不明白为什么1 会打印在第二行。)
然后我将其更改为传入预期值4,作为装饰器参数。如this answer 中所述,它要求原始装饰器是嵌套函数:
def get_all_func_param_name_values(func, *func_args, **func_kwargs):
arg_names = func.__code__.co_varnames[:func.__code__.co_argcount]
args = func_args[:len(arg_names)]
defaults = func.__defaults__ or ()
args = args + defaults[len(defaults) - (func.__code__.co_argcount - len(args)):]
params = list(zip(arg_names, args))
args = func_args[len(arg_names):]
if args: params.append(('args', args))
if func_kwargs: params.append(('kwargs', func_kwargs))
return '(' + ', '.join('%s = %r' % p for p in params) + ')'
def dumpArgs(expected_value):
def dumpArgs2(func):
'''Decorator to print function call details - parameters names and effective values'''
def wrapper(*func_args, **func_kwargs):
a = func(*func_args, **func_kwargs)
if(a == expected_value):
return a
print("FAILURE: " + func.__name__ + get_all_func_param_name_values(func, *func_args, **func_kwargs))
return a
return wrapper
return dumpArgs2
@dumpArgs(4)
def getA(a, b = 4, c = 'blah-blah', *args, **kwargs):
return a
getA(1)
getA(1, 3)
getA(4, d = 5)
getA(1, 2, 3, 4, 5, d = 6, g = 12.9)
输出:
FAILURE: getA(a = 1, b = 4, c = 'blah-blah')
FAILURE: getA(a = 1, b = 3, c = 'blah-blah')
FAILURE: getA(a = 1, b = 2, c = 3, args = (4, 5), kwargs = {'g': 12.9, 'd': 6})
Out[31]: 1
(同样,1...)
我不清楚如何将这个硬编码的4 更改为expected_value 参数,该参数在每个函数调用中都会传递。我见过的所有示例(如this one)都有硬编码参数。
我目前正在尝试
assert_expected_func_params(4, getA, 1)
assert_expected_func_params(4, getA, 1, 3)
assert_expected_func_params(4, getA, 4, d = 5)
assert_expected_func_params(4, getA, 1, 2, 3, 4, 5, d = 6, g = 12.9)
但它远没有工作。
如何实现可以传递给每个函数调用的装饰器参数?
【问题讨论】:
-
记住 Python 之禅:可读性很重要。
-
我不知道你是否可以使用运行时值进行装饰,因为装饰器是在函数定义时评估的,之后就不能再评估了。
-
如果你有一个函数
assert_expected_func_params,你为什么需要一个装饰器? -
由于@TheSoundDefense 提供的原因,您不能使用
@decorator(arg)语法执行此操作,但您可以执行手动等效操作,即func = decorator(arg)(func)。 -
1出现的原因是因为这是您的getA函数的输出(它只返回a的任何参数)。无论是否应用装饰器,您都会得到相同的结果。
标签: python unit-testing python-3.x decorator