pytest-rerunfailures通过pip安装即可使用,核心功能是失败后自动重试,适用于网络抖动等偶发问题;支持命令行--reruns和装饰器@flaky精准控制,需配合--reruns-verbose查看完整失败日志,并注意与requests/aiohttp重试逻辑职责分离。

pytest-rerunfailures 安装和基础用法
直接 pip 安装就能用,不需要改测试代码结构。pytest-rerunfailures 的核心价值是“失败后自动重试”,特别适合因网络抖动、第三方服务响应慢导致的偶发失败。
安装命令:
pip install pytest-rerunfailures
- 运行时加参数:
--reruns 3表示每个失败用例最多重试 3 次(含首次执行,实际最多跑 4 次) - 只对失败的用例重试,跳过、报错、成功的都不动
- 默认不重试
setup或teardown阶段失败——这点容易被忽略,网络测试里如果setUp里连数据库超时,整个用例会直接报错,不会重试 - 重试之间默认无延迟,高频重试可能加重目标服务压力,建议配合
--reruns-delay 2加 2 秒间隔
怎么只对特定测试启用重试
不是所有失败都该重试。比如断言逻辑错误、数据校验失败,重试毫无意义,反而掩盖真问题。应该只对明确知道是外部依赖不稳的测试加策略。
- 用装饰器精准控制:
@pytest.mark.flaky(reruns=2, reruns_delay=1)
这样只有打上@pytest.mark.flaky的用例才参与重试 - 装饰器参数优先级高于命令行参数,适合混合场景(比如大部分用例不重试,个别网络请求测试重试 2 次)
- 注意:装饰器必须写在
@pytest.mark.parametrize外层,否则重试逻辑不生效 - 如果用
pytest.mark.skipif或pytest.mark.xfail,它们和flaky共存时行为复杂,建议避免嵌套
重试失败后怎么定位真正的问题
重试成功了,不代表问题不存在;重试全失败,也不代表一定是代码 bug。关键看每次失败的堆栈是否一致。
- 默认情况下,
pytest-rerunfailures只显示最后一次失败的 traceback,前面几次失败日志被吞掉了——这是最常被吐槽的坑 - 加参数
--reruns-verbose才能看到每次重试的完整输出,包括哪次失败、错误类型、堆栈差异 - 常见现象:
ConnectionError第一次出现,第二次变成TimeoutError,第三次是JSONDecodeError——说明服务端返回了异常响应,不是单纯网络问题,得查接口稳定性 - 重试后通过,但日志里有
WARNING: urllib3.connectionpool: Retrying (Retry(...)),说明底层 requests 已经在重试,再套一层 pytest 重试可能冗余甚至干扰
和 requests / aiohttp 网络重试逻辑怎么共存
Python 网络库本身就有重试机制,和 pytest 插件叠在一起容易互相干扰,尤其在 timeout 和 retry 策略上冲突。
- requests 默认不重试任何错误,但如果你用了
urllib3.util.retry.Retry配置了重试,那它会在单次测试内先重试若干次,之后才轮到 pytest 插件——两次重试叠加可能导致总耗时远超预期 - aiohttp 的
ClientSession默认无重试,但很多人会自己封装带指数退避的请求函数,这种自定义逻辑和 pytest 重试并存时,要确认是否重复处理了同一类错误(比如都对503做重试) - 建议:网络库层面只做“单次请求内”的瞬时错误恢复(如连接拒绝、DNS 失败),pytest 层面负责“测试维度”的跨请求恢复(如服务临时不可用),两者职责分开
- 一个简单判断标准:如果把测试里的
requests.get(...)换成time.sleep(5); requests.get(...)就能稳定通过,那更适合用 pytest 重试;如果换 sleep 没用,大概率是网络库配置或服务端问题
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











