测试含random代码失败的根本原因是随机结果不可重现,解决关键是固定各依赖库的随机种子并用pytest fixture统一管理,同时校验统计性质而非具体值。

为什么测试含 random 的代码总失败?
因为每次运行结果不同,断言必然飘移。核心问题不是“怎么生成随机数”,而是“如何让随机变得可重现”。关键在于控制随机源——不是禁用 random,而是固定它的起点。
用 random.seed() 固定种子的正确姿势
在测试函数开头调用 random.seed(42) 是常见错误:它只影响 random 模块的全局状态,但无法覆盖 numpy.random、secrets 或第三方库(如 torch.manual_seed)。必须按实际依赖精准干预:
- 纯 Python 标准库 → 调用
random.seed(42) - 用了
numpy→ 必须加np.random.seed(42)(注意:新版推荐用np.random.Generator,但老代码仍大量用此) - 涉及
torch→ 补上torch.manual_seed(42) - pytest 中不建议在测试函数内反复调用
seed(),应统一用 fixture 管理
pytest 中用 fixture 封装种子重置逻辑
手动在每个测试里写 random.seed() 易遗漏、难维护。用 pytest fixture 可确保每次测试前环境干净:
@pytest.fixture(autouse=True)
def reset_random_seeds():
random.seed(42)
np.random.seed(42)
if 'torch' in sys.modules:
import torch
torch.manual_seed(42)
这个 reset_random_seeds fixture 带 autouse=True,所有测试自动生效。注意:它不保证线程安全,若测试并行运行(-n auto),需改用模块级或会话级 fixture 并加锁。
校验输出时别只比对“一个值”
即使种子固定,某些算法(如采样、蒙特卡洛)可能因实现细节返回不同但等价的结果。例如:random.sample([1,2,3,4], 2) 在种子 42 下固定返回 [3, 1],但若算法内部先 shuffle 再切片,等价逻辑可能产生相同分布但不同顺序。
- 优先校验统计性质:比如运行 1000 次,检查均值是否落在理论期望 ± 容差内
- 对确定性子步骤单独打桩(mock),比如把
random.random()替换为固定返回序列 - 避免
assert result == expected_list,改用set(result) == set(expected_list)(若顺序无关) - 如果算法本身允许多种合法输出,断言应覆盖“输出空间约束”,而非具体值
最常被忽略的是:种子重置后,若测试中调用了外部服务或读取了未 mock 的文件,那些非随机因素仍会让结果漂移——随机只是变量之一。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











