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

为什么pytest测试简单异步函数时第二个用例会出现挂起问题?

问题根因

  1. 端口占用冲突:你把服务端口硬编码为固定值65432,第一个测试执行完fixture销毁服务时,TCP连接会进入TIME_WAIT状态,端口不会立即释放,第二个测试再启动服务绑定同一个端口会失败,导致等待服务启动的逻辑无限重试挂起。
  2. 等待服务函数缩进错误:_async_wait_for_server中try-except代码块没有缩进进while True循环内,实际执行时会卡在while True里无限创建socket,永远不会执行连接逻辑(单个测试能跑通大概率是你粘贴代码时缩进丢失,本地实际缩进是对的,但这个语法问题必须修正)。
  3. 服务销毁逻辑不全:你调用cancel_handle.cancel()之后没有等待服务任务彻底退出,端口还被前一个服务占用时第二个测试就已经尝试启动新服务,必然绑定失败。
  4. 事件循环复用问题:你自定义的event_loop fixture直接返回全局事件循环,没有做重置处理,多个测试用例复用同一个事件循环可能残留未处理的异步任务。

修复方案

1. 修正等待服务函数的缩进

async def _async_wait_for_server(event_loop, addr, port):
    while True:
        a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            await event_loop.sock_connect(a_socket, (addr, port))
            return
        except ConnectionRefusedError:
            await asyncio.sleep(0.001)
        finally:
            a_socket.close()

2. 改用动态空闲端口+完善服务销毁逻辑

直接删除你自定义的event_loop fixture,用pytest-asyncio自带的即可,同时修改server fixture如下:

import socket
@pytest.fixture()
def server(event_loop):
    # 自动获取系统空闲端口,避免硬编码端口冲突
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 0))
    port = sock.getsockname()[1]
    sock.close()

    # 启动服务
    cancel_handle = asyncio.ensure_future(main(port), loop=event_loop)
    event_loop.run_until_complete(asyncio.wait_for(
        _async_wait_for_server(event_loop, HOST, port), 5.0))
    try:
        yield port
    finally:
        # 取消服务任务后等待彻底退出,释放端口
        cancel_handle.cancel()
        try:
            event_loop.run_until_complete(cancel_handle)
        except asyncio.CancelledError:
            pass

3. 可选优化:服务启动时开启端口复用

如果要继续使用固定端口,可以在main函数启动服务时添加端口复用参数:

async def main(port):
    server = await asyncio.start_server(
        handle_echo, HOST, port,
        reuse_port=True,  # Linux下生效
        reuse_address=True  # Windows、Linux都生效
    )
    addr = server.sockets[0].getsockname()
    print(f'SERVER: Serving on {addr[0:2]}')
    async with server:
        await server.serve_forever()

修改完成后两个测试用例就可以正常执行,不会出现挂起问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 13:15:01