如何在不修改原代码的前提下Monkey Patch导入时执行的函数?
解决方案
核心思路是在包A被导入之前,先对包B中的函数c进行Monkey Patch,这样当A导入时执行的就是被替换后的dummy函数,而非原函数。以下是具体实现方式:
1. 先Patch B的c再导入A
直接在测试函数中调整顺序,先通过monkeypatch修改B模块的c,再导入A:
def test(monkeypatch): # 先patch包B中的c函数 monkeypatch.setattr("B.c", lambda: "dummy") # 此时再导入A,执行的是已patch后的c import A # 后续编写测试逻辑
2. 利用测试框架的前置Fixture确保提前Patch
如果使用pytest,可以定义一个自动执行的会话级Fixture,在所有测试开始前完成Patch并导入A:
import pytest from unittest.mock import patch @pytest.fixture(autouse=True, scope="session") def pre_patch_and_import(): with patch("B.c", return_value="dummy"): import A yield A
后续测试函数可直接使用该Fixture,确保A始终在Patch后被导入。
3. 清除模块缓存(解决模块已被提前导入的情况)
如果测试过程中A或B已被意外导入,可先清除sys.modules中的缓存,再重新Patch并导入:
import sys def test(monkeypatch): # 清除已导入的A和B模块缓存 for mod_name in ["A", "B"]: if mod_name in sys.modules: del sys.modules[mod_name] # 重新patch B的c monkeypatch.setattr("B.c", lambda: "dummy") # 再次导入A import A # 执行测试
原理说明
包A中from B import c的逻辑会在A被导入时执行:
- 若先Patch B的
c,A会引用并执行这个被替换后的函数; - 若先导入A再Patch A的
c,此时原c已经执行完毕,Patch仅替换A模块中c的引用,无法改变已经执行过的代码结果。
内容的提问来源于stack exchange,提问作者ChiPlusPlus
相关产品推荐
相关产品推荐

