pytest中环境变量无法覆盖的问题求助
Flask Config单元测试中环境变量无法覆盖的问题
问题背景
我正在为基础Flask应用编写单元测试,目标是验证config.py中不同环境下数据库配置的正确性。当前遇到的问题是:TestConfigNotDev测试类中设置的CONFIG_MODE='not_development'无法生效,Config.ENVIRONMENT始终显示为development,但TestConfigDev测试能正常通过。
相关代码
config.py
from os import environ as env from dotenv import load_dotenv load_dotenv() class Config: ENVIRONMENT = env['CONFIG_MODE'] SQLALCHEMY_TRACK_MODIFICATIONS = True SECRET_KEY = env['SECRET_KEY'] # Heroku部署适配:Heroku的DATABASE_URL是postgres://,SQLALCHEMY需要postgresql:// if ENVIRONMENT == "development": SQLALCHEMY_DATABASE_URI = env['DEVELOPMENT_DATABASE_URL'] else: SQLALCHEMY_DATABASE_URI = env.get('DATABASE_URL').replace("://", "ql://", 1)
.env文件(已移除CONFIG_MODE)
DEVELOPMENT_DATABASE_URL = 'postgresql://usr:pwd@localhost:5432/db' FLASK_APP=app SECRET_KEY='XXX'
测试代码test_config.py
import os class TestConfigDev: development_database_url = 'development_database_url' os.environ['DEVELOPMENT_DATABASE_URL'] = development_database_url os.environ['CONFIG_MODE'] = 'development' from service_authentication.api.config import Config config = Config def test_sqlalchemy_track_modifications(self): """验证SQLALCHEMY_TRACK_MODIFICATIONS为True""" assert self.config.SQLALCHEMY_TRACK_MODIFICATIONS def test_sqlalchemy_database_uri_dev(self): """验证开发环境下数据库URI正确""" assert self.config.SQLALCHEMY_DATABASE_URI == self.development_database_url class TestConfigNotDev: database_url = 'postgres://database_url' os.environ['DATABASE_URL'] = database_url os.environ['CONFIG_MODE'] = 'not_development' from service_authentication.api.config import Config config = Config def test_sqlalchemy_database_uri_not_dev(self): """验证非开发环境下数据库URI被正确修改""" expected_url = self.database_url.replace("://", "ql://", 1) assert self.config.ENVIRONMENT == 'not_development' # 此处失败,ENVIRONMENT仍为development assert self.config.SQLALCHEMY_DATABASE_URI == expected_url
已尝试的解决方案
- 移除
.env中的CONFIG_MODE变量,仅在测试代码中设置 - 将两个测试类拆分到不同文件(
test_config_dev.py和test_config_not_dev.py) - 在测试类中使用
importlib.reload(os)尝试刷新环境变量
问题原因
核心问题在于Python模块的导入机制:
config.py在第一次被导入时(比如TestConfigDev运行时),会立即执行模块内的所有代码,包括Config类的定义。此时ENVIRONMENT和SQLALCHEMY_DATABASE_URI会被直接赋值并固定。- 后续测试类(如
TestConfigNotDev)再次导入Config时,Python会直接复用已经加载到内存中的模块,不会重新执行模块代码,因此类属性不会根据新的环境变量重新计算。 DEVELOPMENT_DATABASE_URL能正常覆盖是因为TestConfigDev中设置的变量在第一次导入时生效,而TestConfigNotDev并未使用该变量,不存在冲突。
解决办法
方案1:使用pytest Fixture + 模块重载
通过pytest的fixture在每个测试前重置环境变量,并重新加载config模块,确保每次测试都基于最新的环境变量计算类属性:
import os import importlib import pytest from service_authentication.api import config @pytest.fixture(autouse=True) def reset_env_and_reload_config(): # 保存原始环境变量 original_env = os.environ.copy() yield # 测试结束后恢复原始环境变量 os.environ.clear() os.environ.update(original_env) class TestConfigDev: def test_sqlalchemy_track_modifications(self): os.environ['CONFIG_MODE'] = 'development' os.environ['DEVELOPMENT_DATABASE_URL'] = 'development_database_url' importlib.reload(config) assert config.Config.SQLALCHEMY_TRACK_MODIFICATIONS def test_sqlalchemy_database_uri_dev(self): test_url = 'development_database_url' os.environ['CONFIG_MODE'] = 'development' os.environ['DEVELOPMENT_DATABASE_URL'] = test_url importlib.reload(config) assert config.Config.SQLALCHEMY_DATABASE_URI == test_url class TestConfigNotDev: def test_sqlalchemy_database_uri_not_dev(self): test_url = 'postgres://database_url' expected_url = test_url.replace("://", "ql://", 1) os.environ['CONFIG_MODE'] = 'not_development' os.environ['DATABASE_URL'] = test_url importlib.reload(config) assert config.Config.ENVIRONMENT == 'not_development' assert config.Config.SQLALCHEMY_DATABASE_URI == expected_url
方案2:修改Config类为延迟加载
将环境变量的读取逻辑从类定义阶段移到实例化阶段,或者使用类方法动态获取:
# 修改后的config.py from os import environ as env from dotenv import load_dotenv load_dotenv() class Config: SQLALCHEMY_TRACK_MODIFICATIONS = True SECRET_KEY = env['SECRET_KEY'] @classmethod def get_environment(cls): return env['CONFIG_MODE'] @classmethod def get_database_uri(cls): env_mode = cls.get_environment() if env_mode == "development": return env['DEVELOPMENT_DATABASE_URL'] else: return env.get('DATABASE_URL').replace("://", "ql://", 1)
这样测试时可以直接调用类方法,每次都会读取最新的环境变量。
内容的提问来源于stack exchange,提问作者741852963
相关产品推荐
相关产品推荐

