caplog for log messages in PyTest - Build an Automation Script
import logging import pytest # Function to test def greet(name): logger = logging.getLogger('myapp') logger.info(f'Greeting {name}') return f'Hello, {name}!' # Test function using caplog def test_greet_logs_message(caplog): with caplog.at_level(logging.INFO): result = greet('Alice') assert result == 'Hello, Alice!' assert 'Greeting Alice' in caplog.text # Optional: check log level of the first record assert caplog.records[0].levelname == 'INFO'
The greet function logs an info message with the name. The test test_greet_logs_message uses the caplog fixture to capture logs.
Inside the test, caplog.at_level(logging.INFO) sets the log level to INFO to capture info messages. The function is called with 'Alice'.
Assertions check that the function returns the expected greeting string and that the log message 'Greeting Alice' appears in the captured logs. Additionally, it verifies the log level of the first captured record is INFO.
This approach ensures logs are captured and verified without printing them, making tests clean and reliable.
Now add data-driven testing with 3 different names to verify log messages for each
Practice
caplog in pytest tests?Solution
Step 1: Understand caplog's role in pytest
caplog is a pytest fixture used to capture log messages during tests.Step 2: Identify the main use
It allows assertions on log output to verify correct logging behavior.Final Answer:
To capture and check log messages generated during test execution -> Option AQuick Check:
caplog = capture logs [OK]
- Confusing caplog with mocking tools
- Thinking caplog measures time
- Assuming caplog generates reports
caplog to capture only WARNING level logs in a test function?Solution
Step 1: Recall caplog syntax for setting log level
caplog uses a context manager methodat_level()to set the log level temporarily.Step 2: Match correct syntax
The correct syntax iswith caplog.at_level(logging.WARNING):to capture warnings.Final Answer:
with caplog.at_level(logging.WARNING): -> Option DQuick Check:
Use caplog.at_level() for log level [OK]
- Trying to set level by assignment
- Using non-existent caplog methods
- Forgetting to use 'with' for context
import logging
def test_log_message(caplog):
with caplog.at_level(logging.INFO):
logging.info('Test started')
assert 'Test started' in caplog.textSolution
Step 1: Analyze log capture level
The code sets caplog to capture INFO level logs, which includes the info message logged.Step 2: Check assertion on caplog.text
The message 'Test started' is logged and appears in caplog.text, so the assertion passes.Final Answer:
The assertion passes because the log message is captured -> Option AQuick Check:
Logged message in caplog.text = assertion pass [OK]
- Assuming caplog.text is empty without at_level
- Confusing log levels and capture
- Forgetting to import logging
import logging
def test_error_log(caplog):
caplog.at_level(logging.ERROR)
logging.error('An error occurred')
assert 'An error occurred' in caplog.textSolution
Step 1: Check usage of caplog.at_level()
caplog.at_level() is a context manager and must be used with 'with' keyword.Step 2: Identify missing 'with' statement
The code calls caplog.at_level() without 'with', so it does not set the level properly.Final Answer:
caplog.at_level() must be used as a context manager with 'with' -> Option BQuick Check:
Use 'with caplog.at_level()' not just call [OK]
- Calling caplog.at_level() without 'with'
- Checking wrong caplog attribute
- Misunderstanding log level capture
caplog to check this behavior?Solution
Step 1: Understand the requirement
We want to check that the warning message logs only once when condition is true, not when false.Step 2: Analyze options for correct caplog usage
def test_warn_log(caplog): with caplog.at_level(logging.WARNING): my_function(True) my_function(False) assert caplog.text.count('Warning triggered') == 1 uses one context capturing both calls and asserts the warning appears exactly once, matching the requirement.Step 3: Why other options fail
def test_warn_log(caplog): with caplog.at_level(logging.WARNING): my_function(True) assert 'Warning triggered' in caplog.text with caplog.at_level(logging.WARNING): my_function(False) assert 'Warning triggered' not in caplog.text: separate contexts but caplog.text accumulates logs, so second assert 'Warning triggered' not in caplog.text fails (message from first call persists); def test_warn_log(caplog): caplog.set_level(logging.WARNING) my_function(True) assert 'Warning triggered' in caplog.text my_function(False) assert 'Warning triggered' not in caplog.text: caplog.set_level does not exist (AttributeError), and accumulation would fail second assert; def test_warn_log(caplog): with caplog.at_level(logging.INFO): my_function(True) assert 'Warning triggered' in caplog.text: tests only True case with INFO level (captures WARNING but no False verification).Final Answer:
def test_warn_log(caplog): with caplog.at_level(logging.WARNING): my_function(True) my_function(False) assert caplog.text.count('Warning triggered') == 1 -> Option CQuick Check:
Use one caplog context and count messages [OK]
- Using multiple caplog contexts losing logs
- Using wrong log level in at_level
- Assuming caplog.set_level exists
Ready to prove it?
Pass the test and this pattern moves unknown
