pytest-randomly 默认无法稳定检测耦合,因其每次生成新随机种子且在每个测试前重置random.seed(),掩盖全局状态残留问题;需禁用自动重置并固定种子才能可靠暴露模块级变量、共享i/o和未隔离monkeypatch等隐式依赖。

pytest-randomly 能有效暴露测试间隐式依赖,但默认配置不足以稳定复现耦合问题——必须显式禁用种子重置并配合 --randomly-seed 才能可靠验证。
为什么默认 pytest-randomly 无法稳定检测耦合?
pytest-randomly 默认每次运行都生成新随机种子,导致失败不可复现;更关键的是,它会在每个测试函数执行前重置状态(如 random.seed()),掩盖了因全局状态残留引发的顺序敏感问题。
- 现象:
pytest偶然失败,但加--randomly-seed=123后又不失败,误判为“偶发” - 根本原因:插件在每个 test function 开始时调用
random.seed(),清除了上一个测试对random模块的影响,但没清除其他共享状态(如模块级变量、缓存、临时文件) - 正确做法:关闭自动 seed 重置,让随机化真正作用于整个测试生命周期
如何禁用自动 seed 重置并固定随机序列?
通过配置关闭插件的内部 seed 干预,仅靠 pytest 的全局随机打乱机制暴露真实耦合。
- 在
pyproject.toml中添加:[tool.pytest.ini_options] addopts = [ "--randomly-dont-reset-seed", "--randomly-seed=42", ]
- 或命令行直接运行:
pytest --randomly-dont-reset-seed --randomly-seed=42 -
--randomly-dont-reset-seed是关键:它阻止插件在每个 test 函数入口调用random.seed(),使随机数生成器状态跨测试延续 - 固定
--randomly-seed值才能复现同一执行顺序,否则无法定位具体哪两个测试发生了干扰
哪些耦合类型最容易被这个配置暴露?
该配置对以下三类隐式依赖最敏感:
-
模块级 mutable state:比如在
conftest.py或测试模块顶部定义了CACHE = {},多个测试读写它却无清理 -
共享 I/O 资源:如多个测试共用同一临时目录
/tmp/testdata,但只在第一个 test 创建、最后一个 test 清理 -
未隔离的 monkeypatch:一个 test 用
monkeypatch.setattr(os, "getcwd", lambda: "/fake")却没作用域限制,影响后续 test
注意:pytest-randomly 本身不改变 fixture 生命周期,所以 @pytest.fixture(scope="session") 这类长生命周期对象仍是主要风险点。
如何确认是耦合而非随机失败?
单靠一次失败不能下结论,需交叉验证:
- 用同一 seed 连续运行两次:
pytest --randomly-dont-reset-seed --randomly-seed=42→ 失败 → 再次运行 → 必须失败,否则不是顺序耦合 - 手动构造可疑顺序:提取失败日志中的 test 名,用
pytest test_a.py::test_x test_b.py::test_y显式按该顺序运行,看是否必现 - 加
--tb=short -v观察失败是否总发生在某个 test 执行后(例如test_user_create总是让test_user_list失败),这就是强耦合信号
真正难处理的是那些只在特定 seed 下触发的竞态,比如两个 test 都操作同一个 sqlite 内存数据库但没加锁——这种需要结合 --randomly-seed 多轮扫描,而不是指望一次跑出结果。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











