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

基于BaseSettings的类遇.env时忽略指定环境文件?FastAPI测试问题

问题根源

核心原因有两个:

  1. Pydantic环境变量优先级:BaseSettings读取配置的优先级是「系统环境变量 > .env文件 > 显式参数」。pipenv启动时会自动将根目录.env中的变量加载到系统环境变量中,所以即使你在测试时指定test.env,系统中已存在的.env变量会被优先读取,覆盖test.env的内容。
  2. lru_cache的潜在影响:原get_settings函数用了lru_cache装饰器,如果在依赖覆盖前该函数被调用过(比如项目启动时提前初始化了配置),会缓存旧的Settings实例,导致后续依赖覆盖不生效。你提到手动修改变量能识别,说明依赖覆盖后缓存被刷新,但初始化阶段仍受系统环境变量影响。
解决方案

方案一:清除系统环境变量后加载test.env

直接在测试配置函数中清除从.env加载的系统环境变量,确保Pydantic优先读取test.env:

from config import Settings, get_settings
from main import app
from fastapi.testclient import TestClient
from pytest import fixture
import os

def get_test_settings() -> Settings:
    # 清除与配置相关的系统环境变量,避免优先级覆盖
    for env_key in ["VERSION", "DB_CONNECTION_STRING", "DB_NAME"]:
        if env_key in os.environ:
            del os.environ[env_key]
    # 加载test.env
    return Settings(_env_file='test.env', _env_file_encoding='utf-8')

@fixture(scope='session', autouse=True)
def test_client():
    # 清除get_settings的缓存,确保新配置生效
    get_settings.cache_clear()
    app.dependency_overrides[get_settings] = get_test_settings
    return TestClient(app)

方案二:通过环境变量动态指定env文件

修改Settings类,支持通过系统环境变量切换加载的env文件,更灵活且无需额外清除变量:

修改config/settings.py

from pydantic import BaseSettings, MongoDsn
from functools import lru_cache

class Settings(BaseSettings):
    version: str
    db_connection_string: MongoDsn
    db_name: str

    class Config:
        # 通过ENV_FILE环境变量指定加载的文件,默认加载.env
        env_file = "${ENV_FILE:.env}"
        env_file_encoding = 'utf-8'

@lru_cache()
def get_settings() -> Settings:
    return Settings()

修改conftest.py

from config import get_settings
from main import app
from fastapi.testclient import TestClient
from pytest import fixture
import os

@fixture(scope='session', autouse=True)
def test_client():
    # 设置环境变量,指定加载test.env
    os.environ["ENV_FILE"] = "test.env"
    # 清除缓存,让get_settings重新读取配置
    get_settings.cache_clear()
    # 这里无需额外定义get_test_settings,直接复用原函数即可
    app.dependency_overrides[get_settings] = get_settings
    return TestClient(app)

方案三:强制仅从指定文件读取配置

通过自定义配置源,让Settings完全忽略系统环境变量,只加载test.env:

from config import Settings, get_settings
from main import app
from fastapi.testclient import TestClient
from pytest import fixture
from pydantic import BaseSettings
from pydantic.env_settings import dotenv_settings_source

def test_env_source(settings: BaseSettings) -> dict:
    # 仅加载test.env,不读取任何系统环境变量
    return dotenv_settings_source(settings, _env_file='test.env')

def get_test_settings() -> Settings:
    # 替换Settings的配置源
    Settings.__config__.settings_customise_sources = (
        test_env_source,
    )
    return Settings()

@fixture(scope='session', autouse=True)
def test_client():
    get_settings.cache_clear()
    app.dependency_overrides[get_settings] = get_test_settings
    return TestClient(app)
验证方法

在测试用例中添加打印语句,确认配置是否正确加载:

def test_settings(test_client):
    from config import get_settings
    settings = get_settings()
    print(settings.dict())
    # 断言配置值与test.env一致
    assert settings.db_name == "test_db"  # 替换为test.env中的实际值

内容的提问来源于stack exchange,提问作者Magnaillusion

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:35:23