pytest如何mock列表内引用的其他模块函数并验证调用状态
失效原因
Python 模块为单例模式,fun_list.py 首次被导入时会立即执行FUN_LIST的初始化逻辑,直接将other_module.funA、other_module.funB的内存引用存入列表。你测试时Patch的是other_module.funB这个名称的指向,只会影响Patch生效后新访问other_module.funB的取值,无法修改已经存入FUN_LIST的旧引用,因此调用时走的还是真实函数逻辑。
解决方案
方案1:不修改原有业务代码,直接替换列表中的函数引用
直接修改fun_list中已初始化的FUN_LIST,测试完成后恢复原引用避免影响其他用例:
from unittest.mock import Mock from demo import run_demo from fun_list import FUN_LIST def test_demo_funs(): # 保存原函数引用用于测试后恢复 original_funB = FUN_LIST[1] mocked_funB = Mock(return_value={}) # 替换列表中的funB FUN_LIST[1] = mocked_funB try: run_demo() # 验证至少被调用一次 assert mocked_funB.call_count >= 1 finally: # 恢复原引用,避免污染其他测试用例 FUN_LIST[1] = original_funB
方案2:调整业务代码,适配通用Mock逻辑
如果希望使用常规的@patch写法,可以修改fun_list.py的实现,让函数引用在调用时才动态获取:
调整后fun_list.py示例1:函数调用时动态构造列表
import other_module def run_funs(): # 每次调用时才初始化列表,会读取最新的other_module属性 FUN_LIST = [ other_module.funA, other_module.funB, ] for fun in FUN_LIST: fun()
调整后fun_list.py示例2:用lambda延迟执行
import other_module FUN_LIST = [ lambda: other_module.funA(), lambda: other_module.funB(), ] def run_funs(): for fun in FUN_LIST: fun()
调整后你原来的测试代码即可正常生效,不需要修改测试逻辑。
内容的提问来源于stack exchange,提问作者Mr_Somebody
相关产品推荐
相关产品推荐

