如何在unittest中捕获python子进程标准输出

Mar*_*ton 5 python subprocess

我正在尝试编写一个单元测试,该测试执行一个写入标准输出的函数,捕获该输出并检查结果。所讨论的函数是一个黑匣子:我们无法更改它写入输出的方式。出于本示例的目的,我对其进行了相当多的简化,但本质上该函数使用 subprocess.call() 生成其输出。

无论我尝试什么,我都无法捕获输出。它总是被写入屏幕,并且测试失败,因为它没有捕获任何内容。我尝试了 print() 和 os.system()。使用 print() 我可以捕获标准输出,但也不能使用 os.system() 。

它也不是特定于单元测试的。我已经编写了我的测试示例,但没有得到相同的结果。

类似的问题已经被问了很多,答案似乎都归结为使用 subprocess.Popen() 和 communications(),但这需要更改黑匣子。我确信有一个我还没有遇到的答案,但我很困惑。

我们使用的是Python-2.7。

无论如何,我的示例代码是这样的:

#!/usr/bin/env python
from __future__ import print_function
import sys
sys.dont_write_bytecode = True

import os
import unittest
import subprocess
from contextlib import contextmanager
from cStringIO import StringIO

# from somwhere import my_function
def my_function(arg):
    #print('my_function:', arg)
    subprocess.call(['/bin/echo', 'my_function: ', arg], shell=False)
    #os.system('echo my_function: ' + arg)

@contextmanager
def redirect_cm(new_stdout):
    old_stdout =  sys.stdout
    sys.stdout =  new_stdout
    try:
        yield
    finally:
        sys.stdout = old_stdout

class Test_something(unittest.TestCase):
   def test(self):
        fptr = StringIO()
        with redirect_cm(fptr):
            my_function("some_value")

        self.assertEqual("my_function: some_value\n", fptr.getvalue())

if __name__ == '__main__':
    unittest.main()
Run Code Online (Sandbox Code Playgroud)

Jac*_*cky 4

上面的代码有两个问题

\n\n
    \n
  1. StringIO fptrStringIO不被当前进程和衍生进程共享,即使衍生进程已将结果写入对象,我们也无法在当前进程中获取结果

  2. \n
  3. 更改sys.stdoutos.popen()不会影响或os 模块中的函数系列os.system()执行的进程的标准 I/O 流exec*()

  4. \n
\n\n

一个简单的解决方案是

\n\n
    \n
  1. 用于os.pipe在两个进程之间共享结果

  2. \n
  3. 使用os.dup2而不是改变sys.stdout

  4. \n
\n\n

演示示例如下所示

\n\n
import sys\nimport os\nimport subprocess\nfrom contextlib import contextmanager\n\n\n@contextmanager\ndef redirect_stdout(new_out):\n    old_stdout = os.dup(1)\n    try:\n        os.dup2(new_out, sys.stdout.fileno())\n        yield\n    finally:\n        os.dup2(old_stdout, 1)\n\n\ndef test():\n    reader, writer = os.pipe()\n\n    with redirect_stdout(writer):\n        subprocess.call([\'/bin/echo\', \'something happened what\'], shell=False)\n\n    print os.read(reader, 1024)\n\n\ntest()\n
Run Code Online (Sandbox Code Playgroud)\n