How to Capture Logging Records with pytest caplog in Python
Capture and assert on logging records in pytest using the built-in caplog fixture.
pip install pytest
Python code
28 linesimport logging
import pytest
def divide(a, b):
"""Divide two numbers and log an error if b is zero."""
if b == 0:
logging.error("Division by zero attempted")
return None
logging.info(f"Dividing {a} by {b}")
return a / b
def test_divide_logs_error(caplog):
with caplog.at_level(logging.ERROR):
result = divide(10, 0)
assert result is None
assert "Division by zero attempted" in caplog.text
# Check specific record details
assert caplog.records[0].levelname == "ERROR"
assert caplog.records[0].name == "root"
def test_divide_logs_info(caplog):
with caplog.at_level(logging.INFO):
result = divide(10, 2)
assert result == 5.0
assert "Dividing 10 by 2" in caplog.messages[0]
Output
2 passed in 0.01s
How it works
The caplog fixture automatically captures all logging messages emitted during a test. By using caplog.at_level(logging.ERROR), you temporarily set the root logger's level so that only ERROR-level messages (and above) are captured. The captured records are accessible via caplog.records, and you can check the entire log output as text with caplog.text. To get just the messages, use caplog.messages. This is a powerful way to verify that your code logs the right errors and informational messages without cluttering your test output.
Common mistakes
- Forgetting to set the log level with caplog.at_level, which can cause messages to be missed
- Using caplog.text when you only need message content and it includes timestamps and levels
- Assuming caplog captures logs from child loggers without setting propagate=True
Variations
- Use caplog.set_level(logging.DEBUG) inside a test to capture DEBUG and above for the whole test.
- Use caplog.clear() between assertions to isolate log records from different sections.
Real-world use cases
- Verifying that an authentication failure logs a security warning before returning an error to the user.
- Checking that a data processing job logs every failed row for later debugging in production.
- Ensuring a payment integration logs the exact reason for declined transactions for audit trails.
Sponsored
More from Testing & modern typing
- Capture stdout and stderr with pytest capsys easy
- Characterization Test for Legacy Python Code medium
- Dataclass with Type Hints Fields in Python easy
- Dependency Injection in Python for Testability easy
- Design Data Helpers with Python TypedDict and Literal easy
- Fix and Test a Regression Bug in Python with Unit Tests easy
Keep learning
Related tutorials and quizzes for this topic.