pytest使用mock patch拦截__init__失效 无法避免真实数据库连接
问题原因
你的mock逻辑不生效,核心是补丁替换时机晚于模块内实例化逻辑的执行时间:
当你在patch上下文内执行导入操作时,Python加载src.store模块的执行顺序是:
- 先为
src.store创建空模块对象注册到sys.modules - 逐行运行
store.py内的代码:先完成Store类定义、绑定原始的__init__方法,紧接着就会执行模块底部的Store()实例化语句,直接触发真实的数据库连接 - 直到整个
store.py代码全部执行完毕,patch才能拿到完整的src.store.Store类对象替换__init__方法,这时候实例化逻辑早就跑完了,补丁完全没赶上执行。
另外额外提一句:你业务代码里__init__方法的参数用了pass,这是Python保留关键字,实际运行会直接抛语法错误,建议改成password这类合法命名。
解决方案
方案1(推荐):消除导入阶段的副作用
这是最符合Python工程规范的解法:模块导入阶段不应该产生IO连接、全局实例化这类副作用,把顶层实例化逻辑放到脚本入口判断里即可。
修改project/src/store.py末尾代码:
# 只有直接运行当前脚本时才会执行实例化,导入模块时不会触发 if __name__ == "__main__": Store()
改完之后测试代码不需要额外做复杂mock,直接导入Store类即可测试静态方法,不会触发真实数据库连接。
方案2:不修改业务代码的前提下提前mock依赖
如果因为业务限制不能调整源码,可以在导入模块前,提前把触发连接的核心依赖mock掉,比直接mock__init__更稳定:
测试代码参考如下:
from unittest import mock import sys # 先给目标模块注册mock占位,避免导入时直接执行源码 sys.modules["src.store"] = mock.MagicMock() with mock.patch("src.store.connection") as mock_connection: # 移除占位,真正执行模块导入 del sys.modules["src.store"] from src.store import Store # 此时模块内执行Store()时调用的是mock后的connection,不会建立真实连接 # 直接写测试用例即可 def test_get_id(): # 测试静态方法逻辑 assert Store.get_id() == 预期结果
如果你确实需要替换__init__逻辑,只要把上面代码里patch的目标从src.store.connection换成src.store.Store.__init__即可,核心是要提前在导入前把补丁挂上,而不是等导入过程中再替换。
内容的提问来源于stack exchange,提问作者Xhark
相关产品推荐
相关产品推荐

