Python:等效于使用sys.stdin的输入

Jam*_*mes 2 python stdin dependency-injection mocking python-3.x

我想测试一些直接使用printand input函数的(python 3)代码。据我了解,最简单的方法是依赖注入:修改代码,使其将输入和输出流作为参数,默认情况下使用sys.stdinsys.stdout,并在测试过程中传入模拟对象。很明显,如何处理print呼叫:

print(text)
#replaced with...
print(text, file=output_stream)
Run Code Online (Sandbox Code Playgroud)

但是,input没有输入和输出流的参数。以下代码是否正确地重现其行为?

text = input(prompt)
#replaced with...
print(prompt, file=output_stream, end='')
text = input_stream.readline()[:-1]
Run Code Online (Sandbox Code Playgroud)

我看过了的实现input,它做了很多魔术,调用sys.stdin.fileno和检查sys.stdin.encodingsys.stdin.errors而不是调用任何read*方法-我不知道从哪里开始模拟这些方法。

maz*_*yus 5

如果你将一个类文件对象分配给sys.stdinPython 的input函数将使用它而不是标准输入。但是请记住sys.stdin在完成后重新分配回标准输入。同样的技巧适用于sys.stdout. 你可以这样做:

original_stdin = sys.stdin
sys.stdin = open('inputfile.txt', 'r')

original_stdout = sys.stdout
sys.stdout = open('outputfile.txt', 'w')

response = input('say hi: ')
print(response)

sys.stdin = original_stdin
sys.stdout = original_stdout
Run Code Online (Sandbox Code Playgroud)

这两行

response = input('say hi: ')
print(response)
Run Code Online (Sandbox Code Playgroud)

将使用指定的文件(inputfile.txtoutputfile.txt)而不是标准输入和标准输出。

更新:如果您不想处理物理文件,请查看io模块。它提供了io.StringIO允许您执行内存中文本流操作的类。

original_stdin = sys.stdin
sys.stdin = io.StringIO('input string')

original_stdout = sys.stdout
sys.stdout = io.StringIO()

response = input('say hi: ')
print(response)

output = sys.stdout.getvalue()

sys.stdin = original_stdin
sys.stdout = original_stdout

print(output)
Run Code Online (Sandbox Code Playgroud)


lqc*_*lqc 5

input()只做当你提到的魔法stdinstdout不会改变,因为只有这样,它可以使用的东西readline库。如果将它们替换为其他东西(是否是真实文件),它将归结为以下代码:

/* Fallback if we're not interactive */
if (promptarg != NULL) {
    if (PyFile_WriteObject(promptarg, fout, Py_PRINT_RAW) != 0)
         return NULL;
}
tmp = _PyObject_CallMethodId(fout, &PyId_flush, "");
if (tmp == NULL)
    PyErr_Clear();
else
    Py_DECREF(tmp);
return PyFile_GetLine(fin, -1);
Run Code Online (Sandbox Code Playgroud)

在哪里PyFile_GetLine 调用readline方法。因此,嘲讽sys.std*将起作用。

建议您使用try: finally:,上下文处理器或mock模块来执行此操作,以便即使测试的代码因异常而失败,也可以恢复输出:

from unittest.mock import patch
from io import StringIO

with patch("sys.stdin", StringIO("FOO")), patch("sys.stdout", new_callable=StringIO) as mocked_out:
    x = input()
    print("Read:", x)

assert mocked_out.getvalue() == "Read: FOO\n"
Run Code Online (Sandbox Code Playgroud)