【问题标题】:Autospec mock does not correctly preserve signature?Autospec 模拟没有正确保留签名?
【发布时间】:2019-01-08 20:57:58
【问题描述】:

根据mock guide

自动指定创建的模拟对象与被替换的对象具有相同的属性和方法,并且任何函数和方法(包括构造函数)都具有与真实对象相同的调用签名。

但这似乎不是真的。 Stdlib inspect 仍然在 mock 上看到一个通用的 *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

自动规范起作用,因为mock(1,2,3,4) 将正确地引发TypeError: too many positional arguments,但似乎这是通过调用堆栈中更深的一些代码实现的。这不是通过调用签名完成的。

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

【问题讨论】:

  • mock.assert_called_with 是手动执行此检查的方式吗? (即不是自动指定,而是检查签名)

标签: python unit-testing mocking inspect


【解决方案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 模块中没有提到这一点。

【讨论】:

  • 这个问题已经在上游修复了。如果您最近升级到 Python 3.7.3+ 并且仍在手动设置 __signature__,我认为您可以将其从代码中删除(已直接编辑您的答案以在顶部提及这一点)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-14
  • 2020-02-27
  • 1970-01-01
相关资源
最近更新 更多