尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

FastAPI 如何在测试中运行 lifespan 启动与关闭事件?

FastAPI 如何在测试中运行 lifespan 启动与关闭事件? FastAPI 如何在测试中运行 lifespan 启动与关闭事件【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi如果你的 FastAPI 应用通过lifespan参数在启动时加载共享资源比如数据库连接池、机器学习模型那么在测试里直接调用TestClient(app)发请求时这些启动逻辑并不会执行——因为lifespan没有被触发。FastAPI 官方文档给出的解决方法是把TestClient放进with语句中使用进入with块时运行lifespan中yield之前的启动代码退出with块时模拟应用终止并运行yield之后的关闭代码。本文基于 Testing Events 文档 与 Lifespan Events 文档 给出完整可运行的测试写法。前提用 lifespan 定义应用lifespan是一个用asynccontextmanager装饰的、带yield的异步函数通过FastAPI的lifespan参数传入。yield之前的代码在应用开始接收请求之前执行一次yield之后的代码在应用停止处理请求时执行一次。官方示例完整源码from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.testclient import TestClient items {} asynccontextmanager async def lifespan(app: FastAPI): items[foo] {name: Fighters} items[bar] {name: Tenders} yield # clean up items items.clear() app FastAPI(lifespanlifespan) app.get(/items/{item_id}) async def read_items(item_id: str): return items[item_id]需要说明的限制如果你传入了lifespan参数startup和shutdown事件处理器将不再被调用——是lifespan和事件二选一不能同时生效。核心写法把 TestClient 放进 with 语句官方文档给出的关键做法是一行用with TestClient(app) as client:替代直接调用TestClient(app)。上面的应用对应的测试完整文件见 tutorial004_py310.pydef test_read_items(): # Before the lifespan starts, items is still empty assert items {} with TestClient(app) as client: # Inside the with TestClient block, the lifespan starts and items added assert items {foo: {name: Fighters}, bar: {name: Tenders}} response client.get(/items/foo) assert response.status_code 200 assert response.json() {name: Fighters} # After the requests is done, the items are still there assert items {foo: {name: Fighters}, bar: {name: Tenders}} # The end of the with TestClient block simulates terminating the app, so # the lifespan ends and items are cleaned up assert items {}断言的位置体现了lifespan在测试中的执行时机这也是判断写法是否正确的依据with块之前items {}说明启动代码尚未运行with块内部lifespan已启动items中已有foo、bar两个值请求/items/foo返回 200 且 JSON 为{name: Fighters}退出with块之后TestClient模拟应用终止lifespan中yield之后的清理代码执行items变回空字典。运行与验证测试用 pytest 运行官方文档中的运行命令为$ uv run pytest成功条件是上述断言全部通过进入with块前后items的内容变化、请求状态码与响应体、以及退出with块后items被清空。已废弃的 startup / shutdown 事件写法如果你的应用仍在使用已废弃的app.on_event(startup)事件见 events 文档TestClient的with用法同样适用测试写法不变只是应用定义改为事件形式完整文件见 tutorial003_py310.pyfrom fastapi import FastAPI from fastapi.testclient import TestClient app FastAPI() items {} app.on_event(startup) async def startup_event(): items[foo] {name: Fighters} items[bar] {name: Tenders} app.get(/items/{item_id}) async def read_items(item_id: str): return items[item_id] def test_read_items(): with TestClient(app) as client: response client.get(/items/foo) assert response.status_code 200 assert response.json() {name: Fighters}文档同时建议对于启动和关闭相互关联的逻辑获取资源后释放资源应改用lifespan而不是两个独立的事件函数。边界与限制异步测试中AsyncClient不触发 lifespan。Async Tests 文档 明确警告如果测试函数是async def并使用AsyncClientTestClient的那套魔法不再工作AsyncClient不会触发 lifespan 事件文档建议改用 florimondmanca/asgi-lifespan 提供的LifespanManager来确保事件被触发。如果你的测试是普通def函数并使用TestClient则不受此限制。子应用不执行 lifespan 事件。events 文档 指出lifespan 事件只会在主应用上执行不会为通过 Mount 挂载的子应用执行。更多底层细节ASGI Lifespan Protocol可参考 Testing Events 文档 中指向的 Starlette 官方文档。完整的 lifespan 定义方式含模型加载用例见 docs/en/docs/advanced/events.md可运行的测试源码在 docs_src/app_testing/tutorial004_py310.py。【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表