You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何一次性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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 22:09:24