Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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):

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

ENVIRONMENT=dev

Suppose I want to test my code and I want to set ENVIRONMENT=test to use a testing database. 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.

My idea was the following (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). I have the latest version of pytest installed.

I tried to use specific test environment variables because of this: 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). Am I approaching the problem the wrong way?

like image 319
Matteo Silvestro Avatar asked May 03 '20 21:05

Matteo Silvestro


2 Answers

PydanticSettings are mutable, so you can simply override them in your test.py:

from main import settings

settings.ENVIRONMENT = 'test'
like image 71
ErhoSen Avatar answered Sep 19 '22 17:09

ErhoSen


This is a simple way that works for me. Consider that you have a configuration file named APPNAME.cfg with the following settings:

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. For Linux you could enter:

export MY_ENVIORONMENT=DEV

Now consider the following 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)
like image 30
David W. Avatar answered Sep 22 '22 17:09

David W.