如何在unittest中捕获python子进程stdout

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在unittest中捕获python子进程stdout相关的知识,希望对你有一定的参考价值。

我正在尝试编写一个单元测试,执行写入stdout的函数,捕获该输出,并检查结果。有问题的功能是一个黑盒子:我们不能改变它写出输出的方式。出于这个例子的目的,我已经简化了很多,但基本上该函数使用subprocess.call()生成其输出。

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

它也不是单元测试的具体内容。我没有用同样的结果编写了我的测试示例。

类似的问题已经被问了很多,而答案似乎都归结为使用subprocess.Popen()和communic(),但这需要更改黑盒子。我确定有一个我没有遇到的答案,但我很难过。

我们正在使用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
", fptr.getvalue())

if __name__ == '__main__':
    unittest.main()
答案

上面的代码中有两个问题

  1. StringIO fptr不会被当前和生成的进程共享,即使生成的进程已将结果写入StringIO对象,我们也无法在当前进程中获得结果
  2. Changing sys.stdout doesn’t affect the standard I/O streamsos.popen()或os模块中的os.system()函数族执行的进程的exec*()

一个简单的解决方案

  1. 使用os.pipe在两个进程之间共享结果
  2. 使用os.dup2而不是改变sys.stdout

演示示例如下所示

import sys
import os
import subprocess
from contextlib import contextmanager


@contextmanager
def redirect_stdout(new_out):
    old_stdout = os.dup(1)
    try:
        os.dup2(new_out, sys.stdout.fileno())
        yield
    finally:
        os.dup2(old_stdout, 1)


def test():
    reader, writer = os.pipe()

    with redirect_stdout(writer):
        subprocess.call(['/bin/echo', 'something happened what'], shell=False)

    print os.read(reader, 1024)


test()

以上是关于如何在unittest中捕获python子进程stdout的主要内容,如果未能解决你的问题,请参考以下文章

如何捕获从 python 子进程运行的 git clone 命令的输出

PyDev unittesting:如何在“捕获的输出”中捕获记录到 logging.Logger 的文本

perl fork() exec() ,子进程变得狂野

通过不使用通信的子进程捕获输出

捕获信号时如何正确等待bash子进程完成

使用 libuv 捕获子进程的标准输出