使用unittest无法捕获标准输出

时间:2019-08-09 11:51:47

标签: python-3.x stdout python-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']

3 个答案:

答案 0 :(得分:1)

os.system运行一个新进程。如果您对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模块,该模块可让您捕获正在运行的进程的输出。

答案 1 :(得分:1)

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

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

print("r: {r!r} ;".format(r=r))
<块引用>

r: "Foo" ;

答案 2 :(得分:0)

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

要真正能够测试我的代码,我不得不使用subprocess而不是os.system()对其进行重写。最后,我选择了subprocess_run_result = subprocess.run(task, shell=True, stdout=subprocess.PIPE),然后使用subprocess_run_result.stdout.strip().decode("utf-8")得到了结果。

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

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