测试人社区

Pytest-Capturing of the stdout/stderr output

Default stdout/stderr/stdin capturing behaviour

During test execution any output sent to stdout and stderr is
captured. If a test or a setup method fails its according captured
output will usually be shown along with the failure traceback. (this
behavior can be configured by the --show-capture command-line option).

In addition, stdin is set to a “null” object which will fail on
attempts to read from it because it is rarely desired to wait for
interactive input when running automated tests.

By default capturing is done by intercepting writes to low level file
descriptors. This allows to capture output from simple print statements
as well as output from a subprocess started by a test.

Setting capturing methods or disabling capturing {#capture-method}

There are three ways in which pytest can perform capturing:

  • fd (file descriptor) level capturing (default): All writes going
    to the operating system file descriptors 1 and 2 will be captured.
  • sys level capturing: Only writes to Python files sys.stdout and
    sys.stderr will be captured. No capturing of writes to
    filedescriptors is performed.
  • tee-sys capturing: Python writes to sys.stdout and sys.stderr
    will be captured, however the writes will also be passed-through to
    the actual sys.stdout and sys.stderr. This allows output to be
    ‘live printed’ and captured for plugin use, such as junitxml (new
    in pytest 5.4).

::: {#disable capturing}
You can influence output capturing mechanisms from the command line:
:::

pytest -s                  # disable all capturing
pytest --capture=sys       # replace sys.stdout/stderr with in-mem files
pytest --capture=fd        # also point filedescriptors 1 and 2 to temp file
pytest --capture=tee-sys   # combines 'sys' and '-s', capturing sys.stdout/stderr
                           # and passing it along to the actual sys.stdout/stderr

Using print statements for debugging {#printdebugging}

One primary benefit of the default capturing of stdout/stderr output is
that you can use print statements for debugging:

# content of test_module.py


def setup_function(function):
    print("setting up", function)


def test_func1():
    assert True


def test_func2():
    assert False

and running this module will show you precisely the output of the
failing function and hide the other one:

$ pytest
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 2 items

test_module.py .F                                                    [100%]

________________________________ test_func2 ________________________________

    def test_func2():
>       assert False
E       assert False

test_module.py:12: AssertionError
-------------------------- Captured stdout setup ---------------------------
setting up <function test_func2 at 0xdeadbeef>
FAILED test_module.py::test_func2 - assert False

Accessing captured output from a test function

The capsys, capsysbinary, capfd, and capfdbinary fixtures allow
access to stdout/stderr output created during test execution. Here is an
example test function that performs some output related checks:

def test_myoutput(capsys):  # or use "capfd" for fd-level
    print("hello")
    sys.stderr.write("world\n")
    captured = capsys.readouterr()
    print("next")
    captured = capsys.readouterr()

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 per-test capturing.

If you want to capture on filedescriptor level you can use the capfd
fixture which offers the exact same interface but allows to also capture
output from libraries or subprocesses that directly write to operating
system level output streams (FD1 and FD2).

The return value from readouterr changed to a namedtuple with two
attributes, out and err.

If the code under test writes non-textual data, you can capture this
using the capsysbinary fixture which instead returns bytes from the
readouterr method.

If the code under test writes non-textual data, you can capture this
using the capfdbinary fixture which instead returns bytes from the
readouterr method. The capfdbinary fixture operates on the
filedescriptor level.

To temporarily disable capture within a test, both capsys and capfd
have a disabled() method that can be used as a context manager,
disabling capture inside the with block:

def test_disabling_capturing(capsys):
    print("this output is captured")
    with capsys.disabled():
        print("output not captured, going directly to sys.stdout")
    print("this output is also captured")