基于BaseSettings的类遇.env时忽略指定环境文件?FastAPI测试问题
问题根源
核心原因有两个:
- Pydantic环境变量优先级:BaseSettings读取配置的优先级是「系统环境变量 > .env文件 > 显式参数」。pipenv启动时会自动将根目录
.env中的变量加载到系统环境变量中,所以即使你在测试时指定test.env,系统中已存在的.env变量会被优先读取,覆盖test.env的内容。 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
相关产品推荐
相关产品推荐

