如何测试含内部逻辑的FastAPI POST接口?解决422报错问题
FastAPI接口测试422错误修复方案
接口代码(main.py)
from retriever import get_filtered_data @app.post( "/getApi", response_class=JSONResponse, tags=["Some tags"], description="Some description" ) async def get_json_api(request: Basemodel, params: Params = Depends()) -> Page: try: response = get_filtered_data('param_1', request=request) return paginate(response['items'], additional_data={ "ABC": 123 }, params=params ) except Exception as exception: return HttpErrorResponse(data=f'ERROR details = {exception}', timestamp=datetime.now())
原测试用例代码
from fastapi.testclient import TestClient from unittest.mock import patch, MagicMock, ANY def test_get_json_property(self): mocked_response = requests.Response() with patch("requests.post", return_value=mocked_response) as mocked_request: response = self.client.post("/getApi", json={"in_filter": {"test_key": "test_value"}}, headers={"content-type": "application/json"}) mocked_request.return_value = {"items": {"test_key": "test_value"}} assert response.status_code == 200 self.assertEqual(response.json(), mocked_response)
错误输出
{ "type": "missing", "loc": ["response", "items"], "msg": "Field required", "input": "<HttpErrorResponse.HttpErrorResponse object at 0x000001FD7E0C17B0>" }, { "type": "missing", "loc": ["response", "total"], "msg": "Field required", "input": "<HttpErrorResponse.HttpErrorResponse object at 0x000001FD7E0C17B0>" }, { "type": "missing", "loc": ["response", "page"], "msg": "Field required", "input": "<HttpErrorResponse.HttpErrorResponse object at 0x000001FD7E0C17B0>" }, { "type": "missing", "loc": ["response", "size"], "msg": "Field required", "input": "<HttpErrorResponse.HttpErrorResponse object at 0x000001FD7E0C17B0>" }, { "type": "missing", "loc": ["response", "links"], "msg": "Field required", "input": "<HttpErrorResponse.HttpErrorResponse object at 0x000001FD7E0C17B0>" }
问题原因
- Mock对象错误:测试中mock了
requests.post,但接口实际依赖的是retriever.get_filtered_data,导致get_filtered_data执行真实逻辑时抛出异常,返回HttpErrorResponse。而接口声明的响应模型是Page,FastAPI校验返回结构不匹配,触发422错误。 - Mock返回值时机错误:在发送请求后才设置
mocked_request.return_value,此时接口已经拿到初始的requests.Response()对象,该对象不是字典结构,执行response['items']时抛出异常。 - 断言逻辑错误:
response.json()是接口返回的JSON字典,而mocked_response是requests.Response对象,两者类型和内容都不匹配,断言必然失败。
修复后的测试用例
from fastapi.testclient import TestClient from unittest.mock import patch, MagicMock, ANY from your_module import Page # 导入Page模型 def test_get_json_property(self): # 模拟get_filtered_data的返回值,符合接口预期格式 mock_filtered_data = { "items": [{"test_key": "test_value"}] # items应为列表,适配paginate逻辑 } # 正确mock依赖的get_filtered_data函数 with patch("retriever.get_filtered_data", return_value=mock_filtered_data): # 发送请求,Params有默认值可省略,否则需补充参数 response = self.client.post( "/getApi", json={"in_filter": {"test_key": "test_value"}}, headers={"content-type": "application/json"} ) # 断言状态码 assert response.status_code == 200 # 获取接口返回的JSON数据 response_json = response.json() # 断言核心字段符合预期 assert response_json["items"] == mock_filtered_data["items"] assert response_json["ABC"] == 123 # 验证附加数据 # 按需断言分页字段,根据Params默认值调整 assert response_json["page"] == 1 assert response_json["size"] == 10 # 假设默认size为10
关键修改说明
- 锁定Mock目标:直接mock
retriever.get_filtered_data,避免绕开核心依赖层。 - 提前设置返回值:在发送请求前定义好mock的返回结构,确保接口能正常进入
paginate流程。 - 匹配响应模型断言:按照
Page模型的结构断言,验证分页字段和自定义附加数据,保证断言逻辑有效。
内容的提问来源于stack exchange,提问作者iambhaswar
相关产品推荐
相关产品推荐

