caplog默认只捕获warning及以上级别日志;需调用set_level()显式设置info或debug级别,且可指定logger名称以匹配被测代码的日志器。

caplog能捕获哪些日志级别
caplog 默认只捕获 WARNING 及以上级别(WARNING、ERROR、CRITICAL),不会自动记录 INFO 或 DEBUG。这不是 bug,而是 pytest 的默认日志级别设置。
- 要捕获
INFO,必须显式设置caplog.set_level("INFO") - 捕获
DEBUG同理:需调用caplog.set_level("DEBUG") - 级别名不区分大小写,但推荐用全大写字符串(如
"INFO")保持一致性 -
set_level作用于当前测试函数内生效,不影响其他测试
如何断言某条日志是否以指定级别发出
核心是检查 caplog.records 列表中是否存在匹配的 LogRecord 对象:
-
caplog.records是logging.LogRecord实例列表,每个含.levelno和.levelname属性 - 推荐用
any()配合生成器表达式判断,比遍历更简洁
def test_login_failure_logs_warning(caplog):
caplog.set_level("WARNING")
do_login(username="bad", password="wrong") # 假设该函数会发 warning
assert any(record.levelno == logging.WARNING for record in caplog.records)
# 或等价地:
# assert any(record.levelname == "WARNING" for record in caplog.records)
- 直接比较
record.levelno更可靠(避免拼写/大小写误差) - 不要依赖
caplog.text或caplog.messages做级别断言——它们只存格式化后的内容,丢失原始级别信息
为什么断言失败却看不到日志内容
常见现象:测试报错说“no logs of level WARNING”,但你确信代码里写了 logger.warning(...)。
可能原因包括:
- 忘记调用
caplog.set_level(...),导致日志被过滤掉 - 日志器名字不匹配:被测代码用了
logging.getLogger("myapp.api"),但测试没指定 logger 名,caplog默认只捕获根 logger;此时应写caplog.set_level("INFO", logger="myapp.api") - 使用了第三方库的日志器(如
requests),而它默认关闭了日志传播(propagate=False),caplog捕获不到;可临时设logging.getLogger("requests").propagate = True - 测试运行时启用了更高优先级的全局日志配置(如
--log-level=ERRORpytest 参数),会压制caplog的级别设置
想同时验证级别、消息和模块位置怎么办
caplog.records 提供完整上下文,可组合断言:
def test_db_connection_error_logs_critical(caplog):
caplog.set_level("CRITICAL")
connect_to_db("invalid://url")
record = next((r for r in caplog.records if r.levelno == logging.CRITICAL), None)
assert record is not None
assert "connection refused" in record.message.lower()
assert record.module == "database"
assert record.lineno > 0
- 用
next(..., None)安全取第一条匹配记录,避免IndexError -
record.message是格式化前的原始消息(不含时间、级别前缀等),适合做子串断言 -
record.module和record.lineno对定位问题非常有用,但注意它们依赖代码是否在调试模式下运行(通常没问题)
日志级别的断言容易卡在「以为发了,其实没发」或「发了但没捕获到」——关键永远是先确认 caplog.set_level 的参数和作用域是否精准匹配你的日志器配置。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











