如何无需修改main.py调用路径即可Mock run函数?
问题:无需修改main.py调用路径,如何Mock指定函数?
原代码场景
main.py
from path.to.mod import run def foo(args: str): run(args)
test.py
@patch("path.to.mod.run") def verify_args(mock): foo("bar") mock.assert_called_once_with("bar")
上述代码无法Mock run 函数,断言会失败。但如果将main.py修改为直接通过模块路径调用的形式:
def foo(args: str): path.to.mod.run(args)
测试就能成功Mock。
现在需要解决的是:不修改main.py的调用写法,如何实现正确Mock?
错误尝试及报错
如果将test.py修改为以下写法:
from path.to.mod import run @patch("run") def verify_args(mock): foo("bar") mock.assert_called_once_with("bar")
会触发如下报错:
TypeError: Need a valid target to patch. You supplied: 'run'
解决方案
问题根源在于Python的导入机制:当main.py中使用from path.to.mod import run时,run会被作为独立引用存入main.py的全局命名空间,foo函数调用的是这个本地引用,而非原模块path.to.mod中的run。因此,我们需要Mock的是main.py里的run引用,而非原模块的run。
修改test.py的patch目标为main.run(若main.py不在当前测试模块的导入路径下,需替换为完整的模块路径,比如my_package.main.run):
from unittest.mock import patch from main import foo @patch("main.run") def verify_args(mock_run): foo("bar") mock_run.assert_called_once_with("bar")
原理说明
- 使用
from X import Y导入时,Y会成为当前模块的本地变量,后续代码调用的是这个本地变量,而非原模块X中的Y。只有Mock这个本地变量,才能覆盖代码中的实际调用。 - 之前的
patch("path.to.mod.run")仅修改了原模块中的run,但main.py已持有导入时的旧引用,因此无法影响foo中的调用。 - 直接
patch("run")无效,是因为patch要求传入完整的模块路径,不能使用局部变量名。
内容的提问来源于stack exchange,提问作者ealeon
相关产品推荐
相关产品推荐

