[英]Autospec “through” a decorator with unittest.mock
Suppose I have a simple decorated method like the following: 假设我有一个简单的装饰方法,如下所示:
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
The decorator "erases" the method signature for autospec'ing purposes: 装饰器为自动指定目的“擦除”方法签名:
>>> mock_foo = mock.create_autospec(Foo, instance=True)
>>> mock_foo.incr(1, 2, 3, 4)
<MagicMock name='mock.incr()' id='23032592'>
This should raise: 这应该引起:
TypeError: <lambda>() takes exactly 2 arguments (5 given)
I've had bugs like this creep through due to typos in keyword arguments. 由于关键字参数中的拼写错误,我遇到了类似这样的错误。
Is there any way to write the decorator (or give a "hint" to autospec) so that these kinds of errors will be caught? 有什么方法可以编写装饰器(或给“提示”进行自动指定),以便捕获此类错误?
I don't think autospec can do this directly. 我认为autospec不能直接做到这一点。 You can do a little hackery in the decorator to make it possible to test your undecorated function, though.
不过,您可以在装饰器中做一些黑客操作,以测试未修饰的功能。 If you make your decorator save a reference to the undecorated function:
如果让装饰器保存对未修饰函数的引用:
def my_decorator(fn):
def _wrapper(*args, **kwargs):
print 'Calling decorated function'
fn(*args, **kwargs)
_wrapper._orig = fn
return _wrapper
You can access it via the mocked decorated function: 您可以通过模拟的装饰函数访问它:
>>> 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'>
This doesn't work if you autospec the whole instance, though. 但是,如果您自动指定整个实例,则此方法不起作用。 Not sure why.
不知道为什么。
>>> 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'>
Also worth noting is Venusian , which can change the way decorators get bound to decorated methods specifically to address this use-case. 还值得注意的是Venusian ,它可以更改装饰器绑定到装饰方法的方式,专门解决此用例。 Might be more heavyweight than you want, though.
但是,可能会比您想要的更重。
A coworker pointed me to 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 implements the right magic to not hide the wrapped function's signature behind @my_decorator. @decorator实现了正确的魔术,不会将包装的函数的签名隐藏在@my_decorator后面。
You can use functools.wraps, mock.create_autospec understands it: 您可以使用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)
if you place code above into file and run, you'll see trace with last line: 如果将上面的代码放入文件中并运行,您将在最后一行看到跟踪:
TypeError: too many positional arguments
Without @wraps script ends with exit code 0. 如果不使用@wraps,则脚本以退出代码0结尾。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.