Pytest:如何盲目 运行 任何子进程并捕获所有输出?

Pytest: How can I blindly run any subprocess and capture all output?

我有一个 Python 程序调用 print() and/or 在其他几种语言的脚本上调用 subprocess.Popen(),也可以 print, console.log, system.out.println, 等等。实际程序很容易将所有这些打印到终端,这是预期的行为。

对于集成测试,我想捕获所有这些输出以检查它是否与预期值匹配。我不能 模拟子进程。这是 不是 单元测试,我不关心我的 Python 代码实际在做什么,只关心输出是否正确。

所有这些选项都适用于捕获内部 Python 输出:

代码大致如下。对于 capfdredirect_stdout,您只需在 run_string.

之前和之后改变您所做的事情
# tests are parametrized from several JSON files.
def test_code(test_json):
    with open("/tmp/output_file.txt", "w+") as output_file:
        orig_stdout = sys.stdout
        sys.stdout = output_file
        print("initial")
        run_string(test_json["code"])
        output_file.seek(0)
        sys.stdout = orig_stdout
        output = output_file.read()
        assert output == test_json["expected"]

这些都完美地获得了内部输出,但是所有都无法subprocess.Popen获得一致的输出。我已经尝试过使用所有这些方法,将 subprocess.stdout 设置为一个文件,设置为 PIPE 并稍后打印出来,shell=True,以及其他一些东西,但它们都有相同的奇怪的问题。我真的无法解释任何这种行为,所以我只是希望有人能帮助我:

我也可以尝试测试整个 CLI,但我想避免这种情况,而且我不确定它能否正常工作。如果有人知道一种方法可以强制 Pytest 运行 完全像本机代码一样,而无需将子进程放入某种盒子中,那正是我所需要的。

对于那些想知道的人,我最终选择了 capfd 挂钩,并完全删除了嵌套子流程要求。我的应用程序需要执行的子流程不再允许直接输出到 shell,特别是因为它是如此不一致。这是我最终得到的最终代码。

def test_dits(dit_json, capfd):
    if "long" in dit_json and not pytest.all_val:  # type: ignore
        pytest.skip("Long test")
    run_string(dit_json["dit"], "tests/fail.dit")
    output, err = capfd.readouterr()

    assert output == dit_json["expected"]