无法使用单元测试捕获标准输出

Can't capture stdout with unittest

我有一个 python3.7 脚本,它将 YAML 文件作为输入并根据其中的说明对其进行处理。我用于单元测试的 YAML 文件如下所示:

...
tasks:
  - echo '1'
  - echo '2'
  - echo '3'
  - echo '4'
  - echo '5'

脚本循环执行任务,然后使用 os.system() 调用运行每个任务。

手动测试表明,输出符合预期:

1
2
3
4
5

但是我不能让它在我的单元测试中工作。以下是我尝试捕获输出的方式:

from application import application
from io import StringIO
import unittest
from unittest.mock import patch

class TestApplication(unittest.TestCase):
    def test_application_tasks(self):
        expected = ['1','2','3','4','5']

        with patch('sys.stdout', new=StringIO()) as fakeOutput:
            application.parse_event('some event') # print() is called here within parse_event()
            self.assertEqual(fakeOutput.getvalue().strip().split(), expected)

当运行python3 -m unittest discover -s tests时,我得到的只是AssertionError: Lists differ: [] != ['1', '2', '3', '4', '5']

我也试过用with patch('sys.stdout', new_callable=StringIO) as fakeOutput:代替,但没有用。

我尝试的另一件事是 self.assertEqual(fakeOutput.getvalue(), '1\n2\n3\n4\n5'),这里是单元测试输出的内容:

AssertionError: '' != '1\n2\n3\n4\n5'
+ 1
+ 2
+ 3
+ 4
+ 5

显然,脚本可以运行并输出正确的结果,但是 fakeOutput 没有捕捉到它。

使用patch作为装饰器也不起作用:

from application import application
from io import StringIO
import unittest
from unittest.mock import patch

class TestApplication(unittest.TestCase):
    @patch('sys.stdout', new_callable=StringIO)
    def test_application_tasks(self):
        expected = ['1','2','3','4','5']
        application.parse_event('some event') # print() is called here within parse_event()
        self.assertEqual(fakeOutput.getvalue().strip().split(), expected)

会输出完全相同的错误:AssertionError: Lists differ: [] != ['1', '2', '3', '4', '5']

os.system 运行s 一个新进程。如果您使用 monkey-patch sys.stdout 这会影响当前进程,但不会对任何新进程产生影响。

考虑:

import sys

from os import system
from io import BytesIO

capture = sys.stdout = BytesIO()
system("echo Hello")
sys.stdout = sys.__stdout__
print(capture.getvalue())

没有捕获任何内容,因为只有子进程写入了它的标准输出。没有任何内容写入您的 Python 进程的标准输出。

通常,避免 os.system。相反,使用 subprocess 模块,它可以让您从 运行.

的过程中捕获输出

谢谢你,让-保罗·卡尔德隆。我意识到 os.system() 创建了一个完全不同的过程,因此我需要以不同的方式解决问题,只有在我发布问题之后:)

为了真正能够测试我的代码,我不得不使用 subprocess 而不是 os.system() 重写它。最后,我使用 subprocess_run_result = subprocess.run(task, shell=True, stdout=subprocess.PIPE) 然后使用 subprocess_run_result.stdout.strip().decode("utf-8").

得到结果

在测试中,我只是创建了一个 class 的实例并调用了一个方法,该方法在子进程中运行任务。

我的整个重构代码和测试是here in this commit如果有人想看的话。

您的解决方案很好,只需使用 getvalue 代替,如下所示:

with patch("sys.stdout", new_callable=StringIO) as f:
    print("Foo")
    r = f.getvalue()

print("r: {r!r} ;".format(r=r))

r: "Foo" ;