python - 如何模拟未从 __init__ 文件中公开的函数?

标签 python unit-testing mocking python-module

代码结构如下。

service_a/__init__.py:

from .func_a import func_a

service_a/func_a.py

def func_a():
     ...

service_b/__init__.py:

from .func_b import func_b

service_b/func_b.py:

from service_a import func_a
def func_b():
     func_a()
     ...

现在,当我对 func_b 进行单元测试时,我不确定如何模拟 func_a。我的测试代码如下:

from unittest import mock
from service_b import func_b

# within test method:
with mock.patch('[patch_path_for_func_a]') as mock_func:
    func_b()
    ...

我不知道要在 [patch_path_for_func_a] 中放入什么,因为 func_a 在运行时不会从 service_b 公开。

最佳答案

好吧,如果你真的想做到这一点,有很多方法,但可能不是一个好方法。例如,您可以将 service_b 添加到 sys.path,以便可以导入模块 func_b:

import sys
from unittest import mock

import service_b
sys.path.extend(service_b.__path__)
import func_b

with mock.patch('func_b.func_a') as mock_func: 
    func_b.func_b()
    mock_func.assert_called_once_with()

现在,如果您认为尝试编写此测试很痛苦,我建议您退后一步,找出痛苦的根源并摆脱它。您正在主动隐藏模块 service_b.func_b 以便它无法轻松导入,然后您的测试正是想要这样做。代码的不同部分有相反的目标,这就是造成痛苦的原因。

您可以通过以下几种方法来避免这种情况,它们可能适合您的具体情况,也可能不适用于您的具体情况(很难就 foobar 示例提供具体建议):

不要隐藏模块

如果您没有为模块和函数指定相同的名称,或者以某种方式重新组织代码,以便可以轻松导入正在查找的 func_a() 模块,则问题就会消失。

不要模拟 func_a()

func_b() 调用 func_a() 看起来像一个实现细节,因此如果测试不关心它,它可能是有意义的。只需调用 func_b() 并断言它返回预期值。

明确依赖关系

如果您确实认为测试应该使用 func_a() 的替代实现(在本例中为模拟)来调用 func_b() ,则表明其他代码可能想做同样的事情。使依赖关系明确将使任何代码(包括测试)都更容易,并且将避免猴子修补的需要以及必须考虑修补位置的分心。

def func_b(func_a):
    func_a()
    ...

然后测试就很简单(没有猴子补丁,没有紧张的导入,没有不相关和分散注意力的细节):

func_a = mock.Mock()  # arrange
func_b(func_a)  # act
func_a.assert_called_once_with()  # assert

关于python - 如何模拟未从 __init__ 文件中公开的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57486433/

相关文章:

python - 在python中导入其他文件和全局变量

python - python/cv2:创建某种颜色的像素的二维矩阵

unit-testing - nestjs 中的单元测试中间件

java - 代码覆盖率中应该包含注释吗?

c# - 使用 Moq 调用原始方法

python - 何时将 http 方法映射到查看方法 django rest framework

python - 识别数据框中的 ID 列

java - 使用 Pageable 参数对 RestController 进行单元测试时获取 BeanInstantiationException

javascript - 在 Node 中模拟一个使用链式函数调用的 Node 模块

java - 模拟 BindingProvider 和生成的 stub 类时出现问题