You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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模块的导入机制:

  1. config.py在第一次被导入时(比如TestConfigDev运行时),会立即执行模块内的所有代码,包括Config类的定义。此时ENVIRONMENT和SQLALCHEMY_DATABASE_URI会被直接赋值并固定。
  2. 后续测试类(如TestConfigNotDev)再次导入Config时,Python会直接复用已经加载到内存中的模块,不会重新执行模块代码,因此类属性不会根据新的环境变量重新计算。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 22:10:09