Autospec 模拟没有正确保留签名?

Autospec mock does not correctly preserve signature?

根据 mock guide:

Auto-speccing creates mock objects that have the same attributes and methods as the objects they are replacing, and any functions and methods (including constructors) have the same call signature as the real object.

但这似乎不是真的。 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,但似乎这是通过调用堆栈中更深的一些代码实现的.这不是通过调用签名完成的。

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

这实际上被认为是 Python 中的一个错误,并且一直 fixed in Python 3.8. A patch was also 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:

中所述

If the object has a __signature__ attribute and if it is not None - return it

遗憾的是,the documentation for the signature() functioninspect 模块中并未提及。