0

0

在Pytest中测试FastAPI WebSocket连接关闭的正确方法

碧海醫心

碧海醫心

发布时间:2025-10-01 12:35:13

|

807人浏览过

|

来源于php中文网

原创

在Pytest中测试FastAPI WebSocket连接关闭的正确方法

本文详细介绍了在Pytest框架下,如何有效测试FastAPI WebSocket连接的异常关闭情况,特别是当服务器因业务逻辑(如连接无效房间)主动断开连接时。核心解决方案是利用pytest.raises(WebSocketDisconnect)结合客户端尝试接收数据,以捕获服务器断开连接后引发的异常,从而验证连接已按预期关闭。

1. 理解WebSocket连接关闭与测试需求

在开发基于fastapi的websocket应用时,我们经常需要处理各种连接状态,包括正常的建立、通信以及异常的断开。特别是在某些业务逻辑下,服务器可能会主动拒绝或关闭客户端的连接,例如当客户端尝试连接一个不存在的房间或未授权的资源时。为了确保这些断开逻辑的正确性,编写可靠的测试用例至关重要。

使用Pytest作为测试框架,FastAPI提供了TestClient来模拟客户端行为,包括WebSocket连接。我们的目标是验证当服务器主动关闭连接时,客户端能够正确地捕获到WebSocketDisconnect异常。

2. 初始尝试及遇到的问题

最初的测试思路可能是直接将pytest.raises(WebSocketDisconnect)上下文管理器包裹在client.websocket_connect()调用外部,期望连接失败时立即捕获异常。

import pytest
from fastapi.testclient import TestClient
from fastapi.websockets import WebSocketDisconnect

# 假设 app 和 get_manager, override_manager 等已定义
# ... (省略依赖注入和TestClient初始化代码) ...

class TestWebsocketConnection:
    def test_connect_to_non_existing_room_initial_attempt(self):
        with pytest.raises(WebSocketDisconnect) as e_info:
            with client.websocket_connect("/ws/non_existing_room") as ws:
                # 尝试发送数据,但可能此时连接尚未完全关闭或异常未传播
                ws.send_json({"message": "Hello world"})

然而,这种方法通常会导致测试失败,并报错FAILED: DID NOT RAISE <class 'starlette.websockets.WebSocketDisconnect'>。这是因为client.websocket_connect()在某些情况下可能会成功建立底层的TCP连接,即使服务器端的业务逻辑立即决定关闭WebSocket连接。WebSocketDisconnect异常通常不是在连接建立的瞬间抛出,而是在客户端尝试对一个已经关闭的WebSocket连接执行操作(如接收或发送数据)时才会显现。

在服务器端,当manager.connect()方法在检测到无效房间时抛出WebSocketDisconnect,FastAPI的WebSocket路由会捕获这个异常,并相应地关闭连接。

云从科技AI开放平台
云从科技AI开放平台

云从AI开放平台

下载
@router.websocket("/ws/{room_name}")
@router.websocket("/ws/{room_name}/{password}")
async def websocket_endpoint(
    websocket: WebSocket,
    manager: Annotated[GameManager, Depends(get_manager)],
):
    room_name = websocket.path_params["room_name"]
    password = websocket.path_params.get("password", None)

    try:
        await manager.connect(websocket, room_name, password)
        # 如果 manager.connect 成功,客户端才会被接受并进入消息循环
        client_id = websocket.scope["client_id"]
        while True:
            data = await websocket.receive_json()
            await manager.handle_message(room_name, client_id, data)
    except WebSocketDisconnect:
        # 当 manager.connect 抛出 WebSocketDisconnect 或客户端断开时,这里会捕获
        await manager.remove(websocket)
        # FastAPI/Starlette 框架会确保连接被正确关闭

3. 正确测试WebSocket连接关闭的方法

解决上述问题的关键在于理解WebSocketDisconnect的触发时机。它通常在客户端尝试从一个已关闭或断开的WebSocket连接接收数据时被抛出。因此,在客户端成功连接(即使服务器立即关闭)后,我们应该主动尝试从连接中接收数据。

3.1 核心思路

  1. 客户端使用client.websocket_connect()尝试连接。
  2. 即使服务器端因业务逻辑立即关闭连接,websocket_connect本身可能不会立即抛出WebSocketDisconnect。
  3. 在with client.websocket_connect(...) as ws:块内部,立即调用ws.receive_json()(或ws.receive_text()、ws.receive_bytes())。
  4. 如果服务器已经关闭了连接,尝试接收数据将触发WebSocketDisconnect异常。
  5. 将整个过程包裹在pytest.raises(WebSocketDisconnect)中,即可成功捕获并验证异常。

3.2 示例代码

以下是经过修正的、能够正确测试WebSocket连接关闭的Pytest用例:

import pytest
from fastapi.testclient import TestClient
from fastapi.websockets import WebSocketDisconnect
from typing import Annotated

# 假设以下导入和定义已存在
from src.game_manager import GameManager
from src.main import app
from fastapi import Depends, WebSocket, APIRouter # 假设 router 是一个 APIRouter 实例

# 假设 get_manager 是一个依赖函数
def get_manager():
    # 这是一个占位符,实际应用中会从依赖注入系统获取 GameManager 实例
    pass

# 重写依赖以进行测试
async def override_manager() -> GameManager:
    try:
        yield override_manager.manager
    except AttributeError:
        manager = GameManager()
        manager.add_new_game(max_players=2, room_name="foo", password=None)
        manager.add_new_game(max_players=2, room_name="bar", password="123")
        override_manager.manager = manager
        yield override_manager.manager

# 将依赖重写应用于测试客户端
app.dependency_overrides[get_manager] = override_manager

# 初始化测试客户端
client = TestClient(app)

class TestWebsocketConnection:
    def test_connect_to_non_existing_room(self):
        """
        测试连接到不存在的房间时,WebSocket连接是否被正确关闭。
        """
        with pytest.raises(WebSocketDisconnect):
            with client.websocket_connect("/ws/non_existing_room") as ws:
                # 尝试从已关闭的连接接收数据,这将触发 WebSocketDisconnect
                ws.receive_json()

    def test_connect_to_existing_room(self):
        """
        测试连接到存在的房间时,连接是否成功建立且未断开。
        """
        with client.websocket_connect("/ws/foo") as ws:
            # 确保连接成功,可以发送和接收数据
            ws.send_json({"message": "Hello from foo"})
            # 接收一条消息,验证连接活跃
            response = ws.receive_json()
            assert "type" in response and response["type"] == "websocket.send" # 示例断言

        # 确保连接在退出 with 块后正常关闭
        with pytest.raises(WebSocketDisconnect):
            # 尝试在连接关闭后接收数据,验证其已关闭
            ws.receive_json()

3.3 注意事项

  • 服务器端行为是关键: 这种测试方法的前提是服务器端确实在某些条件下(如manager.connect失败)主动关闭了WebSocket连接。如果服务器只是不发送数据但保持连接开放,receive_json()将可能无限期等待或超时,而不是抛出WebSocketDisconnect。
  • WebSocketDisconnect的来源: WebSocketDisconnect异常可能由多种情况触发,包括客户端主动断开、服务器主动断开、网络问题等。在测试中,我们确保它是由于服务器端业务逻辑拒绝连接导致的。
  • 依赖注入: 在测试中,通常需要通过app.dependency_overrides来模拟或控制依赖项(如GameManager)的行为,以确保测试的隔离性和可预测性。
  • 测试覆盖: 除了测试连接关闭,还应测试正常连接、消息发送与接收、以及其他异常情况。

4. 总结

在Pytest中测试FastAPI WebSocket连接的关闭,尤其是当服务器因业务逻辑主动断开连接时,关键在于模拟客户端尝试从一个已关闭的连接接收数据。通过将ws.receive_json()(或类似接收方法)调用包裹在pytest.raises(WebSocketDisconnect)上下文管理器中,我们可以有效地验证服务器是否按预期关闭了连接。这种方法能够精确地捕获到WebSocketDisconnect异常,从而确保WebSocket连接管理逻辑的健壮性。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Python FastAPI异步API开发_Python怎么用FastAPI构建异步API
Python FastAPI异步API开发_Python怎么用FastAPI构建异步API

Python FastAPI 异步开发利用 async/await 关键字,通过定义异步视图函数、使用异步数据库库 (如 databases)、异步 HTTP 客户端 (如 httpx),并结合后台任务队列(如 Celery)和异步依赖项,实现高效的 I/O 密集型 API,显著提升吞吐量和响应速度,尤其适用于处理数据库查询、网络请求等耗时操作,无需阻塞主线程。

28

2025.12.22

Python 微服务架构与 FastAPI 框架
Python 微服务架构与 FastAPI 框架

本专题系统讲解 Python 微服务架构设计与 FastAPI 框架应用,涵盖 FastAPI 的快速开发、路由与依赖注入、数据模型验证、API 文档自动生成、OAuth2 与 JWT 身份验证、异步支持、部署与扩展等。通过实际案例,帮助学习者掌握 使用 FastAPI 构建高效、可扩展的微服务应用,提高服务响应速度与系统可维护性。

251

2026.02.06

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

891

2024.01.03

python中class的含义
python中class的含义

本专题整合了python中class的相关内容,阅读专题下面的文章了解更多详细内容。

32

2025.12.06

Golang WebSocket与实时通信开发
Golang WebSocket与实时通信开发

本专题系统讲解 Golang 在 WebSocket 开发中的应用,涵盖 WebSocket 协议、连接管理、消息推送、心跳机制、群聊功能与广播系统的实现。通过构建实际的聊天应用或实时数据推送系统,帮助开发者掌握 如何使用 Golang 构建高效、可靠的实时通信系统,提高并发处理与系统的可扩展性。

30

2025.12.22

PHP WebSocket 实时通信开发
PHP WebSocket 实时通信开发

本专题系统讲解 PHP 在实时通信与长连接场景中的应用实践,涵盖 WebSocket 协议原理、服务端连接管理、消息推送机制、心跳检测、断线重连以及与前端的实时交互实现。通过聊天系统、实时通知等案例,帮助开发者掌握 使用 PHP 构建实时通信与推送服务的完整开发流程,适用于即时消息与高互动性应用场景。

142

2026.01.19

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

37

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

136

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

47

2026.03.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WEB前端教程【HTML5+CSS3+JS】
WEB前端教程【HTML5+CSS3+JS】

共101课时 | 10.2万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号