Autospec模拟不能正确保留签名吗?

时间:2019-01-08 20:57:59

标签: python unit-testing mocking inspect

根据mock guide

  

自动规范创建的模拟对象的属性和方法与要替换的对象相同,并且任何函数和方法(包括构造函数)的调用签名与真实对象相同。 < / p>

但这似乎不是事实。 Stdlib inspect仍然在模拟中看到通用的*args, **kwargs签名:

>>> from unittest.mock import patch
>>> def foo(arg1, arg2, arg3):
...     pass
...     
>>> print(*inspect.signature(foo).parameters)
arg1 arg2 arg3
>>> with patch("__main__.foo", autospec=True) as mock:
...     print(*inspect.signature(mock).parameters)
...     
args kwargs

autospec可以正常工作,因为mock(1,2,3,4)将正确地引发TypeError: too many positional arguments,但是看来这是通过调用堆栈中更深的一些代码实现的。不能通过呼叫签名来完成。

在您实际上依赖签名本身(并且在测试中进行模拟时需要保留正确的签名)的代码中,如何以一种可以正确保留签名的方式自动指定模拟?

1 个答案:

答案 0 :(得分:1)

这实际上被认为是Python中的错误,并且已经fixed in Python 3.8。补丁也是backported to Python 3.7.3。相关问题和请求请求:

我遇到了类似的问题,测试了一些代码来检查可笑对象的签名。我通过将模拟的__signature__属性设置为原始属性来解决了这个问题:

from inspect import signature
from unittest.mock import patch

def foo(a, b, c):
    pass

foo_sig = signature(foo)  # Need to cache the signature before mocking foo

with patch("__main__.foo") as mock:
    mock.__signature__ = foo_sig
    print(signature(mock))  # Prints '(a, b, c)'

之所以可行,是因为signature()函数在尝试其他方法之前遵循__signature__属性,如PEP 362中所述:

如果对象具有__signature__属性,而对象没有None,则将其返回

不幸的是,the documentation for the signature() functioninspect模块中没有提到这一点。