pytest-timeout插件提供全局(--timeout)和单例(@pytest.mark.timeout)超时控制,支持signal(默认,Linux/macOS)和thread(Windows/阻塞C扩展)两种中断模式,未安装则配置无效。

用 pytest-timeout 插件设置全局或单个用例超时
pytest 本身不内置超时机制,必须借助 pytest-timeout 插件。它支持两种粒度:整个测试会话(--timeout)和单个用例(@pytest.mark.timeout)。不装这个插件,任何超时配置都无效。
安装命令:pip install pytest-timeout
常见错误现象:加了 @pytest.mark.timeout(5) 却没反应——大概率是插件没装,或 pytest 版本太老(需 ≥ 6.0)。
- 全局超时:运行
pytest --timeout=10,所有用例超过 10 秒即被 SIGTERM 中断(Linux/macOS)或 TerminateProcess(Windows) - 单用例超时:在测试函数上加
@pytest.mark.timeout(3),优先级高于全局值 - 超时后默认行为是失败并报错
TimeoutError: Test took longer than 3.0 seconds,不会静默跳过
超时模式选 signal 还是 thread?
pytest-timeout 提供两种中断机制:signal(默认,Unix-like 系统)和 thread(Windows 兼容,但有局限)。选错会导致超时不生效或误杀进程。
使用场景决定选择:
- Linux/macOS + 普通用例 → 用默认
signal模式,开销低、响应快 - Windows 或用例里调用了阻塞型 C 扩展(如某些数据库驱动、
time.sleep()在子线程中)→ 必须显式指定--timeout-method=thread -
thread模式无法中断正在执行的 C 函数(比如requests.get()底层阻塞),此时需配合requests自身的timeout=参数
示例:在 Windows 上跑带网络请求的测试,应写成 pytest --timeout=5 --timeout-method=thread,再在代码里补 requests.get(url, timeout=3)。
@pytest.mark.timeout 的参数细节与陷阱
@pytest.mark.timeout 看似简单,但三个参数容易混淆:数值是秒数(float)、method 可覆盖全局模式、func_only 控制是否包含 fixture 执行时间。
- 基础写法:
@pytest.mark.timeout(2.5)—— 仅限制测试函数体执行时间 - 含 fixture:
@pytest.mark.timeout(5, func_only=False)—— 超时计时从 fixture setup 开始,到 teardown 结束 - 强制模式:
@pytest.mark.timeout(1, method="thread")—— 此用例强制走 thread 模式,无视命令行设置 - 陷阱:若 fixture 里有长时间阻塞(如启动本地服务),又没设
func_only=False,超时将不覆盖那段耗时,导致“明明设了 1 秒却跑了 20 秒还没中断”
替代方案:不用插件,靠信号或 threading 手动控制
极少数场景(如 CI 环境禁止装第三方插件、或需精确控制中断逻辑),可手动实现超时。但复杂度高,且无法优雅处理 fixture。
核心思路是:在测试函数内起一个监控线程或注册信号处理器,超时后抛出异常或调用 os._exit()(⚠️危险,会跳过 teardown)。
示例(简化版 thread 方案):
import threading
import time
<p>def test_with_manual_timeout():
result = []
def target():
time.sleep(4) # 模拟慢操作
result.append("done")</p><pre class="brush:php;toolbar:false;">t = threading.Thread(target=target)
t.start()
t.join(timeout=2)
if t.is_alive():
raise TimeoutError("Manual timeout hit")
assert result == ["done"]
注意:这种写法无法中断 target 中的阻塞调用(如 socket.recv()),且绕过了 pytest 的 fixture 生命周期管理。除非万不得已,别这么干。
真正难的不是设超时,而是判断哪段该被超时覆盖——fixture?网络请求?还是纯计算?漏掉关键环节,超时就形同虚设。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











