Python 模拟调用属性不';不要嘲笑这个函数

Python 模拟调用属性不';不要嘲笑这个函数,python,unit-testing,mocking,python-unittest,Python,Unit Testing,Mocking,Python Unittest,我试图用以下方式模拟函数: 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)不使用模拟的\uuu调用\uu属性。我假设像foo(5)

我试图用以下方式模拟函数:

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)
不使用模拟的
\uuu调用\uu
属性。我假设像
foo(5)
这样的函数调用总是转换为
foo.\uu调用\uuu(5)
。显然不是这样,但我不知道为什么。是否可以使用
patch.object
模拟函数

我知道我可以用
补丁(“path.to.foo”,lambda x:100*x)
来修补它,但我希望我可以在不需要知道路径的情况下修补对象。但也许我只需要做一些事情,比如:

patch(foo.__module__+'.'+foo.__name__, lambda x: 100*x)
但我不确定这是否是一种很好的修补方法。对我来说,这看起来像是自找麻烦,但我不确定那会是什么麻烦

那么,如何修补函数对象呢?

foo(5)
转换为
type(foo)。\uuuu调用\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

因此,需要修改类的
\uuuuu call\uuuu
方法,而不是实例。为了不干扰同一类的其他实例,应该从唯一的类创建实例,然后可以自由地修补该类

我找不到改变函数对象的类的方法,所以我为函数创建了一个简单的包装器,它将函数转换为一个唯一类的实例,同时仍与原始函数的行为类似。然后,可以修补该类的
\uuu调用\uu
方法:

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,…)
的问题是,如果一些模块从some.module import foo
导入
foo
作为
,那么
foo
不会受到影响,而使用
mock.patch.object(foo,…)
它被正确地修补了。

我不知道这里发生了什么,但我肯定事情是这样的,然后猴子修补可能会成为一场噩梦。然后是你已经注意到的在哪里修补的问题。即使你做对了,当你重新组织你的代码时,事情也会破裂。我只能建议你不要打补丁。一定有更好的解决方法。事实上,
foo(5)
被转换为
type(foo)。\uuuu调用(foo,5)
。另外,与X-Y问题的联系也是一个很好的观点。所以,我实际上想要实现的是:我有一些具有副作用的功能(例如,读取文件、查询数据库)。在单元测试中,为了简单起见,我想用纯模拟函数替换其中的一些函数,然后可以用纯方法测试调用这些副作用函数的其他函数。这可能不是一个完美的设计,但考虑到当前的代码库,这似乎是最好的方法。