应使用caplog fixture:先调caplog.set_level()设日志级别(如info),再通过caplog.records遍历断言logrecord的message、levelname、name等字段,避免依赖易变的caplog.text。

pytest中如何捕获并断言日志输出
直接用 assert 检查 print() 那种输出不行——日志是通过 logging 模块发往 handler 的,得让 pytest 把日志记录“抓”进内存里再检查。
核心方法是使用 pytest 内置的 caplog fixture,它会自动捕获测试运行期间所有日志记录(默认级别 WARNING 及以上),但可以配置成捕获 DEBUG/INFO 级别。
- 确保被测代码确实调用了
logging.debug()、logging.info()等,而不是只写 print - 在测试函数参数中声明
caplog,pytest 会自动注入 - 若需捕获 INFO 或 DEBUG 日志,测试开始前加
caplog.set_level(logging.INFO) -
caplog.text是拼接后的完整日志字符串(含 level + logger name + message) -
caplog.records是LogRecord对象列表,可精确断言record.levelno、record.message、record.name
断言日志内容时容易忽略的细节
光看 caplog.text 包含某段文字,不等于日志“正确”——可能来自其他模块、或日志格式被 formatter 改动过。
- 不要只用
assert "user created" in caplog.text:如果多个 logger 都输出这串字,无法定位来源 - 优先用
caplog.records遍历断言:any(r.message == "user created" and r.name == "auth.user" for r in caplog.records) - 注意日志级别是否匹配:
logging.warning("x")不会被caplog.set_level(logging.INFO)捕获到,但会被caplog.set_level(logging.WARNING)捕获 - 如果被测函数内部新建了 logger(如
logging.getLogger(__name__)),确保没被 root logger 的配置覆盖
为什么 caplog 有时“抓不到”日志
最常见原因是日志被发到了别的地方:比如 handler 被重定向到文件、或者 root logger 的 level 太高把消息过滤掉了。
- 检查是否手动调用了
logging.basicConfig(level=...)——它会静默设置 root logger,影响caplog行为 - 确认被测模块没有调用
logger.propagate = False,否则日志不会上浮到 root,caplog就收不到 - 避免在测试外全局修改
logging.getLogger().setLevel(...),这会影响所有后续测试 - 如果用了 structlog 或 loguru,
caplog默认不生效,需额外适配(比如 loguru 要用caplog+loguru的 sink 机制)
一个可靠的小示例
假设你有如下函数:
import logging
<p>logger = logging.getLogger(<strong>name</strong>)</p><p>def create_user(name: str):
logger.info("Creating user: %s", name)
return {"id": 1, "name": name}</p>
对应测试应这样写:
import logging
import pytest
<p>def test_create_user_logs_correctly(caplog):
caplog.set_level(logging.INFO) # 必须设,否则 INFO 不被捕获
result = create_user("alice")</p><pre class="brush:python;toolbar:false;">assert result["name"] == "alice"
assert len(caplog.records) == 1
assert caplog.records[0].levelname == "INFO"
assert caplog.records[0].message == "Creating user: alice"
assert caplog.records[0].name == "__main__" # 注意这里实际是模块名,非 __main__
真正难的不是写断言,而是确保日志路径没被拦截、level 设置得当、以及 record 属性取对——这些地方一错,caplog 就像没开开关。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











