如何捕获 stdout/stderr 输出

How to capture stdout/stderr output

默认 stdout/stderr/stdin 捕获行为

Default stdout/stderr/stdin capturing behaviour

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

此外, stdin 被设置为一个 “null” 对象,这会在尝试从中读取时失败,因为在运行自动化测试时,通常不希望等待交互式输入。

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

设置捕获方法或禁用捕获

Setting capturing methods or disabling capturing

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

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

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

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

您可以通过命令行影响输出捕获机制:

pytest -s                  # 禁用所有捕获
pytest --capture=sys       # 用内存文件替换 sys.stdout/stderr
pytest --capture=fd        # 同时将文件描述符 1 和 2 指向临时文件
pytest --capture=tee-sys   # 结合 'sys' 和 '-s',捕获 sys.stdout/stderr
                        # 并将其传递给实际的 sys.stdout/stderr

使用打印语句进行调试

Using print statements for debugging

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

# test_module.py 的内容


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


def test_func1():
    assert True


def test_func2():
    assert False

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

$ pytest
=========================== 测试会话开始 ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
收集了 2 项

test_module.py .F                                                    [100%]

================================= 失败 =================================
________________________________ test_func2 ________________________________

    def test_func2():
>       assert False
E       assert False

test_module.py:12: AssertionError
-------------------------- 捕获的 stdout 设置 ---------------------------
setting up <function test_func2 at 0xdeadbeef0001>
========================= 短测试摘要信息 ==========================
FAILED test_module.py::test_func2 - assert False
======================= 1 个失败,1 个通过,耗时 0.12s ========================

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

Accessing captured output from a test function

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

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

def test_myoutput(capsys):  # 或使用 "capfd" 进行文件描述符级捕获
    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 的返回值变更为具有两个属性的 namedtupleouterr

如果待测试的代码写入非文本数据(bytes),您可以使用 capsysbinary fixture 来捕获,这样 readouterr 方法将返回 bytes

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

要在测试中暂时禁用捕获,捕获 fixture 提供了一个 disabled() 方法,可以作为上下文管理器使用,在 with 块中禁用捕获:

def test_disabling_capturing(capsys):
    print("此输出被捕获")
    with capsys.disabled():
        print("输出未被捕获,直接写入 sys.stdout")
    print("此输出也被捕获")