python - 模拟 __call__ 属性不会模拟该函数

标签 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) 不使用模拟的__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 foofoo 不会受到影响,而使用 mock.patch.object(foo, ...) > 已正确修补。

关于python - 模拟 __call__ 属性不会模拟该函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57822802/

相关文章:

python - LDA 和 pLSA 包(最好用于 Python)

python - 合并具有分层列的两个数据框

java - 估计大型代码库中所需的单元测试

python - 正在重置的变量

python - Plotly:行数 > 500 时不显示范围 slider

javascript - 如何在backbone.js中为事件编写单元测试

c# - 使用 Moq 验证采用委托(delegate)参数的基类中的方法调用

python - 确定 unittest.mock.patch 的目标

python - 调用 python Mock 时如何运行函数(以获得副作用)?

unit-testing - 可以在 Controller 的show()方法中使用模拟域 “instance”吗?