如何测试含Annotated与Depends依赖注入的FastAPI路由?解决测试问题
解决方案:FastAPI + Pytest 测试问题处理
次要问题:测试客户端弃用警告修复
直接按照警告提示修改测试客户端的创建方式,导入ASGITransport并显式指定transport参数即可消除警告:
from fastapi.testclient import TestClient from httpx import ASGITransport from main import app client = TestClient(transport=ASGITransport(app=app))
主问题:Annotated[Person, Depends()]路由测试422错误解决
问题根源
当用Annotated[Person, Depends()]声明路由参数时,FastAPI会将其识别为依赖项,而非请求体参数。此时FastAPI不会自动从请求JSON中解析Person实例,若没有自定义依赖逻辑,默认的Depends()无法获取到合法的Person对象,就会返回422错误。
分场景解决方案
场景1:仅需接收请求体
如果你的需求只是接收JSON请求体并自动解析为Person模型,完全不需要加Depends(),直接使用Person作为参数即可:
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Person(BaseModel): name: str age: int # 正确的请求体参数写法 @app.post("/person-direct") async def create_person(person: Person): return {"message": f"Hello {person.name}, age {person.age}"}
场景2:确实需要将Person作为依赖使用
如果业务逻辑要求把Person作为依赖(比如从数据库/外部服务获取、或有自定义验证逻辑),则通过**依赖覆盖(dependency override)**在测试中替换依赖返回值,同时结合参数化实现多测试用例:
步骤1:封装依赖函数
先将Person的获取逻辑封装为独立的依赖函数:
# main.py from fastapi import FastAPI, Body from pydantic import BaseModel from typing import Annotated, Depends app = FastAPI() class Person(BaseModel): name: str age: int def get_person() -> Person: # 实际场景可替换为从数据库、请求体或其他服务获取的逻辑 return Body(...) @app.post("/person-depends") async def create_person_depends(person: Annotated[Person, Depends(get_person)]): return {"message": f"Hello {person.name}, age {person.age}"}
步骤2:测试中覆盖依赖并参数化用例
在pytest中使用app.dependency_overrides替换依赖函数,搭配@pytest.mark.parametrize实现多实例测试:
# test_main.py import pytest from fastapi.testclient import TestClient from httpx import ASGITransport from main import app, Person, get_person client = TestClient(transport=ASGITransport(app=app)) # 自动清理依赖覆盖,避免影响其他测试 @pytest.fixture(autouse=True) def reset_dependencies(): yield app.dependency_overrides.clear() @pytest.mark.parametrize("test_person, expected_msg", [ (Person(name="Alice", age=25), "Hello Alice, age 25"), (Person(name="Bob", age=30), "Hello Bob, age 30"), ]) def test_person_depends(test_person, expected_msg): # 覆盖依赖,返回当前测试用的Person实例 app.dependency_overrides[get_person] = lambda: test_person # 发送请求(无需传JSON,依赖已返回测试实例) response = client.post("/person-depends") assert response.status_code == 200 assert response.json()["message"] == expected_msg
如果依赖需要从请求体获取数据,可在依赖函数中结合Body(),此时测试时正常发送JSON请求即可,确保依赖函数能正确解析。
内容的提问来源于stack exchange,提问作者mikeliux
相关产品推荐
相关产品推荐

