如何一次性Mock/Patch模块所有函数或替换整个模块?
一次性替换模块所有函数的几种实用方案
方案1:自定义上下文管理器批量Patch
自己写个上下文管理器,自动遍历假模块的函数,批量完成Patch,用完自动恢复原模块:
# main.py import fancy_module import fake_module from unittest.mock import patch def patch_module(source_module, target_module): # 收集需要替换的可调用属性(排除模块内置的下划线开头属性) patches = [] for name in dir(target_module): attr = getattr(target_module, name) if callable(attr) and not name.startswith('_'): patches.append(patch(f'{source_module.__name__}.{name}', new=attr)) # 批量启动所有Patch return patch.multiple(*patches) # 原函数调用 fancy_module.foo() # 批量替换后的调用 with patch_module(fancy_module, fake_module): fancy_module.foo() fancy_module.bar() fancy_module.baz() # 退出上下文自动恢复原函数 fancy_module.baz()
方案2:直接替换模块命名空间(简单粗暴)
如果不需要保留原模块的非函数属性,可以直接更新原模块的__dict__,配合上下文管理器实现自动恢复:
import fancy_module import fake_module from contextlib import contextmanager @contextmanager def replace_module(source_module, target_module): # 备份原模块所有属性 original_attrs = source_module.__dict__.copy() try: # 用假模块的属性覆盖原模块 source_module.__dict__.update(target_module.__dict__) yield finally: # 恢复原模块属性 source_module.__dict__.clear() source_module.__dict__.update(original_attrs) # 使用方式 with replace_module(fancy_module, fake_module): fancy_module.bar() # 调用假函数 # 退出后自动恢复 fancy_module.bar() # 调用原函数
方案3:全局替换模块(适合测试环境)
在测试脚本最开头修改sys.modules,让后续所有import fancy_module直接指向假模块,全局生效:
import sys import fake_module # 替换系统模块注册表 sys.modules['fancy_module'] = fake_module # 之后导入的fancy_module就是假模块 import fancy_module fancy_module.foo() # 调用假函数
注意:这个方法是全局替换,一旦执行,整个程序生命周期内fancy_module都会是假模块,适合测试场景一次性设置。
内容的提问来源于stack exchange,提问作者Thomas Anderson
相关产品推荐
相关产品推荐

