我正在尝试通过以下方式模拟函数:
def foo(x):
return 10 * x
from unittest.mock import patch
with patch.object(foo, "__call__", lambda x: 100 * x):
print(foo(5))
print(foo.__call__(5))
但是输出如下:
50
500
而不是
500
500
也就是说,foo(5)
不使用模拟的__call__
属性。我假设像 foo(5) 这样的函数调用总是转换为 foo.__call__(5) 。显然事实并非如此,但我不知道为什么。是否可以使用 patch.object
模拟该函数?
我知道我可以用 patch("path.to.foo", lambda x: 100*x)
来修补它,但我希望我可以修补对象而不需要知道路径。但也许我只需要做类似的事情:
patch(foo.__module__+'.'+foo.__name__, lambda x: 100*x)
但我不确定这是否是一个好的修补方法。对我来说,这看起来像是自找麻烦,但我不确定那个麻烦可能是什么。
那么,如何修补函数对象?
最佳答案
foo(5)
转换为 type(foo).__call__(foo, 5)
而不是 foo.__call__(5)
。
因此,需要修改类的 __call__
方法,而不是实例。为了不与同一类的其他实例混淆,应该从一个唯一的类创建实例,然后可以自由地修补该类。
我找不到更改函数对象的类的方法,因此我创建了一个简单的函数包装器,它将函数转换为唯一类的实例,同时仍具有与原始函数类似的行为。然后,可以修补该类的 __call__ 方法:
from unittest import mock
def mockable_function(f):
class Function():
def __call__(self, *args, **kwargs):
return f(*args, **kwargs)
return Function()
@mockable_function
def foo(x):
return 10 * x
with mock.patch.object(type(foo), "__call__", lambda _, x: 100 * x):
print(foo(5))
print(foo.__call__(5))
输出:
500
500
也许不是很漂亮,但至少是一种编写可以使用 mock.patch.object
修改的函数的方法。
根据我的测试,使用 mock.patch("some.module.foo", ...)
的问题是,如果某些模块导入了 foo
作为 from some.module import foo
, foo
不会受到影响,而使用 mock.patch.object(foo, ...)
> 已正确修补。
关于python - 模拟 __call__ 属性不会模拟该函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57822802/