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

如何排查GitLab CI中pytest单TLS端口测试超时问题?

问题描述

我用pytest测试一个监听多端口的应用,在GitLab CI流水线运行时,除了一个TLS端口的测试外,其余测试都通过,但这个测试会超时且没有任何pytest输出。我已经加了-vvv参数提升日志级别,但还是看不到测试执行的具体情况。

当前测试运行命令

python3 -m pytest -vvv /tests

测试代码

def send_tcp(hostname, port, message, protocol=socket.SOCK_STREAM):
    s = socket.socket(socket.AF_INET, protocol)
    s.connect((hostname, int(port)))
    s.sendall(bytes(message, "utf-8"))
    s.shutdown(socket.SHUT_WR)
    s.close()


def send_tcp_TLS(hostname, port, message):
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    context.verify_mode = ssl.CERT_OPTIONAL
    context.check_hostname = False
    context.load_verify_locations(cafile="rootCA.crt")
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ssl_sock = context.wrap_socket(s, server_hostname=hostname)
    ssl_sock.connect((hostname, port))
    ssl_sock.sendall(bytes(message, "utf-8"))
    ssl_sock.shutdown(socket.SHUT_WR)
    ssl_sock.close()


def receive(message):
    r = redis.Redis(host="redis-input", port=6378)
    found = False
    timeout = time.time() + 20
    while not found and time.time() < timeout:
        for value in r.lrange("inq", 0, -1):
            decoded = value.decode("utf-8")
            parsed = json.loads(decoded)
            if message in parsed["raw"]:
                found = True
        if not found:
            time.sleep(0.1)
    assert found


def send_and_receive(hostname, port, message, protocol=socket.SOCK_STREAM, tls=False):
    if tls:
        send_tcp_TLS(hostname, port, message)
    else:
        send_tcp(hostname, port, message, protocol)
    receive(message.rstrip())

def test_z_tls():
    send_and_receive("test-z-tls", 20516, "Test TLS z message", tls=True)

由于其他端口的TLS/非TLS测试都正常,我觉得问题不在测试代码本身。有没有办法获取pytest运行时的排查数据,定位超时原因?


排查方案

以下是几种能获取更多排查数据的实用方法:

1. 调整pytest参数,捕获实时输出

  • 使用-s关闭pytest的输出捕获,让测试过程中的print/log直接输出到控制台:
    python3 -m pytest -vvv -s /tests
    
  • 搭配-x让测试在第一个失败时立即停止,避免等待其他测试执行,同时用--tb=short简化错误栈输出:
    python3 -m pytest -vvv -s -x --tb=short /tests
    
  • 启用日志实时输出,直接打印测试过程中的DEBUG级日志:
    python3 -m pytest -vvv -s --log-cli-level=DEBUG /tests
    

2. 在测试代码中添加关键步骤日志

在send_tcp_TLS和receive函数中插入调试日志,记录每个阶段的执行状态,精准定位卡住的环节:

import logging
logging.basicConfig(level=logging.DEBUG)

def send_tcp_TLS(hostname, port, message):
    logging.debug(f"启动TLS连接: {hostname}:{port}")
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    context.verify_mode = ssl.CERT_OPTIONAL
    context.check_hostname = False
    logging.debug("加载根证书文件")
    context.load_verify_locations(cafile="rootCA.crt")
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    logging.debug("创建基础Socket完成")
    ssl_sock = context.wrap_socket(s, server_hostname=hostname)
    logging.debug("开始TLS握手")
    ssl_sock.connect((hostname, port))
    logging.debug("TLS连接建立成功,发送测试消息")
    ssl_sock.sendall(bytes(message, "utf-8"))
    ssl_sock.shutdown(socket.SHUT_WR)
    ssl_sock.close()
    logging.debug("TLS连接关闭")

def receive(message):
    logging.debug(f"开始从Redis查询目标消息: {message}")
    r = redis.Redis(host="redis-input", port=6378)
    found = False
    timeout = time.time() + 20
    while not found and time.time() < timeout:
        elapsed = time.time() - (timeout - 20)
        logging.debug(f"第 {elapsed:.1f} 秒,查询Redis队列")
        values = r.lrange("inq", 0, -1)
        logging.debug(f"Redis返回 {len(values)} 条消息")
        for value in values:
            decoded = value.decode("utf-8")
            parsed = json.loads(decoded)
            if message in parsed["raw"]:
                logging.debug("找到匹配消息")
                found = True
                break
        if not found:
            time.sleep(0.1)
    assert found, f"超时未找到目标消息: {message}"

3. 在GitLab CI中添加前置环境检查

在测试步骤前,先验证目标端口的连通性和TLS握手是否正常,排除环境层面的问题:

test:
  script:
    # 检查端口是否开放
    - nc -zv test-z-tls 20516
    # 手动发起TLS连接,查看握手详情
    - openssl s_client -connect test-z-tls:20516 -CAfile rootCA.crt
    # 运行测试
    - python3 -m pytest -vvv -s /tests

4. 给测试设置超时并捕获状态

使用pytest-timeout插件给单个测试设置更短的超时,同时强制输出超时前的日志:

  • 先安装插件:pip install pytest-timeout
  • 运行测试时指定全局超时:
    python3 -m pytest -vvv -s --timeout=10 /tests
    
  • 或者在测试代码中单独标记:
    import pytest
    
    @pytest.mark.timeout(10)
    def test_z_tls():
        send_and_receive("test-z-tls", 20516, "Test TLS z message", tls=True)
    

5. 查看GitLab CI完整作业日志

在GitLab作业页面中,切换到"完整日志"视图,检查标准错误输出(stderr)——有时候pytest的错误信息会输出到这里,而不是标准输出(stdout)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:40:05