简体   繁体   English

FastAPI:如何通过覆盖 `Depends()` 中的函数来测试 API

[英]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.我对 FastAPI 测试非常非常陌生,因此我们将不胜感激任何正确方向的指导。

So what I have right now is as follows:所以我现在拥有的如下:

A very simple routes file: datapoint_routes.py一个非常简单的路由文件: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 .在这里查看了 FastAPI 测试依赖项指南 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文件: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 .我希望在测试中, response = client.get()将基于覆盖函数override_dep ,它将替换some_function_is

I thought the response.json() would be:我认为response.json()将是:

{
    '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.这意味着测试中的override_dep函数是没有用的。

I even checked out the value of app.dependency_overrides , and it shows a correct map:我什至检查了app.dependency_overrides的值,它显示了正确的映射:

(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 .您正在test 中创建 FastAPI app对象,但您正在使用已定义的路由器与您的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): TestClient 通常与根应用程序一起使用(以便测试针对应用程序本身运行):

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.这按预期通过了,因为您现在正在针对应用程序( TestClient(app) )进行测试 - 您覆盖依赖项的位置。

MatsLindh's answer does solve the problem, and I would like to suggest another improvement. MatsLindh 的回答确实解决了这个问题,我想提出另一项改进。

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.我编写了一个简单的 pytest 插件来与 FastAPI 的依赖系统集成以简化语法。

Install it via: pip install pytest-fastapi-deps and then use it like so:通过以下方式安装它: pip install pytest-fastapi-deps然后像这样使用它:

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"
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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