You're seeing the correct behaviour. When capsys.readouterr() is called, it consumes the captured output. Hence, any output to stdout and stderr will no longer show up in the test report. But any new output which you create after this and do not consume will still be reported, so you can get the full output back in the report by simply writing it to the output streams once more:

def test_result_and_stdout(capsys):
    result = method_under_test()
    out, err = capsys.readouterr()
    sys.stdout.write(out)
    sys.stderr.write(err)
    assert out.startswith("Hello")
    assert result == 42
Answer from flub on Stack Overflow
🌐
pytest
docs.pytest.org › en › stable › how-to › capture-stdout-stderr.html
How to capture stdout/stderr output - pytest documentation
The readouterr() call snapshots the output so far - and capturing will be continued. After the test function finishes the original streams will be restored. Using capsys this way frees your test from having to care about setting/resetting output streams and also interacts well with pytest’s own ...
🌐
pytest
docs.pytest.org › en › 7.1.x › reference › reference.html
API Reference — pytest documentation
def test_output(capsys): print("hello") captured = capsys.readouterr() assert captured.out == "hello\n"
🌐
Feldroy
daniel.feldroy.com › posts › til-2023-10-capture-stdout-stderr-with-pytest
TIL: Capture stdout & stderr with pytest
How to capture printed text using pytest, something I wish I knew sooner. ... I wish I knew this earlier, but I know it now thanks to Cody Antunez. Here it is: import sys def test_myoutput(capsys): # or use "capfd" for fd-level # Write some text print("hello") sys.stderr.write("world\n") # Capture the text captured = capsys.readouterr() # Test the captured output, both std and err assert captured.out == "hello\n" assert captured.err == "world\n"
🌐
Waylon Walker
waylonwalker.com › pytest-capsys
Pytest capsys - Waylon Walker
April 5, 2021 - capsys is a builtin pytest fixture that can be passed into any test to capture stdin/stdout.
🌐
pytest
docs.pytest.org › en › 4.6.x › how-to › capture-stdout-stderr.html
Capturing of the stdout/stderr output — pytest documentation
The readouterr() call snapshots the output so far - and capturing will be continued. After the test function finishes the original streams will be restored. Using capsys this way frees your test from having to care about setting/resetting output streams and also interacts well with pytest’s own ...
🌐
Pytest with Eric
pytest-with-eric.com › configuration › pytest-stdout
The Ultimate Guide To Capturing Stdout/Stderr Output In Pytest | Pytest with Eric
December 22, 2023 - In this test, capsys captures the output of print, and we assert that the captured output matches our expectations. This fixture is perfect for ensuring your code’s output is as intended, enhancing your unit testing robustness. In Pytest, alongside capsys, there are fixtures like capfd and capfdbinary which serve similar but slightly different purposes:
🌐
Stack Overflow
stackoverflow.com › questions › 42520432 › stdout-not-captured-by-pytest-capsys-when-using-fixture
python - stdout not captured by pytest capsys when using fixture - Stack Overflow
Can I use capsys with fixtures? ... Fixture is invoked before test is run. In your example, shared_args fixture is reading stdout before otherfunction can write anything to stdout. One way to fix your problem is to make your fixture return a function which can do what you want it to do. You can scope the fixture according to your use case. from __future__ import print_function import pytest from sys import stdout import os @pytest.fixture(scope='function') def shared_args(): def args_func(): args = type('', (), {})() args.out = stdout args.prefix = 'dude:' return args return args_func def otherfunction(message, prefix, stream): print(prefix, message, file=stream) def test_dudesweet(shared_args, capsys): prefix, out = shared_args().prefix, shared_args().out otherfunction('sweet', prefix, out) out, err = capsys.readouterr() assert out == 'dude: sweet\n'
Find elsewhere
🌐
pytest
docs.pytest.org › en › stable › reference › reference.html
API Reference - pytest documentation
def test_output(capsys): print("hello") captured = capsys.readouterr() assert captured.out == "hello\n"
Top answer
1 of 3
7

You could define a helper function in the class that inherits the capsys fixture:

@pytest.fixture(autouse=True)
def capsys(self, capsys):
    self.capsys = capsys

Then call this function inside the test:

out,err = self.capsys.readouterr()

assert out == 'foobar'

Kudos to Michał Krassowski for his workaround which helped me work through a similar problem.

https://github.com/pytest-dev/pytest/issues/2504#issuecomment-309475790

2 of 3
1

Thomas Wright's answer is perfect. I'm just sticking this code block here for my own reference as my search led me here and I'll likely forget this in future! [doing a few things in this so useful reference for me]. If anyone is looking and sees where it can be improved - suggest away!

import os
import pytest

from _pytest.monkeypatch import MonkeyPatch
from unittest import TestCase

# -----------------------------------------------------------------------------
def foo_under_test(inp1):
    """Example of a Method under test"""
    do_some_calcs_here = inp1*2
    get_a_return = ClassCalled.foo_called(do_some_calcs_here)
    return get_a_return


# -----------------------------------------------------------------------------
class ClassUnderTest():
    """Example of a Class contained Method under test"""

    def __init__(self):
        """Instantiate the class"""
        self.var1 = "TestVar"

    def foo_under_test2(self, inp11):
        """The class method under test"""
        return self.var1 + self.foo_called2(inp11)

    def foo_called2(self, inp12):
        """Nominal sub-foo to foo_under_test2"""
        return str(inp12*5)


# -----------------------------------------------------------------------------
class ClassCalled:
    """Example of a class that could be called by foo_under_test"""

    def foo_called(inp2):
        """Sub-foo to foo_under_test"""
        return inp2 * 2


# -----------------------------------------------------------------------------
class MockResponses:
    """Class for holding the mock responses"""

    def foo_called(inp2):
        """**Mock of foo_called**"""
        return inp2*3

    def foo_called2(inp12):
        """**Mock of foo_called**"""
        return str(inp12*4)


# -----------------------------------------------------------------------------
class Test_foo_under_test(TestCase):
    """Test class - means of grouping up tests for a target function

    This one is addressing the individual function (not within a class)
    """

    # ---------------------------------------------------------------
    @pytest.fixture(autouse=True)
    def capsys(self, capsys):
        """Capsys hook into this class"""
        self.capsys = capsys

    def print_to_console(self, strOut):
        """Print strOut to console (even within a pyTest execution)"""
        with self.capsys.disabled():
            print(strOut)

    def setUp(self):
        """Ran by pyTest before running any test_*() functions"""
        self.monkeypatch = MonkeyPatch()

    # ---------------------------------------------------------------

    def test_1(self):
        """**Test case**"""
        def mock_foo_called(inp2):
            return MockResponses.foo_called(inp2)

        mockedFoo = ClassCalled.foo_called  # Need to get this handle here
        self.monkeypatch.setattr(ClassCalled, "foo_called", mock_foo_called)

        x = foo_under_test(1)
        self.print_to_console("\n")
        strOut = "Rtn from foo: " + str(x)
        self.print_to_console(strOut)

        assert x == 6

        # Manually clear the monkey patch
        self.monkeypatch.setattr(
            ClassCalled, "foo_called", mockedFoo)
        """I've noticed with me having monkeypatch inside the
        class, the damn thing persists across functions. 
        This is the only workaround I've found so far"""


# -----------------------------------------------------------------------------
class Test_ClassUnderTest_foo_under_test(TestCase):
    """Test class - means of grouping up tests for a target function

    This one is addressing the function within a class
    """

    # ---------------------------------------------------------------
    @pytest.fixture(autouse=True)
    def capsys(self, capsys):
        """Capsys hook into this class"""
        self.capsys = capsys

    def print_to_console(self, strOut):
        """Print strOut to console (even within a pyTest execution)"""
        with self.capsys.disabled():
            print(strOut)

    def setUp(self):
        """Ran by pyTest before running any test_*() functions"""
        self.monkeypatch = MonkeyPatch()

    # ---------------------------------------------------------------

    def test_1(self):
        """**Test case**"""

        def mock_foo_called2(self, inp2):
            """
            Mock function

            Defining a mock function, note this can be dealt with directly
            here, or if its more comprehensible, put it in a separate class
            (i.e. MockResponses)
            """
            # return MockResponses.foo_called2(inp2)  # Delegated approach 
            return str(inp2*4)  # Direct approach

        """Note that the existence of self within this test class forces
        a wrapper around calling a MockClass - so we have to go through
        both the line below and the function mock_foo_called2() above to 
        properly invoke MockResponses.foo_called2()
        """
        mockedFoo = ClassUnderTest.foo_called2
        self.monkeypatch.setattr(
            ClassUnderTest, "foo_called2", mock_foo_called2)

        x = ClassUnderTest().foo_under_test2(1)
        strOut = "Rtn from foo: " + str(x)
        self.print_to_console("\n")
        self.print_to_console(strOut)

        assert x == "TestVar" + str(4)
        self.monkeypatch.setattr(
            ClassUnderTest, "foo_called2", mockedFoo)




# -----------------------------------------------------------------------------
# ---- Main
if __name__ == "__main__":
    #
    # Setup for pytest
    outFileName = os.path.basename(__file__)[:-3]  # Remove the .py from end
    currScript = os.path.basename(__file__)

    # -------------------------------------------------------------------------
    # PyTest execution
    pytest.main([currScript, "--html", outFileName + "_report.html"])

    rtnA = foo_under_test(1)
    print(rtnA == 4)
    # This should output 4, demonstrating effect of stub (which produced 6)

    rtnB = ClassUnderTest().foo_under_test2(1)
    print(rtnB == "TestVar"+str(5))
    # This should output "TestVar5", demonstrating effect of stub
🌐
GitHub
gist.github.com › fieldse › e22820e52f85d74e6ea6ac94852e34c5
Python pytest + stdout: Capture messages sent to stdout / stderr from your functions with pytest capsys · GitHub
Python pytest + stdout: Capture messages sent to stdout / stderr from your functions with pytest capsys - Pytest - Capture print statements in Pytest modules from stdout using capsys parameter
🌐
GitHub
github.com › pytest-dev › pytest › issues › 3819
stdout capture (capsys) does not seem to work with real-time cli logging · Issue #3819 · pytest-dev/pytest
August 16, 2018 - Using the snippet from https://docs.pytest.org/en/latest/capture.html#accessing-captured-output-from-a-test-function, just adding a logging call before the second print: import logging import sys logger = logging.getLogger(__name__) def test_myoutput(capsys): # or use "capfd" for fd-level print("hello") sys.stderr.write("world\n") captured = capsys.readouterr() assert captured.out == "hello\n" assert captured.err == "world\n" logging.info("something") print("next") captured = capsys.readouterr() assert captured.out == "next\n"
Author   Sup3rGeo
🌐
Medium
pavolkutaj.medium.com › how-to-test-printed-output-in-python-with-pytest-and-its-capsys-fixture-161010cfc5ad
How to Test Printed Output in Python with Pytest and its Capsys Fixture | by Pavol Z. Kutaj | Medium
May 15, 2024 - the function test_my_function takes one argument, capsys, which is a built-in pytest fixture that captures the output to stdout and stderr.
🌐
O'Reilly
oreilly.com › library › view › python-testing-with › 9781680502848 › f_0046.xhtml
Python Testing with pytest - Python Testing with pytest [Book]
September 15, 2017 - Using capsys The capsys builtin fixture provides two bits of functionality: it allows you to retrieve stdout and stderr from some code, and it disables output capture temporarily.... - Selection from Python Testing with pytest [Book]
Author   Brian Okken
Published   2017
Pages   222
🌐
pytest
docs.pytest.org › en › stable › _modules › _pytest › capture.html
_pytest.capture - pytest documentation
[docs] @fixture def capsys(request: SubRequest) -> Generator[CaptureFixture[str]]: r"""Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. The captured output is made available via ``capsys.readouterr()`` method calls, which return a ``(out, err)`` namedtuple. ``out`` and ``err`` will be ``text`` objects. Returns an instance of :class:`CaptureFixture[str] <pytest.CaptureFixture>`. Example: ..
🌐
GitHub
github.com › pytest-dev › pytest › issues › 5997
Output appears in "Captured stderr call" but is unavailable in capsys or capfd · Issue #5997 · pytest-dev/pytest
October 18, 2019 - This is a minimal example I came up with (available at https://github.com/butla/experiments/tree/master/pytest_capture_log_error): # a_file.py import logging logging.basicConfig(level=logging.INFO) LOG_MESSAGE = "the message we'll look for in the test" _log = logging.getLogger(__name__) def bla(): _log.info(LOG_MESSAGE) return 5 · # test_file.py import a_file def test_a(capsys): assert a_file.bla() == 5 assert a_file.LOG_MESSAGE in capsys.readouterr().err
Author   butla
🌐
pytest
docs.pytest.org › en › 7.1.x › how-to › capture-stdout-stderr.html
How to capture stdout/stderr output — pytest documentation
The readouterr() call snapshots the output so far - and capturing will be continued. After the test function finishes the original streams will be restored. Using capsys this way frees your test from having to care about setting/resetting output streams and also interacts well with pytest’s own ...
🌐
Software Design
softdes.olin.edu › docs › readings › unit-testing-advanced
Advanced Unit Testing | Software Design
Assign # the capsys to a local variable in the test function. captured = capsys.readouterr() # We can access the output via captured.out assert captured.out == "Hello, World!\n" # Print appends a newline · monkeypatch is a powerful and incredibly useful Pytest feature.