测试阶段如何覆盖Pydantic的env_file配置?
问题
我通过Pydantic的BaseSettings在config.py中读取.prod.env的环境变量:
from pydantic import BaseSettings class Settings(BaseSettings): A: int class Config: env_file = ".prod.env" env_file_encoding = "utf-8" settings = Settings()
在main.py中创建FastAPI应用:
from fastapi import FastAPI from app.config import settings app = FastAPI() print(settings.A)
目前我能在conftest.py中通过直接赋值覆盖配置变量:
import pytest from fastapi.testclient import TestClient from app.main import app from app.config import settings settings.A = 42 @pytest.fixture(scope="module") def test_client(): with TestClient(app) as client: yield client
这种方式能正常运行,使用settings.A时会得到42。但我有两个疑问:
- 是否可以将整个
env_file从.prod.env覆盖为另一个环境文件.test.env? - 是否应该在
conftest.py中导入app之前调用settings.A = 42?
回答
1. 如何覆盖整个环境文件为.test.env
当然可以,有几种可行的实现方式:
方式一:重新实例化Settings类并指定env_file
在conftest.py中直接创建新的Settings实例,指定.test.env作为环境文件,替换原有单例对象:
import pytest from fastapi.testclient import TestClient from app.config import Settings # 加载.test.env并生成新的配置实例 settings = Settings(_env_file=".test.env", _env_file_encoding="utf-8") from app.main import app @pytest.fixture(scope="module") def test_client(): with TestClient(app) as client: yield client
注意:如果项目其他模块依赖app.config.settings这个单例,要确保替换操作在所有模块导入前完成,避免出现配置不一致的情况。
方式二:修改Settings.Config后重新加载
先修改Settings内部配置类的env_file属性,再重新初始化配置实例:
import pytest from fastapi.testclient import TestClient from app.config import settings, Settings # 更新环境文件路径 Settings.Config.env_file = ".test.env" # 重新加载配置 settings = Settings() from app.main import app @pytest.fixture(scope="module") def test_client(): with TestClient(app) as client: yield client
方式三:通过环境变量覆盖Pydantic配置
Pydantic支持用环境变量覆盖BaseSettings的配置项,运行pytest前设置SETTINGS_CONFIG_ENV_FILE=.test.env(变量名规则为{类名大写}_CONFIG_{配置项大写}),Pydantic会自动加载指定的环境文件。
2. 是否需要在导入app前修改settings.A
是的,必须在导入app之前修改settings.A。
原因是main.py在导入settings后会立刻执行print(settings.A),如果先导入app,main.py的代码已经执行完毕,此时修改settings.A只会影响后续代码,但main.py中打印的仍是原始值。
正确的写法是先修改配置,再导入app:
import pytest from fastapi.testclient import TestClient from app.config import settings # 先修改配置值 settings.A = 42 # 再导入app,确保main.py使用修改后的配置 from app.main import app @pytest.fixture(scope="module") def test_client(): with TestClient(app) as client: yield client
这样main.py中打印的settings.A就是修改后的42,后续所有依赖settings的代码也会使用这个值。
内容的提问来源于stack exchange,提问作者Ruuza

