如何在pytest中用monkeypatch覆盖DATABASE_URL环境变量?
解决pytest中monkeypatch覆盖DATABASE_URL无效的问题
问题根源
你的代码在模块导入阶段就直接读取环境变量并初始化了engine——测试代码运行前,目标模块已经被加载,DATABASE_URL和engine已经固定为导入时的旧值,后续monkeypatch环境变量无法改变已经初始化的变量。
解决方案
方案1:封装初始化逻辑,延迟加载(推荐)
修改原代码,将engine的初始化逻辑封装为函数,避免在模块顶层直接执行:
import os from sqlalchemy.ext.asyncio import create_async_engine def get_engine(): database_url = os.environ.get("DATABASE_URL") return create_async_engine(database_url, echo=False, future=True)
测试时,先monkeypatch环境变量,再调用get_engine()获取使用新变量的engine:
def test_database_engine(monkeypatch): monkeypatch.setenv("DATABASE_URL", "sqlite+aiosqlite:///:memory:") engine = get_engine() assert engine.url.render_as_string(hide_password=False) == "sqlite+aiosqlite:///:memory:"
方案2:测试中重新加载模块
如果无法修改原代码,可以在测试函数中先monkeypatch,再重载目标模块(假设模块名为db.py):
import importlib import pytest def test_patched_database(monkeypatch): # 覆盖环境变量 monkeypatch.setenv("DATABASE_URL", "sqlite+aiosqlite:///:memory:") # 重新加载模块,让模块重新读取环境变量 import db importlib.reload(db) # 验证engine使用了新的URL assert db.engine.url.render_as_string(hide_password=False) == "sqlite+aiosqlite:///:memory:"
注意:需确保测试前没有其他地方提前导入该模块,否则重载可能不生效。
方案3:用fixture提前monkeypatch并加载模块
通过fixture完成环境变量覆盖和模块重载,复用性更高:
import importlib import pytest @pytest.fixture def patched_db(monkeypatch): monkeypatch.setenv("DATABASE_URL", "sqlite+aiosqlite:///:memory:") import db importlib.reload(db) return db def test_engine_url(patched_db): assert patched_db.engine.url.render_as_string(hide_password=False) == "sqlite+aiosqlite:///:memory:"
内容的提问来源于stack exchange,提问作者tobias
相关产品推荐
相关产品推荐

