假设我有一个简单的装饰方法,如下所示:
def my_decorator(fn):
def _wrapper(*args, **kwargs):
print 'Calling decorated function'
fn(*args, **kwargs)
return _wrapper
class Foo(object):
@my_decorator
def incr(self, x):
return x+1
装饰器“擦除”用于自动指定目的的方法签名:
>>> mock_foo = mock.create_autospec(Foo, instance=True)
>>> mock_foo.incr(1, 2, 3, 4)
<MagicMock name='mock.incr()' id='23032592'>
这应该提出:
TypeError: <lambda>() takes exactly 2 arguments (5 given)
由于关键字参数中的拼写错误,我遇到了类似这样的错误。
有没有办法编写装饰器(或给autospec提供“提示”)以便捕获这些类型的错误?
答案 0 :(得分:1)
我不认为autospec可以直接执行此操作。不过,你可以在装饰器中做一些小装饰,以便测试你的未修饰功能。如果您让装饰者保存对未修饰函数的引用:
def my_decorator(fn):
def _wrapper(*args, **kwargs):
print 'Calling decorated function'
fn(*args, **kwargs)
_wrapper._orig = fn
return _wrapper
您可以通过模拟的装饰功能访问它:
>>> mock_incr = mock.create_autospec(Foo.incr)
>>> mock_incr(1,3,4,5,5) # Decorated function doesn't fail.
<MagicMock name='mock()' id='8734864'>
>>> mock_incr._orig(1,3,4,5,5) # But the original does, which is what we want
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib64/python2.6/site-packages/mock.py", line 954, in __call__
_mock_self._mock_check_sig(*args, **kwargs)
TypeError: <lambda>() takes exactly 3 arguments (6 given)
>>> mock_incr._orig(1,3)
<MagicMock name='mock._orig()' id='8739664'>
但是,如果您自动指定整个实例,则这不起作用。不知道为什么。
>>> mock_foo = mock.create_autospec(Foo, instance=True)
>>> mock_foo.incr(1,3,4,5) # We expect this to not raise an exception
<MagicMock name='mock.incr2()' id='8758416'>
>>> mock_foo.incr._orig(1,3,4,5) # But we were hoping this would :(
<MagicMock name='mock.incr._orig()' id='8740624'>
另外值得注意的是Venusian,它可以改变装饰器绑定到装饰方法的方式,专门用于解决此用例。不过,可能比你想要的更重量级。
答案 1 :(得分:0)
一位同事指着我the decorator library
from decorator import decorator
@decorator
def my_decorator(fn, *args, **kwargs):
print 'Calling decorated function'
return fn(*args, **kwargs)
class Foo(object):
@my_decorator
def incr(self, x):
return x+1
@decorator实现了正确的魔法,不会隐藏@my_decorator背后的包装函数的签名。
答案 2 :(得分:0)
你可以使用functools.wraps,mock.create_autospec理解它:
from functools import wraps
from unittest import mock
def my_decorator(fn):
@wraps(fn)
def _wrapper(*args, **kwargs):
print('Calling decorated function')
fn(*args, **kwargs)
return _wrapper
class Foo(object):
@my_decorator
def incr(self, x):
return x + 1
if __name__ == '__main__':
mock_foo = mock.create_autospec(Foo, instance=True)
mock_foo.incr(1, 2, 3, 4)
如果您将代码置于文件中并运行,则会在最后一行中看到跟踪:
TypeError: too many positional arguments
没有@wraps脚本以退出代码0结束。