单元测试中如何修改Starlette/FastAPI的Request对象scope字段
问题背景
你现有定义的基础GET请求FastAPI路由代码如下:
@router.get("/reports") async def get_reports(request: Request) -> Dict: return
初始规划的单元测试代码如下:
def test_method_can_access_request_fields(): client = TestClient() response = client.get("/")
当前已知信息:
- 路由内接收到的
request为starlette.requests.Request类型对象,包含字典类型的scope字段 - 生产环境使用Mangum将FastAPI部署为AWS Lambda服务时,Request对象会被自动注入
aws.event字段 - 单元测试需要实现通过TestClient修改传入请求的
request.scope字典,添加自定义aws.event字段,确认是否可通过传入测试配置参数的方式完成字段注入
实现方案
目前TestClient没有提供直接传入测试配置参数注入scope顶层自定义字段的公开API,推荐通过测试专用中间件的方式实现注入,实现简单且不会侵入生产代码,两种常用实现方式如下:
方法1:单测试用例内注入(适合独立用例)
在单个测试用例内创建独立的测试应用实例,添加临时中间件往请求scope里注入目标字段,测试结束后不会影响生产逻辑:
from fastapi import FastAPI from starlette.testclient import TestClient # 导入你项目中定义好的router def test_method_can_access_request_fields(): # 初始化测试专用的FastAPI实例 test_app = FastAPI() # 注册临时注入中间件 @test_app.middleware("http") async def inject_mock_aws_event(request, call_next): # 替换为你业务需要的模拟Lambda event内容 request.scope["aws.event"] = { "version": "2.0", "rawPath": "/reports", "requestContext": {}, "headers": {} } return await call_next(request) # 挂载业务路由 test_app.include_router(router) client = TestClient(test_app) # 注意路由路径要和定义的/reports保持一致,原测试代码写的/路径会返回404 response = client.get("/reports") # 后续编写响应断言、request字段校验逻辑即可
方法2:pytest fixture注入(适合多测试用例复用)
如果项目用pytest编写单元测试,可以把注入逻辑封装为公共fixture,所有需要模拟Lambda事件的用例直接调用即可,减少重复代码:
import pytest from fastapi import FastAPI from starlette.testclient import TestClient # 导入你项目中定义好的router @pytest.fixture def mock_event_client(): test_app = FastAPI() @test_app.middleware("http") async def inject_mock_aws_event(request, call_next): request.scope["aws.event"] = { # 填写通用的模拟Lambda event内容,需要自定义的内容可以在用例内再调整 } return await call_next(request) test_app.include_router(router) yield TestClient(test_app) # 测试用例直接传入fixture即可使用 def test_method_can_access_request_fields(mock_event_client): response = mock_event_client.get("/reports") # 编写断言逻辑
注意:不要直接在生产环境使用的FastAPI实例上添加测试中间件,避免测试逻辑污染生产运行流程。
内容的提问来源于stack exchange,提问作者John Kealy
相关产品推荐
相关产品推荐

