[英]Test Pydantic settings in FastAPI
Suppose my main.py
is like this (this is a simplified example, in my app I use an actual database and I have two different database URIs for development and testing):假设我的
main.py
是这样的(这是一个简化的示例,在我的应用程序中我使用了一个实际的数据库,并且我有两个不同的数据库 URI 用于开发和测试):
from fastapi import FastAPI
from pydantic import BaseSettings
app = FastAPI()
class Settings(BaseSettings):
ENVIRONMENT: str
class Config:
env_file = ".env"
case_sensitive = True
settings = Settings()
databases = {
"dev": "Development",
"test": "Testing"
}
database = databases[settings.ENVIRONMENT]
@app.get("/")
def read_root():
return {"Environment": database}
while the .env
is而
.env
是
ENVIRONMENT=dev
Suppose I want to test my code and I want to set ENVIRONMENT=test
to use a testing database.假设我想测试我的代码并且我想设置
ENVIRONMENT=test
以使用测试数据库。 What should I do?我应该怎么办? In FastAPI documentation ( https://fastapi.tiangolo.com/advanced/settings/#settings-and-testing ) there is a good example but it is about dependencies, so it is a different case as far as I know.
在 FastAPI 文档( https://fastapi.tiangolo.com/advanced/settings/#settings-and-testing )中有一个很好的例子,但它是关于依赖关系的,所以据我所知,情况不同。
My idea was the following ( test.py
):我的想法如下(
test.py
):
import pytest
from fastapi.testclient import TestClient
from main import app
@pytest.fixture(scope="session", autouse=True)
def test_config(monkeypatch):
monkeypatch.setenv("ENVIRONMENT", "test")
@pytest.fixture(scope="session")
def client():
return TestClient(app)
def test_root(client):
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"Environment": "Testing"}
but it doesn't work.但它不起作用。
Furthermore I get this error:此外,我收到此错误:
ScopeMismatch: You tried to access the 'function' scoped fixture 'monkeypatch' with a 'session' scoped request object, involved factories
test.py:7: def test_config(monkeypatch)
env\lib\site-packages\_pytest\monkeypatch.py:16: def monkeypatch()
while from pytest
official documentation it should work ( https://docs.pytest.org/en/3.0.1/monkeypatch.html#example-setting-an-environment-variable-for-the-test-session ).而从
pytest
官方文档中它应该可以工作( https://docs.pytest.org/en/3.0.1/monkeypatch-for#the-setting--example.environment ) I have the latest version of pytest
installed.我安装了最新版本的
pytest
。
I tried to use specific test environment variables because of this: https://pydantic-docs.helpmanual.io/usage/settings/#field-value-priority .因此,我尝试使用特定的测试环境变量: https://pydantic-docs.helpmanual.io/usage/settings/#field-value-priority 。
To be honest I'm lost, my only real aim is to have a different test configuration (in the same way Flask works: https://flask.palletsprojects.com/en/1.1.x/tutorial/tests/#setup-and-fixtures ).老实说,我迷路了,我唯一真正的目标是拥有不同的测试配置(与 Flask 的工作方式相同: https://flask.palletsprojects.com/en/1.1.x/tutorial/tests/#setup-和固定装置)。 Am I approaching the problem the wrong way?
我是否以错误的方式解决问题?
PydanticSettings
are mutable, so you can simply override them in your test.py
: PydanticSettings
是可变的,因此您可以在test.py
中简单地覆盖它们:
from main import settings
settings.ENVIRONMENT = 'test'
This is a simple way that works for me.这是一种对我有用的简单方法。 Consider that you have a configuration file named APPNAME.cfg with the following settings:
假设您有一个名为 APPNAME.cfg 的配置文件,其中包含以下设置:
DEV_DSN='DSN=my_dev_dsn; UID=my_dev_user_id; PWD=my_dev_password'
PROD_DSN='DSN=my_prod_dsn; UID=my_prod_user_id; PWD=my_prod_password'
Set your environment according to your OS or Docker variable.根据您的操作系统或 Docker 变量设置您的环境。 For Linux you could enter:
对于 Linux,您可以输入:
export MY_ENVIORONMENT=DEV
Now consider the following settings.py:现在考虑以下 settings.py:
from pydantic import BaseSettings
import os
class Settings(BaseSettings):
DSN: str
class Config():
env_prefix = f"{os.environ['MY_ENVIORONMENT']}_"
env_file = "APPNAME.cfg"
Your app would simply need to do the following:您的应用只需要执行以下操作:
from settings import Settings
s = Settings()
db = pyodbc.connect(s.DSN)
It's really tricky to mock environment with pydantic involved.模拟涉及 pydantic 的环境真的很棘手。
I only achieved desired behaviour with dependency injection in fastapi and making get_settings
function, which itself seems to be good practice since even documentation says to do so.我只在 fastapi 中通过依赖注入实现了所需的行为,并制作了
get_settings
function,这本身似乎是一个很好的做法,因为甚至文档都说这样做。
Suppose you have假设你有
...
class Settings(BaseSettings):
ENVIRONMENT: str
class Config:
env_file = ".env"
case_sensitive = True
def get_settings() -> Settings:
return Settings()
databases = {
"dev": "Development",
"test": "Testing"
}
database = databases[get_settings().ENVIRONMENT]
@app.get("/")
def read_root():
return {"Environment": database}
And in your tests you would write:在你的测试中你会写:
import pytest
from main import get_settings
def get_settings_override() -> Settings:
return Settings(ENVIRONMENT="dev")
@pytest.fixture(autouse=True)
def override_settings() -> None:
app.dependency_overrides[get_settings] = get_settings_override
You can use scope session if you'd like.如果您愿意,可以使用 scope session。
This would override your ENVIRONMENT
variable and wouldn't touch rest of configuration variables.这将覆盖您的
ENVIRONMENT
变量,并且不会触及配置变量的 rest。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.