测试用例互相影响源于共享可变状态,如全局变量、类属性、单例实例或模块级缓存未清理,导致后续测试读取脏数据。

为什么测试用例会互相影响
Python 测试(尤其是用 unittest 或 pytest)中,状态污染最常见于共享的可变对象:全局变量、模块级缓存、类属性、单例实例、或未清理的临时文件/数据库连接。一旦某个测试修改了这些状态,后续测试就可能读到“脏数据”,导致偶发失败、结果不一致,甚至本地能过 CI 报错。
典型现象包括:test_a 单独跑通过,test_b 单独跑也通过,但一起运行时 test_b 失败;或者测试顺序调换后结果变化。
用 setUp / tearDown 清理模块级副作用
当被测代码依赖模块级状态(比如 requests.adapters.DEFAULT_ADAPTERS 被 patch 过、或自定义的 CONFIG 字典被修改),仅靠实例方法级的 setUp 不够——因为模块在测试间不会重载。
- 对
unittest:在setUp中备份关键模块变量,在tearDown中恢复,例如:def setUp(self): self._orig_config = copy.deepcopy(my_module.CONFIG) <p>def tearDown(self): my_module.CONFIG.clear() my_module.CONFIG.update(self._orig_config) - 避免在
setUpClass中做不可逆初始化(如启动全局服务),除非你明确在tearDownClass中彻底关闭它 - 用
pytest的autouse=True, scope="function"fixture 更安全,它天然隔离函数级生命周期
警惕 @patch 和 mock 的作用域泄漏
unittest.mock.patch 如果用装饰器方式打在类或方法上,且没正确停止,mock 对象可能残留并影响后续测试。更隐蔽的是,用 patch.object 修改类属性后,若没还原,所有后续测试看到的都是 patched 版本。
立即学习“Python免费学习笔记(深入)”;
- 优先使用上下文管理器语法,确保退出时自动 stop:
with patch('my_module.some_func') as mock_f: mock_f.return_value = 42 # ... test code # mock_f 自动 unpatched - 如果必须用装饰器,加上
autostart=False并手动控制 start/stop,尤其在参数化测试中 - 检查是否误用了
patch.multiple但只 stop 了其中一部分
用 pytest --tb=short -x --maxfail=1 快速定位污染源头
状态污染问题往往有“顺序敏感性”。与其逐行读代码,不如让测试自己暴露依赖关系:
- 用
pytest --random-order(需装pytest-randomly)反复跑,看哪些测试组合容易失败 - 运行
pytest test_module.py::TestClass::test_one -s,再紧接着跑pytest test_module.py::TestClass::test_two -s,观察test_two的前置状态是否异常 - 在疑似污染点加日志:
print(f"[{self.__class__.__name__}] CONFIG = {my_module.CONFIG}"),对比单独跑和连跑时输出差异
真正难缠的污染常藏在 import 时就执行的代码里——比如某个模块顶部写了 logging.basicConfig(),或初始化了一个全局 threading.local() 实例。这类问题不会出现在测试方法内,得去查 __init__.py 和顶层语句。


















