如何使用子流程有效地测试基于readline的Python程序?
我有一个Python程序,在某些情况下,它会提示用户输入文件名。但是,我想提供一个默认文件名,用户可以根据自己的意愿进行编辑。这通常意味着他们需要点击backspace键来删除当前文件名,并将其替换为他们喜欢的文件名 为此,我将Python 3改编为:如何使用子流程有效地测试基于readline的Python程序?,python,unit-testing,python-3.x,subprocess,readline,Python,Unit Testing,Python 3.x,Subprocess,Readline,我有一个Python程序,在某些情况下,它会提示用户输入文件名。但是,我想提供一个默认文件名,用户可以根据自己的意愿进行编辑。这通常意味着他们需要点击backspace键来删除当前文件名,并将其替换为他们喜欢的文件名 为此,我将Python 3改编为: def rlinput(prompt, prefill=''): readline.set_startup_hook(lambda: readline.insert_text(prefill)) try: retu
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt)
finally:
readline.set_startup_hook()
new_filename = rlinput("What filename do you want?", "foo.txt")
当程序按预期以交互方式运行时,此功能会正常工作-在退格并输入新文件名后,new_filename
包含bar.txt
或用户输入的任何文件名
但是,我还想使用单元测试来测试程序。一般来说,要做到这一点,我将程序作为子进程运行,以便将其输入提供给stdin(从而按照用户使用它的方式进行测试)。我有一些单元测试代码(简化)如下所示:
p = Popen(['mypythonutility', 'some', 'arguments'], stdin=PIPE)
p.communicate('\b\b\bbar.txt')
我的意图是,这应该模拟用户在提供的foo.txt
上的“退格”,并输入bar.txt
然而,这似乎没有达到预期的效果。相反,在一些调试之后,我的程序中的新文件名
最终会与\b\b\bbar.txt
等效。我期待的只是bar.txt
我做错了什么?从Python控制交互式子进程的适当方法是使用模块。此模块使子进程相信它正在交互终端会话中运行,并让父进程准确地确定哪些击键被发送到子进程 Pexpect是一个纯Python模块,用于生成子应用程序;控制它们;并对其输出中的预期模式做出响应。Pexpect的工作原理与Don Libes的Expect类似。Pexpect允许您的脚本生成一个子应用程序并控制它,就像人类键入命令一样
好的,谢谢。这很有意义(可以看到与Unix实用程序expect的相似之处)。我来看看。但是,您知道为什么子流程不能按我期望的方式工作吗?仍然对此感到困惑。@AndrewFerrier:可能是子进程中的readline库发现它正在从管道而不是交互式tty获取输入,并禁用了诸如
\b
处理之类的功能。是的,这听起来很有道理。我可以在我的实用程序中解决这个问题,但是很明显,我正在改变程序来处理测试——这不是一个好主意。将调查Pexpect。@AndrewFerrier:您可以使用pty
模块在子流程
模块上重新实现Pexpect
模块的相应部分:在对Pexpect进行了大量的修改之后,我终于让它做了正确的事情。关键是使用child.sendcontrol(“H”)
发送退格,这有点复杂。但这一切都是可行的。