Best way to override FastAPI dependencies for testing with a different dependency for each test

Solution 1:

This is what I use myself to override dependencies in my tests. You have to use it as a context manager with a with statement (See examples below). Upon entering the context it saves existing overrides it encounters. These will be restored when exiting the context.

import typing
from fastapi import FastAPI


class DependencyOverrider:
    def __init__(
        self, app: FastAPI, overrides: typing.Mapping[typing.Callable, typing.Callable]
    ) -> None:
        self.overrides = overrides
        self._app = app
        self._old_overrides = {}

    def __enter__(self):
        for dep, new_dep in self.overrides.items():
            if dep in self._app.dependency_overrides:
                # Save existing overrides
                self._old_overrides[dep] = self._app.dependency_overrides[dep]
            self._app.dependency_overrides[dep] = new_dep
        return self

    def __exit__(self, *args: typing.Any) -> None:
        for dep in self.overrides.keys():
            if dep in self._old_overrides:
                # Restore previous overrides
                self._app.dependency_overrides[dep] = self._old_overrides.pop(dep)
            else:
                # Just delete the entry
                del self._app.dependency_overrides[dep]

You can use it like this in your tests:

from app.main import app
from fastapi.testclient import TestClient


def test_override_in_items(client: TestClient):
    with DependencyOverrider(app, overrides={common_parameters: override_dependency}):
        response = client.get("/items/")
        assert response.status_code == 200
        assert response.json() == {
            "message": "Hello Items!",
            "params": {"q": None, "skip": 5, "limit": 10},
        }

Or one could use it as a fixture in pytest:


@pytest.fixture(scope="function")
def faked_common_parameters():
    with DependencyOverrider(
        app, overrides={common_parameters: override_dependency}
    ) as overrider:
        yield overrider


def test_override_in_items(client: TestClient, faked_common_parameters):
    # Running with the overridden dependency 
    response = client.get("/items/")
    assert response.status_code == 200
    assert response.json() == {
        "message": "Hello Items!",
        "params": {"q": None, "skip": 5, "limit": 10},
    }