侧边栏壁纸
博主头像
一朵云的博客博主等级

拥抱生活,向阳而生。

  • 累计撰写 108 篇文章
  • 累计创建 28 个标签
  • 累计收到 7 条评论

目 录CONTENT

文章目录

pytest -- pytest-timeout插件的介绍和使用

一朵云
2023-12-14 / 0 评论 / 0 点赞 / 171 阅读 / 5862 字

pytest-timeout插件的介绍和使用

简介:

pytest-timeout 是非常实用的 pytest 插件,它可以为你的测试设置 超时时间防止测试卡死或运行太久。这对于自动化测试、CI/CD 流水线、并行测试(如 pytest-xdist)中特别有用。

准备:

插件安装:

pip install pytest-timeout

运行方式:

1、为所有测试设置超时时间(单位:秒)

pytest --timeout=30 --timeout-method=thread

这表示:每个测试最多运行 30 秒,超过则报错。

  • aignal:(默认):使用信号中断测试(在 Unix 系统上)。使用 signal 方法在 Windows 上运行测试,当测试超时时,可能不会按预期终止测试,从而导致测试挂起或卡死。
  • thread:使用线程检测超时(跨平台,但不强制中断)。通过线程的方式来监控测试执行的时间,并在超时时中断测试,这种方法是跨平台兼容的,适用于所有操作系统,包括 Windows。

2、为单个测试设置超时时间

使用 @pytest.mark.timeout() 装饰器:

这个 @pytest.mark.timeout() 标记的优先级比 1、4步骤更高!

import pytest
import time

@pytest.mark.timeout(5)  # 这个测试最多运行 5 秒
def test_slow_function():
    time.sleep(6)  # 会超时

3、禁用某个测试的超时机制

@pytest.mark.timeout(None)
def test_no_timeout():
    time.sleep(100)  # 不会超时
  • Unix 系统(Linux/macOS)可以完美支持 signalthread 两种模式。
  • Windows 只能使用 thread 模式,且对 None 的处理存在缺陷。(可通过超大超时值替代 None

4、在 pytest.ini 中设置默认超时时间

[pytest]
timeout = 5
timeout_method = thread  # 推荐在 Windows 上使用 thread

所有测试用例默认超时时间为5秒!

示例:

pytest.ini 文件

[pytest]

# pytest-xdist 插件运行指令 -n auto
addopts = -v -n 2 --capture=no
# 禁用xdist
# addopts = -p no:xdist

# pytest-timeout 超时配置
timeout = 3
timeout_method = thread

*.py 文件

import pytest
import time


# 这个测试会超时(运行 5 秒 > 3 秒)
def test_too_slow():
    print("Starting slow test...")
    time.sleep(5)
    assert True


# 这个测试正常(运行 1 秒 < 3 秒)
def test_fast():
    print("Starting fast test...")
    time.sleep(1)
    assert True


# 这个测试被标记超时时间10秒,优先级高于ini文件,故用例不会超时
@pytest.mark.timeout(10)
def test_long_but_allowed():
    print("Starting long test (10s allowed)...")
    time.sleep(7)
    assert True


# @pytest.mark.timeout(None) 禁用超时,但在windows系统中该方法存在问题可能不生效,仅在linux系统中能生效。
# windows 中建议改为比较大的数来巧妙的“禁用超时”。
@pytest.mark.timeout(600)
def test_no_timeout():
    print("This test has no timeout...")
    time.sleep(5)
    assert True

运行结果:

image-vgkb.png

如果不安装使用 pytest-xdist 插件,运行结果就截然不同了!Timeout !!!

test\python_timeout_demo\test_pytest_timeout.py +++++++++++++++ Timeout +++++++++++++++

问题🧠:

​ ​ 在 Windows 系统上使用 pytest-timeout 时确实会遇到比 Linux/macOS 更多的问题,主要原因并不是 pytest-timeout 本身设计不好,而是因为Windows 与 Linux 的线程/信号机制不同。


1、Windows 与 Linux 的线程/信号机制不同

①. timeout_method = signal 不支持 Windows ❌

  • signal 是 Unix 系统(Linux/macOS)中的中断机制,可以精确地中断主线程。
  • Windows 没有 signal 这个机制,所以 pytest-timeout 在 Windows 上只能使用 thread 方法。

②. timeout_method = thread 在 Windows 上存在局限性 ⚠️

  • 这是 Windows 上唯一可用的超时中断方法。
  • 它通过创建一个“监控线程”来中断主线程。
  • 这个方法无法中断某些阻塞调用,比如:
    • time.sleep()
    • input()
    • socket.recv()
    • os.wait()
    • 等等

这些调用在 Windows 上是“不可中断的系统调用”,即使你设置了超时,它也会继续执行,直到完成。

2、单进程模式下, time.sleep() 在 Windows 上无法被中断?

这是 Python 的 time.sleep() 在 Windows 上的实现机制决定的:

  • 它是基于 Windows API 的 Sleep() 函数(来自 kernel32.dll)。
  • 这个函数是 阻塞式调用,不支持中断。
  • 即使你从另一个线程尝试中断它,它也会“无视中断”继续休眠,直到时间结束。

✅ 如果使用了 pytest-xdist 插件多进程运行,就比较巧妙的规避了该问题。

3、Linux/macOS 上为何表现更好?

  • 在 Linux/macOS 上,time.sleep() 底层调用的是 nanosleep()usleep(),它们可以被信号中断。
  • pytest-timeout 使用 signal 方法发送一个 SIGALRM 信号,就能打断这些调用。
  • 所以在 Linux 上,pytest-timeout 的行为更“智能”和“可控”。
0

评论区