如何向Python单元测试提供stdin、文件和环境变量输入?
如何在出现以下情况时编写测试:如何向Python单元测试提供stdin、文件和环境变量输入?,python,unit-testing,user-input,Python,Unit Testing,User Input,如何在出现以下情况时编写测试: 测试用户输入 从文件读取的测试输入 从环境变量读取的测试输入 如果有人能告诉我如何处理上述场景,那就太好了;如果你能给我指出一些我能看到的文档/文章/博客文章,那就太棒了 阅读。您描述的所有三种情况都需要特别注意,以确保在设计中使用松耦合 您真的需要单元测试Python的raw\u input方法吗?open方法操作系统环境获取?没有 您需要设置您的设计,以便可以替换检索该输入的其他方法。然后,在单元测试期间,您将插入某种存根,它实际上不会调用raw\u inpu
阅读。您描述的所有三种情况都需要特别注意,以确保在设计中使用松耦合 您真的需要单元测试Python的
raw\u input
方法吗?open
方法<代码>操作系统环境获取?没有
您需要设置您的设计,以便可以替换检索该输入的其他方法。然后,在单元测试期间,您将插入某种存根,它实际上不会调用raw\u input
或open
例如,您的普通代码可能类似于:
import os
def say_hello(input_func):
name = input_func()
return "Hello " + name
def prompt_for_name():
return raw_input("What is your name? ")
print say_hello(prompt_for_name)
# Normally would pass in methods, but lambdas can be used for brevity
print say_hello(lambda: open("a.txt").readline())
print say_hello(lambda: os.environ.get("USER"))
会议内容如下:
What is your name? somebody
Hello somebody
Hello [some text]
Hello mark
请记住,您不必测试一种语言的IO功能(除非您是设计该语言的人,这是一种完全不同的情况)。如果您不需要使用外部进程就可以脱身,请这样做 然而,在有些情况下,这是复杂的,并且您确实希望使用进程,例如,您希望测试C可执行文件的命令行接口 用户输入 使用
subprocess.Popen
如中所示:
process = subprocess.Popen(
command,
shell = False,
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
universal_newlines = True
)
stdout, stderr = process.communicate("the user input\nline 2")
exit_status = process.wait()
从用户处获取输入和从管道中获取输入之间没有区别,输入是通过raw\u input
或sys.stdin.read()
等方法完成的
文件
- 创建一个临时目录,并在测试
方法中创建要从中读取的文件:setUp
tdir = tempfile.mkdtemp( prefix = 'filetest_', ) fpath = os.path.join(tdir,'filename') fp = open(fpath, 'w') fp.write("contents") fp.close()
- 在测试中读取文件
- 然后移除临时目录
shutil.rmtree(tdir)
- 从文件中读取是相当复杂的,大多数程序都可以从文件或STDIN中读取(例如使用
)。因此,如果您想测试的是输入特定内容时发生的情况,并且您的程序接受STDIN,那么只需使用fileinput
测试程序Popen
- 使用
os.environ[“the_VAR”]=“the_val”
- 使用
delos.environ[“THE_VAR”]
不工作os.environ={'a':'b'}
- 然后调用
。环境是从调用进程继承的subprocess.Popen
我有一个模块测试STDOUT,
STDERR
,以及给定的退出状态STDIN,命令行参数和环境。另外,在“tests”目录下检查该模块的测试。在这方面肯定有更好的模块,所以就拿我的模块来学习吧。如果你习惯于使用原始输入(或任何其他特定的输入源),我是这方面的大力支持者。鉴于Mark Rushakoff在其示例中使用的代码:
def say_hello():
name = raw_input("What is your name? ")
return "Hello " + name
您的测试代码可以使用mock:
import mock
def test_say_hello():
with mock.patch('__builtin__.raw_input', return_value='dbw'):
assert say_hello() == 'Hello dbw'
with mock.patch('__builtin__.raw_input', side_effect=['dbw', 'uki']):
assert say_hello() == 'Hello dbw'
assert say_hello() == 'Hello uki'
这些断言将成为事实。注意,side_效应按顺序返回列表中的元素。它可以做更多!我建议您查看文档。使用:
+1:这不仅使代码更易于测试,而且更易于重用,因为它不依赖于特定的输入源。除非您无法控制上述内容。好的路线通常是不可用的。那么这种方法如何处理分配给多个变量的多个输入呢。你是创建多输入函数还是有更优雅的方法?检查我是否正确理解你的问题:你想知道的是:如何测试一个可执行文件/脚本,给定一些输入,如stdin/环境变量,是否给出预期的输出,如stdout、stderr或exit status?@CiroSantilli新疆改造中心六四事件法轮功 我确实需要它,你知道怎么做吗?@johnballinarias请看我的答案:-)对于Python3,请注意,
\uuuuu-builtin\uuuu
被重命名为builtins
,而raw\u-input()
变成了input()
,因此:mock.patch('builtins.input',return\u value='dew'))
Mock是Python3中核心python的一部分,正如unittest import Mock中的一样。谢谢,我最喜欢这种方法(包括Python3变体)。另外,当使用Pytestas时,mock当然非常有用。据我所知,它只允许插入一次输入,如果有一个“while循环”从用户n次收集输入,该怎么办?@JohnBalvinArias除非涉及计时,否则无法区分管道和用户输入。如果有时间安排,请查看pexpect。
import mock
def test_say_hello():
with mock.patch('__builtin__.raw_input', return_value='dbw'):
assert say_hello() == 'Hello dbw'
with mock.patch('__builtin__.raw_input', side_effect=['dbw', 'uki']):
assert say_hello() == 'Hello dbw'
assert say_hello() == 'Hello uki'
import os
def test_user_input(monkeypatch):
inputs = [10, 'y']
input_generator = (i for i in inputs)
monkeypatch.setattr('__builtin__.raw_input', lambda prompt: next(input_generator))
assert raw_input('how many?') == 10
assert raw_input('you sure?') == 'y'
def test_file_input(tmpdir):
fixture = tmpdir.join('fixture.txt')
fixture.write(os.linesep.join(['1', '2', '3']))
fixture_path = str(fixture.realpath())
with open(fixture_path) as f:
assert f.readline() == '1' + os.linesep
def test_environment_input(monkeypatch):
monkeypatch.setenv('STAGING', 1)
assert os.environ['STAGING'] == '1'