app.dependency_overrides not working in test

178 Views Asked by At

I have a project that uses FastApi and Pydantic-settings. While the injection of settings works fine i am not able to override them in test.

My settings look like this:

class LevelDBSettings(BaseSettings):
    """
    Settings for the levelDB database.
    """

    fetch_url: str
    """URL to fetch data from levelDB"""

    read_token: str
    """Token to authenticate against levelDB"""

    model_config = SettingsConfigDict(env_file="dev_default.env", env_file_encoding="utf-8")

    def __init__(self):
        super().__init__(self.model_config)

def get_level_db_settings():
    """provide level db settings application wide. injectable via dependency injection"""
    return LevelDBSettings()

Injecting them also works fine:

def get_data_from_db(msn: str, start: datetime, end: datetime):
    request = FetchRequest(start, end, msn)
    settings = Annotated[LevelDBSettings, Depends(get_level_db_settings)]()
    # inject settings

As you see i put the method to get setings in settings class for 2 reasons:

  • it just belongs here (my opinion)
  • if put in file with app = FastAPI() i get circular dependency errors. because app imports routers that call methods that inject settings from fast api - circle

First question: is this a general problem? Can i only use "dependency_overrides" for methods in app? (How to inject arbitrary things and override them in tests?)

My test is this (separate path /tests/integration)

def get_test_level_db_settings():
    settings = LevelDBSettings()
    settings.fetch_url = "http://localhost:8080/api/v0/fetch"
    settings.read_token = "sometest"
    return settings

def test_fetch_data():

    app.dependency_overrides[get_level_db_settings] = get_test_level_db_settings
    ...

This test fails due to missing settings in pydantic-settings (it can not load env file because test runs in different folder)

How to get this working?

2

There are 2 best solutions below

4
DurandA On

app.dependency_overrides works only for replacing injected dependencies, i.e. Depends or Annotated parameters of your routes.

If your function/callable is not injected, you should use a different technique to "patch" it. I personally use patch from unittest.mock when I cannot use app.dependency_overrides.

Note that you didn't provide an explicit example on how your settings are used in your routes. Maybe we can help you refactor your example to inject the settings as a dependency.

0
Yurii Motov On
def get_data_from_db(msn: str, start: datetime, end: datetime):
    request = FetchRequest(start, end, msn)
    settings = Annotated[LevelDBSettings, Depends(get_level_db_settings)]()
    # inject settings

By using Depends inside function's body (not in the parameters of function) you lose some advantages of FastAPI dependencies, and one of them is dependency_override.

Try to move Depends to the path function parameters:

def get_data_from_db(
    msn: str, start: datetime, end: datetime,
    settings = Annotated[LevelDBSettings, Depends(get_level_db_settings)]
):
    ...