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

Python Socket服务端Pytest单元测试卡顿问题排查求助

解决Pytest测试Socket服务端时卡住的问题

核心问题

Socket服务端的阻塞式监听循环是罪魁祸首——如果测试代码直接触发这个循环,会把pytest的测试收集进程卡死,一直停在collecting状态。


具体修复方案

1. 拆分服务端代码,避免模块加载时阻塞

把服务端的初始化和启动逻辑分开,只在直接运行服务端脚本时才启动循环:
原SERVER.py如果是模块级直接启动循环,改成:

import socket

def create_server(port=8080):
    """创建并初始化socket服务端"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 允许端口复用,避免测试后端口占用
    sock.bind(('localhost', port))
    sock.listen(5)
    return sock

def run_server(sock):
    """启动阻塞式监听循环"""
    while True:
        conn, addr = sock.accept()
        # 处理客户端请求的逻辑
        conn.send(b"Hello from server")
        conn.close()

if __name__ == '__main__':
    # 只有直接运行脚本时才启动服务
    server_sock = create_server()
    run_server(server_sock)

2. 用后台线程运行服务端

测试代码里绝对不能直接调用run_server,必须把服务端放到后台线程,避免阻塞测试主线程:

import pytest
import threading
import socket
from SERVER import create_server, run_server

@pytest.fixture(scope="module")
def server():
    """启动后台服务端的fixture"""
    sock = create_server()
    # 用守护线程运行,测试结束后自动退出
    server_thread = threading.Thread(target=run_server, args=(sock,), daemon=True)
    server_thread.start()
    yield sock
    # 测试完成后关闭socket
    sock.close()

def test_server_response(server):
    """测试服务端响应"""
    client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_sock.connect(('localhost', 8080))
    client_sock.send(b"Test request")
    response = client_sock.recv(1024)
    assert response == b"Hello from server"
    client_sock.close()

3. 调整pytest-cov参数,避免不必要的覆盖率收集

如果是覆盖率工具导致的额外阻塞,精准指定要收集的模块,不要全局扫描:

pytest test_server.py --cov=SERVER --cov-report=xml

用--cov=SERVER只收集服务端代码的覆盖率,避免扫描测试文件或其他无关模块。

4. 检查测试文件的模块级代码

确保test_server.py里没有在模块顶部直接执行服务端启动逻辑——比如不要在测试文件开头写run_server(create_server()),这种代码会在pytest收集测试用例时就触发阻塞。

5. 处理端口占用问题

测试前检查端口是否被占用,避免服务端启动失败导致卡住:

def check_port_available(port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        return s.connect_ex(('localhost', port)) != 0

@pytest.fixture(scope="module")
def server():
    port = 8080
    if not check_port_available(port):
        pytest.skip(f"Port {port} is occupied, skip test")
    sock = create_server(port)
    # ... 后续启动线程逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 07:12:30