Writing a pytest function for checking the output on console (stdout)
Categories:
Testing Console Output (stdout) with Pytest

Learn how to effectively write pytest functions to capture and assert against console output (stdout) in your Python applications, ensuring your print statements and logging work as expected.
When developing Python applications, it's common to use print()
statements or logging to output information to the console (standard output, or stdout). While these are often used for debugging, sometimes the console output itself is a critical part of your application's functionality that needs to be tested. Pytest provides robust mechanisms to capture stdout, allowing you to write tests that assert the exact content or format of what your code prints.
Understanding Pytest's capsys
Fixture
Pytest offers a built-in fixture called capsys
(short for 'capture system output') specifically designed for capturing output to sys.stdout
and sys.stderr
. This fixture temporarily redirects stdout
and stderr
during the test execution, allowing you to inspect what your code has printed. After the test, capsys
restores the original streams, ensuring that subsequent tests or the test runner itself are not affected.
sequenceDiagram participant TestRunner participant Pytest participant capsys participant YourCode TestRunner->>Pytest: Run test_my_function() Pytest->>capsys: Request 'capsys' fixture capsys->>Pytest: Provide 'capsys' object Pytest->>YourCode: Execute my_function() YourCode->>stdout: print("Hello") stdout-->>capsys: Captured output Pytest->>capsys: Call readouterr() capsys->>Pytest: Return (stdout_str, stderr_str) Pytest->>Pytest: Assert stdout_str == "Hello\n" Pytest->>capsys: Restore original stdout/stderr Pytest->>TestRunner: Test result
Sequence diagram illustrating how capsys
captures stdout during a pytest run.
Basic Usage of capsys
To use capsys
, you simply include it as an argument in your test function. The fixture provides two main methods: readouterr()
and disabled()
. The readouterr()
method returns a named tuple (out, err)
containing the captured stdout and stderr as strings. The disabled()
method is a context manager that temporarily disables capturing, which can be useful if you need to print something to the actual console during a test for debugging purposes.
# my_module.py
def greet(name):
print(f"Hello, {name}!")
def warn_user(message):
import sys
print(f"WARNING: {message}", file=sys.stderr)
# test_my_module.py
def test_greet_output(capsys):
greet("World")
captured = capsys.readouterr()
assert captured.out == "Hello, World!\n"
assert captured.err == ""
def test_warn_user_output(capsys):
warn_user("Low disk space")
captured = capsys.readouterr()
assert captured.out == ""
assert captured.err == "WARNING: Low disk space\n"
def test_mixed_output(capsys):
print("This is stdout")
import sys
print("This is stderr", file=sys.stderr)
captured = capsys.readouterr()
assert captured.out == "This is stdout\n"
assert captured.err == "This is stderr\n"
print()
statements automatically append a newline character (\n
). When asserting against captured output, always include this newline character in your expected string, unless you explicitly use end=''
in your print()
call.Advanced Scenarios: Disabling Capture and Partial Matches
Sometimes you might need to temporarily disable output capture within a test, or you might only want to check for a substring in the output rather than an exact match. Pytest's capsys
fixture handles these scenarios gracefully. For partial matches, standard string methods like in
or regular expressions can be used on the captured output string.
# test_advanced_output.py
import re
def test_disable_capture(capsys):
print("This will be captured.")
with capsys.disabled():
print("This will go to the actual console.")
print("This will be captured again.")
captured = capsys.readouterr()
assert "This will be captured." in captured.out
assert "This will go to the actual console." not in captured.out
def test_partial_output_match(capsys):
print("User 'john_doe' logged in successfully.")
captured = capsys.readouterr()
assert "logged in successfully" in captured.out
def test_regex_output_match(capsys):
print("Processing file: report_2023_10_26.csv")
captured = capsys.readouterr()
match = re.search(r"report_\d{4}_\d{2}_\d{2}\.csv", captured.out)
assert match is not None
assert match.group(0) == "report_2023_10_26.csv"
capsys.disabled()
in a CI/CD environment. While useful for local debugging, excessive use can clutter build logs and make it harder to pinpoint issues if the output is not expected.