pytest 开启 log_cli 后,redirect_stdout 突然接不到输出
一条实时日志会触发捕获暂停与恢复,覆盖测试后来装上的输出流。对依赖重定向的测试关闭实时显示,保留输出与日志的正向断言。
测试中使用 redirect_stdout(StringIO()),日志出现之前能接住输出,一条 warning 之后却收不到后续 print();文本反而出现在 pytest 的 Captured stdout call。若开启了 log_cli=true,应检查实时日志触发的捕获暂停与恢复。
pytest 9.1.1 的实时日志处理器会临时停用输出捕获。相关 SysCapture 恢复时重新装回 pytest 自己的流,没有恢复暂停前由测试新换上的流,外层 redirect_stdout 或 CLI 测试工具因此被绕过。这不等于命令没有真正输出,也不是将日志级别改低就能保证消失的问题。
把依赖重定向的测试放在关闭实时日志的运行中
如果这些测试不需要终端实时日志,可以只为这一组调整调用:
python -m pytest tests/test_cli.py -o log_cli=false
tests/test_cli.py 换成实际的输出测试文件。这个参数关闭终端实时日志,不等于禁用整个 logging 插件;原来的日志捕获与 caplog 断言仍需正常保留。其他任务确实需要实时日志时,不必全项目关闭。
下面是适合确认输出通路的独立测试形状:
import contextlib
import io
import logging
def test_redirect_keeps_output():
stream = io.StringIO()
with contextlib.redirect_stdout(stream):
logging.getLogger("cli.probe").warning("diagnostic record")
print("expected output")
assert stream.getvalue() == "expected output\n"
这里用正向的完整输出断言。只写 assert "--debug" not in result.output,空输出也会通过,不能证明 CLI 没有泄漏那个选项。实际 CLI 测试还要检查退出码、关键正常文本以及必要的 stderr 约定。
不要在 conftest 里长期替换私有捕获类
直接覆盖 _pytest.capture 的 suspend/resume 方法,会影响嵌套捕获、文件描述符捕获、capsys 与其他插件,不能用一条测试变绿证明全套交互正确。先选择范围明确的运行配置,保持测试自己的流上下文正常结束。
这与后台线程注册 logger 导致字典迭代错误不同。那条路径可能在关闭实时日志后仍发生,本题则需要实时日志暂停捕获这个触发环节;不要把两者的取舍混用。
保存原 CI 命令和日志要求。复查输出、日志断言与测试数量,确认关闭的是这组任务的实时显示。将来恢复 log_cli=true 前,用原重定向与日志顺序重新检查,不要只凭 pytest 版本号升高就移除这项临时配置。


