如何阻止pytest缓存动态修改并重新导入的模块?
问题:pytest参数化测试中towerlib动态加载Credential类失败
我用pytest测试依赖towerlib的代码,这个库的towerlib.entities.credential.py模块会动态添加属性,Credential类通过重新导入自身来查找对应类型的Credential子类。在REPL里直接跑e2e.prepare_and_run_e2e能正常返回结果,但用@pytest.mark.parametrize装饰的测试函数会抛出异常:'Could not dynamically load credential with type',具体报错是AttributeError: module 'towerlib.entities.credential' has no attribute 'NetworkCredential'。试过用-p no:cacheprovider参数运行pytest,没用。因为是需要调用外部系统的测试,不想用patch/mock绕过Credential类,也改不了towerlib的实现,想找pytest相关配置解决。
相关代码片段
towerlib中的Credential类实现
class Credential: # pylint: disable=too-few-public-methods """Credential factory to handle the different credential types returned.""" def __new__(cls, tower_instance, data): try: credential_type_name = tower_instance.get_credential_type_by_id(data.get('credential_type')).name credential_type_name = ''.join(credential_type_name.split()) credential_type = f'{credential_type_name}Credential' credential_type_obj = getattr(importlib.import_module('towerlib.entities.credential'), credential_type) credential = credential_type_obj(tower_instance, data) except Exception: # pylint: disable=broad-except LOGGER.exception( 'Could not dynamically load credential with type : "%s", trying a generic one.', credential_type ) credential = GenericCredential(tower_instance, data) return credential
我的pytest测试函数
@pytest.mark.parametrize("device", get_all_e2e_devices()) def test_e2e(device): results = e2e.prepare_and_run_e2e([device], None, None, False, True, True, False, True) ...
报错信息
ERROR credentials:credential.py:189 Could not dynamically load credential with type : "NetworkCredential", trying a generic one. Traceback (most recent call last): File "{my local path}\.venv\lib\site-packages\towerlib\entities\credential.py", line 186, in __new__ credential_type_obj = getattr(importlib.import_module('towerlib.entities.credential'), credential_type) AttributeError: module 'towerlib.entities.credential' has no attribute 'NetworkCredential'
解决方案
1. 修改pytest的模块导入模式
pytest默认的importmode=prepend会导致模块重复导入时状态不一致,改成append模式可以让模块导入行为和REPL保持一致:
- 命令行执行:
pytest --importmode=append - 配置文件(
pytest.ini)设置:
[pytest] importmode = append
2. 用fixture替代直接参数化
把设备列表的生成逻辑移到fixture中,延迟参数初始化时机,确保towerlib模块先完成加载:
@pytest.fixture(params=get_all_e2e_devices()) def device(request): return request.param def test_e2e(device): results = e2e.prepare_and_run_e2e([device], None, None, False, True, True, False, True) ...
3. 预加载并强制刷新towerlib模块
在项目根目录的conftest.py中添加代码,提前加载并刷新credential模块,确保所有动态生成的子类都被注册:
# conftest.py import importlib import towerlib.entities.credential # 强制触发模块内所有Credential子类的定义 importlib.reload(towerlib.entities.credential)
内容的提问来源于stack exchange,提问作者pythko
相关产品推荐
相关产品推荐

