[英]How to capture python subprocess stdout in unittest
I am trying to write a unit test that executes a function that writes to stdout, capture that output, and check the result. 我正在尝试编写一个单元测试,该单元测试执行写入stdout的功能,捕获该输出并检查结果。 The function in question is a black box: we can't change how it is writing it's output. 有问题的函数是一个黑匣子:我们无法更改其写入输出的方式。 For purposes of this example I've simplified it quite a bit, but essentially the function generates its output using subprocess.call(). 出于本示例的目的,我已经对其进行了相当多的简化,但是实质上该函数使用subprocess.call()生成其输出。
No matter what I try I can't capture the output. 无论我尝试什么,都无法捕获输出。 It is always written to the screen, and the test fails because it captures nothing. 它始终被写入屏幕,并且测试失败,因为它没有捕获任何内容。 I experimented with both print() and os.system(). 我尝试了print()和os.system()。 With print() I can capture stdout, but not with os.system() either. 使用print()可以捕获标准输出,但是不能使用os.system()捕获。
It's also not specific to unittesting. 它也不特定于单元测试。 I've written my test example without that with the same results. 我没有用相同的结果编写测试示例。
Questions similar to this have been asked a lot, and the answers all seem to boil down to use subprocess.Popen() and communicate(), but that would require changing the black box. 与此类似的问题已经问了很多,答案似乎都归结为使用subprocess.Popen()和communication(),但这将需要更改黑框。 I'm sure there's an answer I just haven't come across, but I'm stumped. 我敢肯定我还没有找到答案,但是我很困惑。
We are using Python-2.7. 我们正在使用Python-2.7。
Anyway my example code is this: 无论如何,我的示例代码是这样的:
#!/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()
There are two issues in the above code 上面的代码有两个问题
StringIO fptr
does not shared by the current and the spawned process, we could not get the result in current process even if the spawned process has written result to StringIO
object StringIO fptr
不被当前和生成的进程共享,即使生成的进程已将结果写入StringIO
对象,我们也无法在当前进程中获取结果
Changing sys.stdout
doesn't affect the standard I/O streams of processes executed by os.popen()
, os.system()
or the exec*()
family of functions in the os module 更改sys.stdout
不会影响 os模块中os.popen()
, os.system()
或exec*()
系列函数执行的标准I / O流程流
A simple solution is 一个简单的解决方案是
use os.pipe
to share result between the two processes 使用os.pipe
在两个进程之间共享结果
use os.dup2
instead of changing sys.stdout
使用os.dup2
而不是更改sys.stdout
A demo example as following shown 演示示例如下所示
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()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.