You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用pytest测试基于FastAPI实现的SSE(Server-Sent Events)端点?

测试FastAPI的SSE端点问题及解决方法

问题场景

基于FastAPI实现的SSE端点可正常运行,但使用TestClient编写pytest测试时,代码在client.get('/')处停滞,无法执行后续断言逻辑。

问题原因

TestClient默认会等待整个响应流完全结束后才返回结果,但SSE是无限持续的数据流,端点中的生成器会一直循环发送事件,导致测试进程被无限阻塞。

解决方案

方案1:开启流式请求并限制读取行数

在发送请求时指定stream=True,让客户端立即返回响应对象,读取指定数量的事件后主动终止循环并关闭响应,避免无限等待。

修改后的测试代码:

# test_main.py
from fastapi import FastAPI, Request
from fastapi.testclient import TestClient
from fastapi.responses import StreamingResponse
import asyncio
import json   

app = FastAPI()

@app.get('/')
async def main(request: Request) -> StreamingResponse:
    async def gen():
        while True: 
            yield 'data: ' + json.dumps({'msg': 'Hello World!'}) + '\n\n'
            await asyncio.sleep(0.5)
            
    return StreamingResponse(gen(), media_type='text/event-stream')
    
client = TestClient(app)
    
def test_read_stream():
    # 开启流式请求,不等待响应完全结束
    with client.get("/", stream=True) as response:
        assert response.status_code == 200
        # 读取前2条有效事件后终止
        count = 0
        for line in response.iter_lines():
            if line:  # 跳过SSE中的空行
                assert line == 'data: {"msg": "Hello World!"}'
                count += 1
                if count >= 2:
                    break

方案2:给SSE生成器添加终止逻辑

在端点的生成器中增加检测逻辑,比如判断客户端是否断开连接,或者通过查询参数控制事件发送次数,让生成器在测试场景下主动结束流。

修改后的端点代码:

@app.get('/')
async def main(request: Request) -> StreamingResponse:
    async def gen():
        try:
            send_count = 0
            # 通过查询参数控制测试时的最大发送次数
            max_count = request.query_params.get("max_count", None)
            
            while True: 
                yield 'data: ' + json.dumps({'msg': 'Hello World!'}) + '\n\n'
                await asyncio.sleep(0.5)
                
                # 测试场景下,达到指定次数后停止
                if max_count and send_count >= int(max_count):
                    break
                send_count += 1
                
                # 检测客户端是否断开连接,真实场景下自动终止
                if await request.is_disconnected():
                    break
        except asyncio.CancelledError:
            pass
            
    return StreamingResponse(gen(), media_type='text/event-stream')

对应的测试代码:

def test_read_stream():
    # 传递参数控制只发送2条事件
    response = client.get("/?max_count=2")
    assert response.status_code == 200
    # 过滤空行后验证事件数量和内容
    valid_lines = [line for line in response.iter_lines() if line]
    assert len(valid_lines) == 2
    for line in valid_lines:
        assert line == 'data: {"msg": "Hello World!"}'

说明

方案1无需修改端点代码,适合快速验证SSE格式和内容;方案2更贴近真实场景的连接管理,同时支持测试时的可控终止。

内容的提问来源于stack exchange,提问作者ywat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 11:27:11