本文目录导读:

Aiohttp 测试 WebSocket 非常方便,尤其适合异步场景。
Aiohttp 本身就支持 WebSocket 客户端和服务端的完整实现,测试时无需引入额外的库(如 websockets 或 socket.io),它的设计让异步 WebSocket 连接的创建、发送和接收都变得自然。
核心优势
- 原生支持:服务端和客户端都是
aiohttp的一部分,接口统一,学习成本低。 - 异步友好:完全基于
asyncio,测试代码不会阻塞事件循环。 - 可直接与
pytest配合:使用pytest-asyncio可以轻松编写异步测试用例。 - 内置测试客户端:
aiohttp.test_utils提供了AioHTTPTestCase和TestClient,可以启动一个真实的测试服务器,而不是 mock。
快速示例:测试 WebSocket 双向通信
假设你有一个简单的 WebSocket 服务端,它接收消息并原样返回(Echo):
# server.py (仅用于演示,实际测试中会内联)
from aiohttp import web
async def websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
async for msg in ws:
if msg.type == web.WSMsgType.TEXT:
await ws.send_str(f"Echo: {msg.data}")
elif msg.type == web.WSMsgType.ERROR:
break
return ws
编写测试
import pytest
from aiohttp import web
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
# 方案一:基于类的测试 (传统方式)
class TestWebSocket(AioHTTPTestCase):
async def get_application(self):
app = web.Application()
app.router.add_get('/ws', self.websocket_handler)
return app
async def websocket_handler(self, request):
# 实际测试中,这里可以复用或内联生产代码
ws = web.WebSocketResponse()
await ws.prepare(request)
async for msg in ws:
if msg.type == web.WSMsgType.TEXT:
await ws.send_str(f"Echo: {msg.data}")
return ws
@unittest_run_loop
async def test_echo(self):
# 连接 WebSocket
resp = await self.client.ws_connect('/ws')
assert resp.status == 101 # 升级协议成功
# 发送消息
await resp.send_str("Hello")
# 接收响应
msg = await resp.receive()
assert msg.data == "Echo: Hello"
await resp.close()
更现代的 pytest 风格(推荐)
import pytest
from aiohttp import web
@pytest.fixture
def app():
app = web.Application()
app.router.add_get('/ws', websocket_handler)
return app
# 需要安装 pytest-aiohttp 插件
async def test_websocket_echo(aiohttp_client, app):
client = await aiohttp_client(app)
# 连接
ws = await client.ws_connect('/ws')
assert ws.closed is False
# 测试双向通信
await ws.send_str('ping')
response = await ws.receive()
assert response.data == 'Echo: ping'
# 测试关闭
await ws.close()
assert ws.closed
# 或者更直接地使用 aiohttp 客户端 (不需要服务器,适用于纯客户端测试)
async def test_websocket_connectivity():
async with aiohttp.ClientSession() as session:
async with session.ws_connect('ws://echo.websocket.org') as ws:
await ws.send_str('hello')
msg = await ws.receive(timeout=10)
assert msg.data == 'hello' # 公共 echo 服务器会原样返回
常见测试场景与代码片段
测试服务器主动推送 (如心跳)
async def test_server_push(aiohttp_client, app):
client = await aiohttp_client(app)
ws = await client.ws_connect('/ws')
# 假设服务器每 5 秒发送一次 "heartbeat"
msg = await ws.receive(timeout=10)
assert msg.data == "heartbeat"
测试不合法消息(如二进制、关闭帧)
async def test_binary_message_rejected(aiohttp_client, app):
client = await aiohttp_client(app)
ws = await client.ws_connect('/ws')
# 发送二进制数据 (假设服务端只接受文本)
await ws.send_bytes(b'\x00\x01')
# 服务端可能发送错误码或主动关闭
msg = await ws.receive()
assert msg.type == web.WSMsgType.CLOSE
assert msg.data == 1003 # 不支持的关闭码
测试并发连接
async def test_multiple_clients(aiohttp_client, app):
client = await aiohttp_client(app)
# 同时打开两个连接
ws1 = await client.ws_connect('/ws')
ws2 = await client.ws_connect('/ws')
await ws1.send_str("from ws1")
await ws2.send_str("from ws2")
assert (await ws1.receive()).data == "Echo: from ws1"
assert (await ws2.receive()).data == "Echo: from ws2"
可能遇到的问题及解决
| 问题 | 原因 | 解决 |
|---|---|---|
Connection closed 错误 |
测试结束时未正确关闭连接 | 使用 async with 或显式 await ws.close() |
| 接收超时 | 服务器未按预期发送消息 | 设置 receive(timeout=5) 参数 |
RuntimeError: Session is closed |
在 ClientSession 外使用 WebSocket |
确保操作在 async with session: 块内 |
无法连接到 localhost |
测试客户端未启动服务器 | 使用 aiohttp_client(app) 会自动创建测试服务器 |
pytest-asyncio 冲突 |
其他库使用不同的事件循环 | 明确设置 @pytest.mark.asyncio |
- 方便指数:★★★★★ (满分)
- 理由:Aiohttp 的 WebSocket 测试非常直观,代码量少,错误处理清晰,与
pytest-asyncio和aiohttp.test_utils结合时,可以模拟真实网络环境(包括并发、断线、异常消息)而无需外部工具。 - 建议:如果项目已经用 aiohttp 作为异步框架,完全不需要 引入其他 WebSocket 测试库,对于生产级测试,配合 pytest-timeout 和 pytest-rerunfailures 可以处理网络不稳定性。
如果你有具体的测试场景(比如需要测试鉴权、多房间广播、二进制协议),我可以提供更详细的代码示例。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播