ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FastAPI WebSocket 实战:用 TestClient 连接并断言消息

FastAPI WebSocket 实战:用 TestClient 连接并断言消息 FastAPI WebSocket 实战用 TestClient 连接并断言消息【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapiWebSocket 端点在测试套件里跑不动怎么办如果你的 FastAPI 项目里有app.websocket端点多半撞上过这个情况CI 里 HTTP 接口全绿偏偏 WebSocket 端点没人敢写测试。是不是得专门引一个 ws 客户端其实不用——跑 HTTP 用的同一个TestClient就能建一条真实的 WebSocket 连接并把服务端发出的消息逐条断言掉。不用装新库也不用你懂 ws 协议细节。websocket_connect 到底从哪来先破除一个常见误解FastAPI 没有自己实现测试客户端。打开 fastapi/testclient.py整个文件只有一行再导出from starlette.testclient import TestClient # noqa换句话说websocket_connect()是上游 Starlette 的 TestClient 提供的会话机制进入上下文context manager时执行 WebSocket 握手并建立连接退出上下文时自动关闭连接无需手动清理。异步应用由 TestClient 在内部驱动所以测试函数本身不需要写任何await。同理fastapi/websockets.py 也只是把WebSocket、WebSocketDisconnect、WebSocketState从 Starlette 再导出出来应用代码和测试代码统一从 fastapi 导入即可。机制清楚后直接看最小可运行版本。最小可测的 WebSocket 测试长什么样下面这段代码验证一条最简链路服务端 accept 连接后发一条 JSON测试端接收这条 JSON 并断言它一致。被测代码from fastapi import FastAPI from fastapi.testclient import TestClient from fastapi.websockets import WebSocket app FastAPI() app.websocket(/ws) async def websocket_endpoint(websocket: WebSocket): await websocket.accept() # 第一行必须 accept否则握手不成立 await websocket.send_json({msg: Hello WebSocket}) await websocket.close()测试代码有效行只有 4 行def test_websocket(): client TestClient(app) with client.websocket_connect(/ws) as websocket: # 灵魂行上下文里建连 data websocket.receive_json() assert data {msg: Hello WebSocket}这套结构可以直接执行官方示例 docs_src/app_testing/tutorial002_py310.py 就是这两部分的合体tests/test_tutorial/test_testing/test_tutorial002.py 直接导入这两个函数当回归用例跑侧面说明它本身就是最小可执行单元。服务端accept→send_json→close三连是测试里所有断言顺序的来源。回显、lifespan 等场景怎么写连接会话里的会话对象提供这样一组收发方法发送方与对应接收方配对断言对象就是 receive 的返回值方法用途receive_text()/receive_json()/receive_bytes()接收一条文本 / JSON / 二进制消息并断言send_text()/send_json()/send_bytes()向服务端发消息触发其处理逻辑后等待应答变体一回显端点双向对话。把端点改成收一条、回一条测试端就变成先发后收app.websocket(/ws) async def echo(websocket: WebSocket): await websocket.accept() data await websocket.receive_text() # 服务端后开口 await websocket.send_text(data) def test_echo(): client TestClient(app) with client.websocket_connect(/ws) as ws: ws.send_text(Hello, server) assert ws.receive_text() Hello, server结论对话型测试由谁先开口决定——服务端先 receive测试就先 send顺序一错测试直接挂起。变体二嵌套 lifespan启动时初始化应用状态。应用若靠 lifespan 初始化全局状态参考 docs_src/app_testing/tutorial004_py310.py 里预置items字典的写法WebSocket 测试要多包一层def test_websocket_with_lifespan(): with TestClient(app) as client: # 外层触发 lifespan 启动与关闭 with client.websocket_connect(/ws) as websocket: assert websocket.receive_json() {msg: Hello WebSocket}结论外层with管应用生命周期内层with管连接会话两层职责不同不能合并。这两个套路覆盖了大部分真实场景剩下的就是时序别错。这些测试里有哪些坑要防异步测试函数现象是async def测试里 TestClient 报错或挂起 → 原因是 TestClient 靠同步调用栈驱动 ASGI 应用异步上下文里事件循环已被占用 → 规避测试函数保持同步def异步场景改用httpx.AsyncClientASGITransport方案仅适用于 HTTP。消息顺序错位现象是测试卡到超时或断言失败 → 原因是服务端发送顺序与测试接收顺序对不上 → 规避按服务端收发时序严格复现。漏写 accept()现象是测试端连接立刻断开、receive_*抛WebSocketDisconnect→ 原因是服务端没接受握手 → 规避端点第一行写await websocket.accept()。服务端主动关闭被误判为失败现象是receive_*抛出WebSocketDisconnect被当成测试错误 → 原因是服务端调用了close()→ 规避用pytest.raises(WebSocketDisconnect)主动断言断连路径。lifespan 没触发现象是 lifespan 初始化的全局状态在测试里为空 → 原因是直接TestClient(app)没进with块 → 规避整体包一层with TestClient(app) as client:。坑防完测试是否真稳得住要靠实跑确认。跑完之后核对什么websocket_connect能进入上下文且receive_json()断言通过回显端点的收发顺序与服务端一致无挂起嵌套with TestClient形式下 lifespan 被触发lifespan 初始化的状态断言通过pytest.raises(WebSocketDisconnect)能接住服务端的主动断开测试函数全部为同步def不依赖异步测试环境延伸阅读docs/en/docs/advanced/websockets.mdWebSocket 端点怎么编写、tests/test_tutorial/test_testing/本教程对应的回归测试用例。【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表