DeanWay / fastapi-versioning

api versioning for fastapi web applications
MIT License
644 stars 63 forks source link

dependency_overrides cannot be used in tests as expected, documentation lacking #19

Open languitar opened 4 years ago

languitar commented 4 years ago

Describe the bug

Without fastapi-versioning, dependency_overrides can easily be specified on the app contained in the TestClient a la (pytest fixture like pseudo code):

before = client.app.dependency_overrides
client.app.dependency_overrides = dict(overrides)
try:
    yield
finally:
    client.app.dependency_overrides = before

With the internal use of sub application mounts, this doesn't work anymore and the override never reaches one of the versioned sub applications.

To Reproduce Steps to reproduce the behavior:

from fastapi import APIRouter, Depends, FastAPI
from fastapi.testclient import TestClient
from fastapi_versioning import versioned_api_route, VersionedFastAPI

app = FastAPI(title="test-app")
router = APIRouter(route_class=versioned_api_route(1, 0))

def dependency() -> str:
    return "original"

@router.get("/test")
def get_test(dep: str = Depends(dependency)) -> str:
    return dep

app.include_router(router)
app = VersionedFastAPI(app)

def test_this() -> None:
    client = TestClient(app)
    client.app.dependency_overrides = {dependency: lambda: "patched"}
    assert client.get("/v1_0/test").json() == "patched"

This test will fail

Expected behavior

It should at least be documented how to access the correct sub application for providing the dependency_overrides.

Acerinth commented 3 years ago

Hello there...

Having the same issue here. Spent literally two days of trying to make tests work, to realize it's because of VersionedAPI... :( Anyone managed to find a workaround? It's basically impossible to attach a test database for testing purposes.

Thanks!

pshen-rp commented 3 years ago

I was running into the same issue and realized it's caused by the order of operations:

  1. You create a FastAPI app
  2. You add routes with dependencies
  3. You create a VersionedFastAPI from the original app
  4. VersionedFastAPI creates a whole new FastAPI app (the versioned app) and for each version number, it takes all the routes from your original app that match that version number and attaches them to a new sub-app mounted on the versioned app
  5. You get back the versioned app
  6. You make a request to the versioned app

The issue is when you create the routes in step 2, they are "compiled" and still reference the original FastAPI app from step 1 even when the dependencies are resolved when you make a request in step 6, so you have to add the dependency overrides to the original app:

from fastapi import APIRouter, Depends, FastAPI
from fastapi.testclient import TestClient
from fastapi_versioning import versioned_api_route, VersionedFastAPI

original_app = FastAPI(title="test-app")
router = APIRouter(route_class=versioned_api_route(1, 0))

def dependency() -> str:
    return "original"

@router.get("/test")
def get_test(dep: str = Depends(dependency)) -> str:
    return dep

original_app.include_router(router)
app = VersionedFastAPI(original_app)

def test_this() -> None:
    client = TestClient(app)
    original_app.dependency_overrides = {dependency: lambda: "patched"}
    assert client.get("/v1_0/test").json() == "patched"