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 输出:
- Use the
capfd
fixture 直接用 Pytest 捕获和读取所有输出
- Use
context_lib
and redirect_stdout
- 手动重新分配
sys.stdout
到一个明确的文件,然后读取这个文件以获得输出
代码大致如下。对于 capfd
和 redirect_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 时,所有这些代码都工作正常。
- JavaScript 子进程始终有效,它的输出被正确捕获,无论我使用哪种方法。
- 当我使用VSCode集成测试调试时Python子进程确实工作,但是当我使用不时运行
pytest
来自命令行。嗯?为什么?!?!?!?
- Lua 子进程完全挂起。看起来子进程从未真正开始,尽管很难说出发生了什么,因为我无法从 Python 调试到 Lua,而且我不能只是打印出来,因为 Pytest 正在尝试捕捉一切。
- 到目前为止我只添加了这 3 种语言,但我打算添加更多(理论上,所有这些)所以我希望有一个通用的解决方案。
我也可以尝试测试整个 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"]
我有一个 Python 程序调用 print()
and/or 在其他几种语言的脚本上调用 subprocess.Popen()
,也可以 print, console.log, system.out.println,
等等。实际程序很容易将所有这些打印到终端,这是预期的行为。
对于集成测试,我想捕获所有这些输出以检查它是否与预期值匹配。我不能 模拟子进程。这是 不是 单元测试,我不关心我的 Python 代码实际在做什么,只关心输出是否正确。
所有这些选项都适用于捕获内部 Python 输出:
- Use the
capfd
fixture 直接用 Pytest 捕获和读取所有输出 - Use
context_lib
andredirect_stdout
- 手动重新分配
sys.stdout
到一个明确的文件,然后读取这个文件以获得输出
代码大致如下。对于 capfd
和 redirect_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 时,所有这些代码都工作正常。
- JavaScript 子进程始终有效,它的输出被正确捕获,无论我使用哪种方法。
- 当我使用VSCode集成测试调试时Python子进程确实工作,但是当我使用不时运行
pytest
来自命令行。嗯?为什么?!?!?!? - Lua 子进程完全挂起。看起来子进程从未真正开始,尽管很难说出发生了什么,因为我无法从 Python 调试到 Lua,而且我不能只是打印出来,因为 Pytest 正在尝试捕捉一切。
- 到目前为止我只添加了这 3 种语言,但我打算添加更多(理论上,所有这些)所以我希望有一个通用的解决方案。
我也可以尝试测试整个 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"]