Python 3.12中pytest本身不支持混沌工程,需组合chaospy、pytest-asyncio等库实现;直接调用os.kill()或subprocess.Popen易致僵尸进程,造成静默失败。

Python 3.12 中 pytest 本身不支持混沌工程,必须靠外部库组合实现——核心是用 pytest 控制测试生命周期,用 chaoslib(如 chaospy 或 chaostoolkit)注入故障,再用 pytest-asyncio 处理异步韧性验证。
为什么直接用 pytest 写混沌测试会静默失败
pytest 是断言驱动的确定性框架,而混沌工程需要主动扰动系统状态(如杀进程、延迟网络、填满磁盘)。直接在 test_*.py 里调用 os.kill() 或 time.sleep(5) 不构成“混沌”,只是普通延时;更危险的是:Python 3.12 对信号处理和子进程重载更敏感,subprocess.Popen 启动的故障注入进程若没显式 wait() 或捕获 BrokenPipeError,会导致 pytest 在 CI 中卡住或跳过后续用例。
- 现象:
pytest test_chaos.py运行到一半无输出、不报错、不退出 - 根本原因:chaos 注入进程变成僵尸,pytest 的 fixture teardown 阶段无法回收资源
- Python 3.12 特别要求:所有子进程必须用
subprocess.run(..., timeout=...)显式设超时,否则默认阻塞
用 pytest + chaospy 实现最小可行混沌测试
chaospy 是轻量级 Python 混沌库(非官方 chaostoolkit),适配 Python 3.12,无需 JVM 或 YAML 编排。它提供 chaospy.network.latency()、chaospy.disk.fill() 等函数,底层用 iptables 和 fallocate,但封装了异常兜底。
- 安装:
pip install "chaospy>=0.8.0" "pytest-asyncio>=0.23.0"(注意 pytest-asyncio 版本必须 ≥0.23.0,否则 async fixture 不生效) - 关键写法:故障注入必须放在
@pytest.fixture(scope="function")中,且加autouse=True,确保每次测试前触发、后清理 - 示例:
import pytest import time from chaospy.network import latency <p>@pytest.fixture(autouse=True, scope="function") def induce_network_delay():</p><h1>注入 300ms 延迟,仅作用于 localhost:8000</h1><pre class="brush:php;toolbar:false;">handle = latency(target="127.0.0.1:8000", delay_ms=300) yield handle.restore() # 自动恢复,避免污染其他测试
def test_api_timeout_handling(): start = time.time()
此处调用你的 HTTP 客户端(如 httpx.get(...))
# 应该捕获 TimeoutException 并降级 assert time.time() - start > 0.25 # 验证延迟确实生效
⚠️ 注意:latency() 在 macOS 上不可用(无 iptables),CI 中需用 Linux runner;Windows 下需启用 WSL2 才能运行。
参数化混沌场景时 ids 必须显式声明
混沌测试天然需要多组扰动组合(如延迟+丢包、CPU 占用+内存压力),但 pytest 默认参数化会把元组转成 test_foo[('100ms', '5%')] 这种不可读名称,导致报告无法定位问题。
- 错误写法:
@pytest.mark.parametrize("delay,loss", [("100ms", "5%"), ("500ms", "20%")]) - 正确写法:必须加
ids,且内容要能反推故障类型:@pytest.mark.parametrize("delay,loss", [("100ms", "5%"), ("500ms", "20%")], ids=["latency_100_loss_5", "latency_500_loss_20"]) - 生成 HTML 报告时务必加
--charset=utf-8,否则中文 ids(如"高延迟低丢包")在 Windows 下会变test_xxx[???]
混沌测试最难的不是注入故障,而是定义“系统仍算可用”的断言边界——比如 API 响应时间从 100ms 涨到 400ms 是否可接受?这得结合业务 SLA 写进 assert,而不是依赖混沌库自带的健康检查。Python 3.12 的 asyncio 改进让异步故障验证更稳,但你也得同步升级 pytest-asyncio,否则 @pytest.mark.asyncio 标记的测试会被完全跳过,连日志都不留。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











