简体   繁体   中英

FastAPI: How to test APIs by overriding functions in `Depends()`

I am very, very new to FastAPI testing, so any guidance in the right direction would be appreciated.

So what I have right now is as follows:

A very simple routes file: datapoint_routes.py

from fastapi import APIRouter, Depends

datapoint_router = APIRouter()


def some_function_is():
    return "Actual"


@datapoint_router.get('/{datapoint_name}')
def get_db(
    datapoint_name: str,
    some_function_output=Depends(some_function_is)
) -> dict:
    return {
        'datapoint_name': datapoint_name,
        'state': some_function_output
    }

I want to be able to test this. I checked out FastAPI Testing Dependencies guide here . But this did not help at all, because it didn't work for me.


For my tests, what I have right now is something like this:

File: test_datapoint_router.py

from typing import Union

from fastapi import FastAPI
from fastapi.testclient import TestClient

from datapoint_routes import datapoint_router, some_function_is


DATAPOINT_NAME = 'abcdef'

app = FastAPI()
client = TestClient(datapoint_router)


def override_dep(q: Union[str, None] = None):
    return "Test"


app.dependency_overrides[some_function_is] = override_dep


def test_read_main():
    response = client.get(f"/{DATAPOINT_NAME}")
    assert response.status_code == 200
    assert response.json() == {
        'datapoint_name': DATAPOINT_NAME,
        'state': "Test"
    }

I would hope in the test, the response = client.get() would be based on the overriding function override_dep , which would replace some_function_is .

I thought the response.json() would be:

{
    'datapoint_name': 'abcdef',
    'state': 'Test'
}

instead, it is:

{
    'datapoint_name': 'abcdef',
    'state': 'Actual'
}

This means that the override_dep function in the test is useless.

I even checked out the value of app.dependency_overrides , and it shows a correct map:

(Pdb) app.dependency_overrides
{<function some_function_is at 0x102b3d1b0>: <function override_dep at 0x102b3e0e0>}

Where the memory values of functions do match:

(Pdb) some_function_is
<function some_function_is at 0x102b3d1b0>

(Pdb) override_dep
<function override_dep at 0x102b3e0e0>

What am I doing wrong?

You're creating the FastAPI app object in your test , but you're using a defined router with your TestClient . Since this router is never registered with the app, overriding a dependency with the app won't do anything useful.

The TestClient is usually used with the root app (so that the tests run against the app itself):

from fastapi import APIRouter, Depends, FastAPI

app = FastAPI()
datapoint_router = APIRouter()

def some_function_is():
    return "Actual"


@datapoint_router.get('/{datapoint_name}')
def get_db(
    datapoint_name: str,
    some_function_output=Depends(some_function_is)
) -> dict:
    return {
        'datapoint_name': datapoint_name,
        'state': some_function_output
    }

app.include_router(datapoint_router)

And then the test:

from typing import Union

from fastapi.testclient import TestClient
from datapoint_routes import app, datapoint_router, some_function_is


DATAPOINT_NAME = 'abcdef'

client = TestClient(app)


def override_dep(q: Union[str, None] = None):
    return "Test"


app.dependency_overrides[some_function_is] = override_dep


def test_read_main():
    response = client.get(f"/{DATAPOINT_NAME}")
    assert response.status_code == 200
    assert response.json() == {
        'datapoint_name': DATAPOINT_NAME,
        'state': "Test"
    }

This passes as expected, since you're now testing against the app ( TestClient(app) ) - the location where you overrode the dependency.

MatsLindh's answer does solve the problem, and I would like to suggest another improvement.

Overriding the depends function at the root of the test file, introduces a risk of interfering with the following tests, due to a lack of cleanup.

Instead, I suggest using a fixture, which would ensure the isolation of your tests. I wrote a simple pytest plugin to integrate with the dependency system of FastAPI to simplify the syntax as well.

Install it via: pip install pytest-fastapi-deps and then use it like so:

from typing import Union

from fastapi.testclient import TestClient
from datapoint_routes import app, datapoint_router, some_function_is

DATAPOINT_NAME = 'abcdef'

client = TestClient(app)


def override_dep(q: Union[str, None] = None):
    return "Test"


def test_read_main_context_manager(fastapi_dep):
    with fastapi_dep(app).override({some_function_is: override_dep}):
        response = client.get(f"/{DATAPOINT_NAME}")
        assert response.status_code == 200
        assert response.json() == {
            'datapoint_name': DATAPOINT_NAME,
            'state': "Test"
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM