FastAPI:如何通过覆盖 `Depends()` 中的函数来测试 API
FastAPI: How to test APIs by overriding functions in `Depends()`
我对 FastAPI 测试非常非常陌生,所以任何正确方向的指导都将不胜感激。
所以我现在的情况如下:
一个非常简单的路由文件: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
}
我希望能够对此进行测试。我查看了 FastAPI Testing Dependencies guide here。但这根本没有帮助,因为它对我不起作用。
对于我的测试,我现在拥有的是这样的:
文件: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"
}
我希望在测试中,response = client.get()
将基于覆盖函数 override_dep
,它将取代 some_function_is
.
我认为 response.json()
会是:
{
'datapoint_name': 'abcdef',
'state': 'Test'
}
相反,它是:
{
'datapoint_name': 'abcdef',
'state': 'Actual'
}
这意味着测试中的override_dep
功能没有用
我什至检查了 app.dependency_overrides
的值,它显示了正确的地图:
(Pdb) app.dependency_overrides
{<function some_function_is at 0x102b3d1b0>: <function override_dep at 0x102b3e0e0>}
函数的内存值匹配的地方:
(Pdb) some_function_is
<function some_function_is at 0x102b3d1b0>
(Pdb) override_dep
<function override_dep at 0x102b3e0e0>
我做错了什么?
您正在测试中创建 FastAPI app
对象 ,但您使用的是已定义的 路由器 你的TestClient
。由于此路由器从未在应用程序中注册,因此覆盖应用程序的依赖项不会做任何有用的事情。
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)
然后是测试:
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"
}
这按预期通过了,因为您现在正在针对应用程序(TestClient(app)
)进行测试 - 您覆盖依赖项的位置。
确实解决了问题,我想提出另一个改进建议。
覆盖测试文件根目录中的 depends 函数,由于缺乏清理,引入了干扰以下测试的风险。
相反,我建议使用夹具,这将确保测试的隔离。我写了一个简单的pytest插件来集成FastAPI的依赖系统来简化语法。
通过以下方式安装它: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"
}
我对 FastAPI 测试非常非常陌生,所以任何正确方向的指导都将不胜感激。
所以我现在的情况如下:
一个非常简单的路由文件: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
}
我希望能够对此进行测试。我查看了 FastAPI Testing Dependencies guide here。但这根本没有帮助,因为它对我不起作用。
对于我的测试,我现在拥有的是这样的:
文件: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"
}
我希望在测试中,response = client.get()
将基于覆盖函数 override_dep
,它将取代 some_function_is
.
我认为 response.json()
会是:
{
'datapoint_name': 'abcdef',
'state': 'Test'
}
相反,它是:
{
'datapoint_name': 'abcdef',
'state': 'Actual'
}
这意味着测试中的override_dep
功能没有用
我什至检查了 app.dependency_overrides
的值,它显示了正确的地图:
(Pdb) app.dependency_overrides
{<function some_function_is at 0x102b3d1b0>: <function override_dep at 0x102b3e0e0>}
函数的内存值匹配的地方:
(Pdb) some_function_is
<function some_function_is at 0x102b3d1b0>
(Pdb) override_dep
<function override_dep at 0x102b3e0e0>
我做错了什么?
您正在测试中创建 FastAPI app
对象 ,但您使用的是已定义的 路由器 你的TestClient
。由于此路由器从未在应用程序中注册,因此覆盖应用程序的依赖项不会做任何有用的事情。
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)
然后是测试:
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"
}
这按预期通过了,因为您现在正在针对应用程序(TestClient(app)
)进行测试 - 您覆盖依赖项的位置。
覆盖测试文件根目录中的 depends 函数,由于缺乏清理,引入了干扰以下测试的风险。
相反,我建议使用夹具,这将确保测试的隔离。我写了一个简单的pytest插件来集成FastAPI的依赖系统来简化语法。
通过以下方式安装它: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"
}