I'm not sure whether I'm just doing something wrong or if this is a bug. I'm on version 0.42.0. I'm trying to use dependency_overrides to override a function for testing, but I can't get it to work. Here's a basic example.
routers/some_router.py
from typing import List
from fastapi import APIRouter
router = APIRouter()
def some_func():
return ['foo']
@router.get('/some-endpoint', response_model=List[str])
def some_endpoint() -> List[str]:
return some_func()
test_some_router.py
from fastapi import FastAPI
from starlette.testclient import TestClient
from routers import some_router
app = FastAPI()
app.include_router(some_router.router)
client = TestClient(app)
def some_func_new():
return ['foo', 'bar', 'baz', 'qux']
app.dependency_overrides[some_router.some_func] = some_func_new
def test_some_endpoint():
response = client.get('/some-endpoint')
assert response.status_code == 200
assert response.json() == ['foo', 'bar', 'baz', 'qux']
And the result:
===================== FAILURES =====================
________________ test_some_endpoint ________________
def test_some_endpoint():
response = client.get('/some-endpoint')
assert response.status_code == 200
> assert response.json() == ['foo', 'bar', 'baz', 'qux']
E AssertionError: assert ['foo'] == ['foo', 'bar', 'baz', 'qux']
I'm not sure if I should be importing things or setting them up in a different way. I've also tried from main import app, some_router as well as directly importing some_func. If I'm doing something wrong, I think the documentation could be made clearer (e.g. a two file example instead of having both the API and tests in the same file).
You have to put dependencies in the signature, and the value gets plugged into the request endpoint by fastapi. (This is the only way fastapi can make use of dependencies.)
So, you'd need to write this as:
@router.get('/some-endpoint', response_model=List[str])
def some_endpoint(some_func_result = Depends(some_func)) -> List[str]:
return some_func_result
I tested it with your code and your test passes.
That makes sense. It's unfortunate that you have to make a change to the code for the sole purpose of testing it though.
It seems much preferable to just directly override functions if you're not already using Depends:
app = FastAPI()
def some_func_new():
return ['foo', 'bar', 'baz', 'qux']
some_router.some_func = some_func_new
app.include_router(some_router.router)
client = TestClient(app)
Yeah, in this case you can use all the usual approaches to monkeypatching for tests (unittest.mock, the pytest monkeypatch fixture, etc.).
Thanks for the help here @dmontagu ! :clap: :bow:
Thanks for reporting back and closing the issue @noctuid :+1:
Sorry to resurrect this, but I have a related question.
I need to override the dependencies in a dependencies list in a router endpoint.
I am trying something like this in my tests:
from app.routers import my_router
...
def override_permission_required():
return None
@pytest.fixture
def client():
app = FastAPI()
app.include_router(my_router.router())
client = TestClient(app)
app.dependency_overrides[my_router.permission_required] = override_permission_required
yield client
and this is the actual router:
...
from app.services.security import permission_required
resource = "my-resource"
def router():
api_router = APIRouter()
@api_router.get(
"/my_route",
dependencies=[Depends(permission_required(resource, Operation.READ))],
)
def get_items():
return ["foo", "bar"]
However, in the test that tries to client.get("/my_route"), it calls the original permission_required function. What am I missing?
Thanks in advance!
You are calling permission_required in your code and not in the test?
You are calling permission_required in your code and not in the test?
permission_required is fired in the test within client.get
This is roughly the test that I have:
def test_my_route(client):
response = client.get("/my_route")
The response returned is 401 "Unauthorized", raised by the permission_required function, instead of ["foo", "bar"]. Basically I just want to mock/skip the dependency function altogether.
Have another look at your code. In one place, Depends(permission_required()), in the other, app.dependency_overrides[permission_required]. These need to be the same
Have another look at your code. In one place, Depends(permission_required()), in the other, app.dependency_overrides[permission_required]. These need to be the same
Not sure if you meant because in the route dependency I have permission_required(), but I just omitted the function parameters in this example, sorry if that caused confusion. In reality it is dependencies=[Depends(permission_required(resource, Operation.READ))], I assume it not to be relevant here, but will edit it above.
In case you meant changing the conftest to:
from app.routers import my_router
from app.services.security import permission_required
...
def override_permission_required():
return None
@pytest.fixture
def client():
app = FastAPI()
app.include_router(my_router.router())
client = TestClient(app)
app.dependency_overrides[permission_required] = override_permission_required
yield client
I have also tried that, and the result is the same. When I ran into this question here, then it occurred to me to set it in the router (as the original code I posted).
I assume I'm not setting the dependency in the correct way. So, my question is: how do I set it correctly?
@larissaleite It looks like FastAPI can't override the dependency due to not understanding what you want to override.
I've done some debugging into that problem. Please take a look at this snippet:
from fastapi import FastAPI, HTTPException, Depends
from fastapi.testclient import TestClient
app = FastAPI()
def permission(resource: str):
def dependency():
if resource != "read":
raise HTTPException(status_code=403)
return dependency
@app.get("/", dependencies=[Depends(permission("read"))])
def home():
return "Hello world"
def override_permission():
1 / 0
app.dependency_overrides[permission] = override_permission
client = TestClient(app)
def test_dependency():
print(permission.__name__) # it will print 'permission'
res = client.get("/")
assert res.json() == "Hello world"
assert False # Only to see the 'permission' print
As your example, the dependency permission is not overridden.
Using a debugger and marking some of FastAPI code (look at dependencies/utils.py:solve_dependencies) you can find this:

And if you look closely at the variables data on the debugger, you'll find that:

I'm too tired to debug further, but if you remove the parameter 'resource', the dependency is overridden. So it looks like the problem is around there. :detective:
Maybe is a FastAPI issue, maybe it was on purpose or I may be missing something (if the first we probably should dig further). For the moment I don't have a solution, maybe if you post your permission_required function someone can help you with an alternative... :disappointed:
Edit: I've read what I said again, and the name attribute affirmation doesn't help a lot, but if you debug further on that function you'll find something.
Thanks @Kludex for going deeper into this, I appreciate it!
It'd be indeed good to understand whether this is a limitation of FastAPI or if there's a possible way around it. In the mean time, I will check whether I can refactor my function to not take any parameters
In the end, for the context of my project, we decided to go with not touching anything else but the client fixture, and not use dependency_overrides, at least in the mean time:
@pytest.fixture
def client(mocker):
mocker.patch("app.routers.my_router.permission_required", return_value=lambda: None)
app = FastAPI()
app.include_router(my_router.router())
client = TestClient(app)
yield client
This does the trick for us, hopefully it can also help others!