我正在测试一段使用subprocess.call()的Python代码,所以我无法控制该函数调用。我需要捕获系统调用的输出来做断言。我试图将os.stdout设置为StringIO对象,但这并不能捕获系统调用输出。我该如何解决这个问题?到目前为止,这是我的代码:
要测试的代码(我无法控制):
def runFile(filename):
check_call("./" + filename)
我尝试捕获系统调用输出:
import StringIO
oldout = sys.stdout
try:
sys.stdout = StringIO.StringIO()
runFile("somefile")
output = sys.stdout.getvalue()
assert output == "expected-output"
finally:
sys.stdout = oldout
答案 0 :(得分:1)
您可以通过
重定向自己的标准输出rd, wr = os.pipe()
oldstdout = os.dup(1)
os.dup2(wr, 1)
os.close(wr)
然后在外部进程运行时从rd
读取。
这可能不起作用,因为外部进程可能会在管道缓冲区中写入更多内容。在这种情况下,你必须产生一个阅读线程。
然后,使用
恢复旧状态os.dup2(oldstdout, 1)
os.close(oldstdout)
os.close(rd)
并继续正常。
答案 1 :(得分:0)
subprocess
模块使用os.write
直接写入输出流,因此更改sys.stdout
将无法执行任何操作。
这就是为什么你通常需要使用...([cmd, arg, ...], stdout=output_file, ...)
表示法指定与子进程模块一起使用的输出流,以及为什么你不能使用StringIO
作为输出文件,因为它没有要写的fileno到。
因此,在导入您需要测试的模块之前,实现您想要做的事情的唯一方法是使用monkeypatch subproces.check_call
,以便它更像check_output。
例如:
import subprocess
def patched_call(*popenargs, **kwargs):
if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.')
process = subprocess.Popen(*popenargs, stdout=subprocess.PIPE, **kwargs)
output, unused_err = process.communicate()
# do something with output here
retcode = process.poll()
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
raise CalledProcessError(retcode, cmd, output=output)
return retcode
subprocess.check_call = patched_call