1

我正在测试一段使用 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
4

2 回答 2

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)

并正常继续。

于 2012-06-02T10:31:57.757 回答
0

subprocess模块使用 直接写入输出流os.write,因此更改sys.stdout不会做任何事情。

这就是为什么您通常需要使用...([cmd, arg, ...], stdout=output_file, ...)符号指定与子流程模块一起使用的输出流,以及为什么不能使用StringIO作为输出文件的原因,因为它没有要写入的文件号。

因此,在导入您需要测试的模块之前,实现您想要做的事情的唯一方法是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
于 2012-06-02T09:15:18.900 回答