代码结构如下。
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/