7

I'm using Pydantic settings management in a FastAPI-based project. I have a Settings class like this one:

class Settings(BaseSettings):
    FOO: str = ''
    BAR: int = 0

    class Config:
        env_file = "path/to/.my_env_file")
        env_nested_delimiter = "__"

The file path/to/.my_env_file contains FOO and BAR values.

During tests, I need to selectively patch Settings, and I do not want to read anything from path/to/.my_env_file. E.g.,

path/to/.my_env_file

FOO=i_do_not_wanna_read_this
BAR=100

my test file:

@lru_cache()
def get_settings():
    return Settings()

def get_settings_override() -> Settings:
    return Settings(
        FOO = 'foo'
    )

app.dependency_overrides[get_settings] = get_settings_override

I want to run tests with FOO='foo' and with the default value of BAR (i.e., BAR=0, ignoring the content of path/to/.my_env_file. In the code above, I get FOO='foo' but BAR is still read from path/to/.my_env_file (i.e., BAR=100)

Is there a straightforward way to handle that?

floatingpurr
  • 7,749
  • 9
  • 46
  • 106

2 Answers2

1

While I couldn't find a straightforward solution in the docs, or any other page, this worked for my tests:

When using tox, put this into your tox.ini, as per this Stack Overflow question:

[testenv]
setenv = TOX_TESTENV = true

You can then simply use the following snippet to override your env_file settings:

import os

# ... snip ...

if os.environ.get("TOX_TESTENV") is not None:
    Settings.Config.env_file = ""           # Pydantic 1.x
    Settings.model_config["env_file"] = ""  # Pydantic 2.x

Similar approaches, for example by checking sys.argv for the existence of "test" or checking if unittest is loaded, should work fine too:

import sys

# ... snip ...

if len(sys.argv) > 1 and "pytest" in sys.argv[0]:
    Settings.Config.env_file = ""           # Pydantic 1.x
    Settings.model_config["env_file"] = ""  # Pydantic 2.x

Alternatively, here's a solution by @hoefling, utilizing pytest hooks:

def pytest_configure():
    Settings.Config.env_file = ""           # Pydantic 1.x
    Settings.model_config["env_file"] = ""  # Pydantic 2.x
nvs
  • 11
  • 3
  • Awesome, a custom hook `def pytest_configure(): Settings.Config.env_file = ''` did it for my test suite. Thank you for sharing! – hoefling Feb 16 '23 at 15:39
0

You could use monkeypatch in a pytest fixture:

import pytest

from app.settings import Settings


@pytest.fixture(autouse=True, scope="function")
def patch_settings_env_file_location(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(Settings.Config, "env_file", "")

In this particular case, I've defined the patch_settings_env_file_location in the conftest.py located at the root folder of my tests.

doublethink13
  • 633
  • 8
  • 19