Python如何通过修改sys.modules模拟`module.module.Class`类路径实现单元测试?
问题根因
你当前报错的核心原因是__import__导入多层级子模块的逻辑不符合预期:默认调用__import__('a.b.c')时返回的是顶层的a模块,而非你需要的c子模块。你代码中赋值给sys.modules['firebase_admin.messaging']的对象实际是顶层的tests模块,自然找不到你定义的Aps类。
修复方案
方案1:修正现有手动替换模块的写法
仅需调整tests.py中导入mock模块的逻辑,给__import__增加fromlist参数即可正确获取到messaging子模块:
import sys # 正确获取mock的firebase_admin根模块 firebase_admin_module = __import__('tests.mock_firebase_admin', fromlist=['']) sys.modules['firebase_admin'] = firebase_admin_module # 正确获取mock的messaging子模块 messaging = __import__('tests.mock_firebase_admin.messaging', fromlist=['']) sys.modules['firebase_admin.messaging'] = messaging setattr(firebase_admin_module, 'messaging', messaging)
修改后业务代码中调用firebase_admin.messaging.Aps()就会正常触发你自定义的mock类构造逻辑。
方案2:使用unittest.mock简化实现(更推荐)
不需要手动维护sys.modules的替换逻辑,直接通过patch装饰器/上下文管理器替换目标模块的依赖即可,示例如下:
from unittest import mock from tests.mock_firebase_admin import messaging as mock_messaging # 直接替换业务代码中引用的firebase_admin.messaging模块 @mock.patch('app.cannot_edit_this_file.firebase_admin.messaging', new=mock_messaging) def test_your_business_logic(): # 此处执行业务代码,Aps会自动使用你定义的mock类 ...
注意事项
避免可变默认参数坑:你当前mock的Aps类中custom_data={}属于可变默认参数,多次实例化时会复用同一个字典对象,建议调整为custom_data=None,在__init__内部初始化空字典:
class Aps(): def __init__(self, alert=None, content_available=True, sound="default", custom_data=None): custom_data = custom_data if custom_data is not None else {} print("Hello from mock Aps constructor!")
内容的提问来源于stack exchange,提问作者Andy Mac
相关产品推荐
相关产品推荐

