如何捕获 stdout/stderr 输出

pytest 根据 --capture= 命令行参数的配置或通过使用 fixture 来拦截 stdout 和 stderr。 --capture= 标志配置报告,而 fixture 则提供更精细的控制并允许在测试期间检查输出。报告可以使用 -r 标志进行自定义。

默认 stdout/stderr/stdin 捕获行为

在测试执行期间,任何发送到 stdoutstderr 的输出都会被捕获。如果测试或 setup 方法失败,其相应的捕获输出通常会与失败的 traceback 一起显示。(此行为可以通过 --show-capture 命令行选项进行配置)。

此外,stdin 被设置为一个“null”对象,尝试从中读取时将失败,因为在运行自动化测试时很少需要等待交互式输入。

默认情况下,捕获是通过拦截对低级文件描述符的写入来完成的。这允许捕获简单的 print 语句的输出以及由测试启动的子进程的输出。

设置捕获方法或禁用捕获

pytest 可以通过以下三种方式执行捕获:

  • fd (文件描述符)级别捕获(默认):所有写入操作系统文件描述符 1 和 2 的内容都将被捕获。

  • sys 级别捕获:仅捕获写入 Python 文件 sys.stdoutsys.stderr 的内容。不捕获写入文件描述符的内容。

  • tee-sys 捕获:Python 写入 sys.stdoutsys.stderr 的内容将被捕获,但这些写入也会被传递到实际的 sys.stdoutsys.stderr。这允许输出“实时打印”并捕获供插件使用,例如 junitxml(pytest 5.4 中的新功能)。

您可以从命令行影响输出捕获机制

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

使用 print 语句进行调试

默认捕获 stdout/stderr 输出的一个主要好处是您可以使用 print 语句进行调试

# content of test_module.py


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


def test_func1():
    assert True


def test_func2():
    assert False

运行此模块将精确显示失败函数的输出并隐藏其他函数

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 2 items

test_module.py .F                                                    [100%]

================================= FAILURES =================================
________________________________ test_func2 ________________________________

    def test_func2():
>       assert False
E       assert False

test_module.py:12: AssertionError
-------------------------- Captured stdout setup ---------------------------
setting up <function test_func2 at 0xdeadbeef0001>
========================= short test summary info ==========================
FAILED test_module.py::test_func2 - assert False
======================= 1 failed, 1 passed in 0.12s ========================

从测试函数访问捕获的输出

capsyscapteesyscapsysbinarycapfdcapfdbinary fixture 允许访问在测试执行期间创建的 stdout/stderr 输出。

这是一个执行一些输出相关检查的示例测试函数

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"
    print("next")
    captured = capsys.readouterr()
    assert captured.out == "next\n"

readouterr() 调用会快照到目前为止的输出——并且捕获将继续。测试函数完成后,原始流将被恢复。通过这种方式使用 capsys 使您的测试无需关心设置/重置输出流,并且还与 pytest 自己的每测试捕获良好地交互。

readouterr() 的返回值是一个 namedtuple,带有两个属性:outerr

如果被测代码写入非文本数据(bytes),您可以使用 capsysbinary fixture 进行捕获,它会从 readouterr 方法返回 bytes

如果您想在文件描述符级别进行捕获,可以使用 capfd fixture,它提供完全相同的接口,但还允许捕获直接写入操作系统级别输出流(FD1 和 FD2)的库或子进程的输出。与 capsysbinary 类似,capfdbinary 可用于在文件描述符级别捕获 bytes

要在测试中临时禁用捕获,捕获 fixture 具有一个 disabled() 方法,可以用作上下文管理器,在 with 块内禁用捕获

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")