我想测试一些直接使用print
和input
函数的(python 3)代码。据我了解,最简单的方法是通过依赖注入:修改代码,使其将输入和输出流作为参数,默认情况下使用sys.stdin
和sys.stdout
并在传递期间传入模拟对象测试。很明显如何处理print
次呼叫:
print(text)
#replaced with...
print(text, file=output_stream)
但是,input
没有输入和输出流的参数。以下代码是否正确地重现了它的行为?
text = input(prompt)
#replaced with...
print(prompt, file=output_stream, end='')
text = input_stream.readline()[:-1]
我查看了input
的实现,并且它有很多魔力,调用sys.stdin.fileno
并检查sys.stdin.encoding
和sys.stdin.errors
而不是调用任何read*
方法 - 我不知道从哪里开始嘲笑那些。
答案 0 :(得分:5)
input()
只会在stdin
和stdout
未被修改时提到您的魔力,因为只有这样才能使用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);
PyFile_GetLine
calls the 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"
答案 1 :(得分:4)
如果将类文件对象分配给sys.stdin
,Python的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
这两行
response = input('say hi: ')
print(response)
将使用指定的文件(inputfile.txt
和outputfile.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)