我正在从我的
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 中,我正在创建
app
,如下所示:
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_clinet():
with TestClient(app) as client:
yield client
这很好用,每当我使用
settings.A
时我都会得到42。
但是是否可以将整个
env_file
从.prod.env
覆盖到另一个环境文件.test.env
?
另外,我可能想在导入
settings.A = 42
之前在 conftest.py 中调用 app
,对吗?
您可以通过使用
Settings
关键字参数创建 _env_file
实例来覆盖您使用的 env 文件。
来自文档:
在实例化时通过
关键字参数传递文件路径(方法 2)将覆盖_env_file
类上设置的值(如果有)。如果结合使用上述代码片段,则会加载 prod.env,而 .env 将被忽略。Config
例如,这应该适用于您的测试 -
import pytest
from fastapi.testclient import TestClient
import app.config as conf
from app.config import Settings
# replace the settings object that you created in the module
conf.settings = Settings(_env_file='.test.env')
from app.main import app
# just to show you that you changed the module-level
# settings
from app.config import settings
@pytest.fixture(scope="module")
def test_client():
with TestClient(app) as client:
yield client
def test_settings():
print(conf.settings)
print(settings)
您可以创建一个
.test.env
,设置 A=10000000
,然后使用 运行
pytest -rP conftest.py
# stuff
----- Captured stdout call -----
A=10000000
A=10000000
这看起来有点混乱(尽管这可能只用于测试目的),所以我建议不要创建一个可以被代码中的所有内容导入的
settings
对象,而是让它成为你创建的东西,比如说,您的 __main__
实际创建并运行应用程序,但这是您需要做出的设计选择。
我今天也遇到了同样的问题,真的很烦人。我的目标是为单元测试设置不同的 postgresql 数据库。默认配置来自
.env
文件。但仔细想一想,也就不难理解了。这一切都归结为 conftest.py
中导入模块的顺序。我基于@wkl的答案来给出下面的例子:
import pytest
from typing import Generator
from fastapi.testclient import TestClient
import app.core.config as config
from app.core.config import Settings
# Replace individual attribute in the settings object
config.settings = Settings(
POSTGRES_DB="test_db")
# Or replace the env file in the settings object
config.settings = Settings(_env_file='.test.env')
# All other modules that import settings are imported here
# This ensures that those modules will use the updated settings object
# Don't forget to use "noqa", otherwise a formatter might put it back on top
from app.main import app # noqa
from app.db.session import SessionLocal # noqa
@pytest.fixture(scope="session")
def db() -> Generator:
try:
db = SessionLocal()
yield db
finally:
db.close()
@pytest.fixture(scope="module")
def client() -> Generator:
with TestClient(app) as c:
yield c
就今天而言,BaseSettings 已移至 pydantic-settings。
我今天遇到了同样的问题(pydantic-settings==2.2.1)。 对我有用的是:
Settings.model_config.update(env_file=".test.env")
Pydantic Settings 类读取 .env 文件中指定的环境变量以及当前会话环境中找到的环境变量。如果两者都存在一个值,它将选择从环境中读取。您可以利用这一事实来覆盖测试期间在 env 文件中找到的值。
如果您在
pytest_configure
中包含 /conftest.py
,您可以在进行任何导入之前运行代码。这允许您在初始化设置之前覆盖环境变量。环境变量的改变只影响python进程。
from pathlib import Path
from dotenv import load_dotenv
def pytest_configure(config):
# This is run before any imports allowing us to inject
# dependencies via environment variables into Settings
# This just affects the variables in this process's environment
# Find the .env file for the test environment
test_env = str(Path(__file__).parent / "test.env")
# Load the environment variables and overwrite any existing ones
load_dotenv(test_env, override=True)
我发现的一个解决方法是从 Config 中完全删除
env_file
并将其功能替换为 dotenv 中的 load_dotenv()
,如下所示:
config.py:
from pydantic import BaseSettings
from dotenv import load_dotenv
load_dotenv(".prod.env")
class Settings(BaseSettings):
A: int
settings = Settings()
conftest.py:
import pytest
from fastapi.testclient import TestClient
from dotenv import load_dotenv
load_dotenv("test.env")
from app.config import settings
from app.main import app
@pytest.fixture(scope="module")
def test_clinet():
with TestClient(app) as client:
yield client
请注意,调用
load_dotenv("test.env")
发生在导入设置之前 (from app.config import settings
)
还要注意 load_dotenv() 将为整个 python 脚本全局加载环境变量。
像这样加载环境变量不会覆盖已经导出的变量,与在 pydantic 的 BaseSettings 中使用
env_file
相同