我正在尝试通过以下方式模拟函数:
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)
但是我不确定这是否是修补的好方法。对我来说,这看起来像是在自找麻烦,但我不确定那是什么麻烦。
那么,我该如何修补功能对象?
答案 0 :(得分:0)
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, ...)
已正确修补。