pytest需通过--log-cli-level或pyproject.toml中[tool.pytest.ini_options]下配置log_cli=true、log_cli_level="info"等启用全局日志捕获,避免root logger被basicconfig()干扰,确保日志不刷屏且可追溯。

pytest里怎么让日志不刷屏又全被捕获
默认跑 pytest 时,logging 输出直接打到终端,既干扰断言结果,又没法在失败时回溯上下文。根本解法不是关日志,而是让 pytest 主动接管日志捕获链路——靠 --log-cli-level 或配置项触发内置 handler,而不是自己手动 caplog 每个测试函数。
- 必须在
pytest.ini、pyproject.toml或命令行显式启用,否则caplogfixture 只对单测生效,全局日志仍直出 -
--log-cli-level=INFO是最常用起点;设太低(如DEBUG)会塞爆输出,太高(如WARNING)可能漏掉关键调试信息 - 如果用了
logging.basicConfig()或其他 root logger 初始化,会和 pytest 冲突,导致日志重复或丢失——删掉或改成只初始化非 root 的 logger
pyproject.toml 中正确配置日志捕获
现在主流项目用 pyproject.toml,但很多人填错 section 或 level 类型,导致配置静默失效。
- 必须放在
[tool.pytest.ini_options]下,不是[tool.pytest]或顶层 -
log_cli = true是开关,log_cli_level = "INFO"才指定级别(注意是字符串,不是数字或常量) - 加
log_cli_format = "%(asctime)s %(levelname)s %(name)s: %(message)s"可读性高很多,避免默认的纯消息体 - 如果想同时输出到文件做归档,加
log_file = "pytest.log"和log_file_level = "DEBUG",二者独立控制
[tool.pytest.ini_options] log_cli = true log_cli_level = "INFO" log_cli_format = "%(asctime)s %(levelname)s %(name)s: %(message)s" log_file = "pytest.log" log_file_level = "DEBUG"
为什么 caplog.set_level 不总管用
caplog.set_level() 看似能动态调日志级别,但实际只影响当前测试函数内新创建的 logger,对已存在的模块级 logger(比如 import mymodule 里定义的)无效。
- 常见错误:在测试开头写
caplog.set_level(logging.DEBUG, logger="mymodule"),但mymodule早就在 import 时配好了 handler,级别已锁定 - 真正生效的做法是:在
conftest.py里用pytest_configure钩子,提前对目标 logger 调用logger.setLevel() - 更稳妥的是统一用 pytest 配置的
log_cli_level,它作用于整个 logging 树,无需逐个 patch
测试中验证日志内容的可靠写法
别依赖 caplog.text 做模糊匹配,容易因格式变化或并发日志崩掉断言。
- 用
caplog.records遍历LogRecord对象,检查.levelname、.message、.name字段最稳 - 要断言“某条日志出现且仅出现一次”,别用
in,改用[r for r in caplog.records if r.message == "xxx"]然后 assert len == 1 - 如果日志含变量(如
logger.info("user %s logged in", user_id)),断言时用r.args检查参数元组,比解析 message 字符串更准
basicConfig() 锁死的 root logger,能让所有 pytest 日志配置形同虚设。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











