如何使用 pytest monkeypatch 模拟两个连续的控制台输入
How to simulate two consecutive console inputs with pytest monkeypatch
模块 "overwrite_file"(参见代码示例)如果第一个用户输入得到 "n"
的回答,则要求输入新的文件名
在我的测试设置中,我使用两个连续的 monkeypatch.setattr 调用来模拟输入。
如果我使用顺序如下,结果是无限循环:
monkeypatch.setattr('builtins.input', lambda overwrite: "n")
monkeypatch.setattr('builtins.input', lambda new_name: new_filename)
第二个 monkeypatch.setattr 调用被激活,'new.pkl' 被赋值给变量 overwrite。
如果我这样更改 monkeypatch 命令的顺序:
monkeypatch.setattr('builtins.input', lambda new_name: new_filename)
monkeypatch.setattr('builtins.input', lambda overwrite: "n")
我得到一个断言错误,因为 'n' 被分配给变量 new_name 并且创建了一个名为 "n" 的文件。
如何获得预期的测试功能?
口译员:Python 3.8
from os.path import exists, join, dirname
import pickle
import pytest
def overwrite_file(filename):
# loop until overwrite existing file or input of a file name which does not exist
dump_file = False
while not dump_file:
if exists(filename):
overwrite = input(f"overwrite {filename} (y/n): ")
if overwrite in ["y", "Y"]:
dump_file = True
if overwrite in ["n", "N"]:
new_name = input("new filename: ")
filename = join(dirname(filename), new_name)
else:
dump_file = True
return filename
@pytest.fixture()
def pickle_test_env(tmpdir_factory):
a_dir = tmpdir_factory.mktemp('src_dir')
a_file = a_dir.join('already_there.pkl')
with open(a_file, "wb") as f:
pickle.dump({"C": 27.1, "S": -8.2, "T": 29.7}, f)
return a_dir
def test_new_filename_if_file_exists(pickle_test_env, monkeypatch):
""" is overwrite_file returning a valid new filename if filename exists
and should not be overwritten? """
filename = 'already_there.pkl'
new_filename = 'new.pkl'
assert exists(join(pickle_test_env, filename))
monkeypatch.setattr('builtins.input', lambda new_name: new_filename)
monkeypatch.setattr('builtins.input', lambda overwrite: "n")
assert overwrite_file(join(pickle_test_env, filename)) == join(pickle_test_env, new_filename)
最后一个猴子补丁将战胜所有其他猴子补丁,因此 input(f"overwrite {filename} (y/n): ")
正在获得 "n"
,input("new filename: ")
也是如此。为了以正确的顺序提供所需的输入,我们可以 monkeypatch 一个循环其响应的方法
responses = iter(['n', new_filename])
monkeypatch.setattr('builtins.input', lambda msg: next(responses))
请注意 responses
是一个迭代器对象——也就是说,对其调用 next()
将 return 列表中的下一个项目。如果调用 input()
的次数多于列表中的项目数,则会引发 StopIteration
。可以提供可选的默认值,避免 StopIteration
异常,并允许永远调用 input()
:
next(responses, '\n')
可能有一种更简洁的方法来为 input()
提供标准输入,但我现在不知所措。
模块 "overwrite_file"(参见代码示例)如果第一个用户输入得到 "n"
的回答,则要求输入新的文件名在我的测试设置中,我使用两个连续的 monkeypatch.setattr 调用来模拟输入。 如果我使用顺序如下,结果是无限循环:
monkeypatch.setattr('builtins.input', lambda overwrite: "n")
monkeypatch.setattr('builtins.input', lambda new_name: new_filename)
第二个 monkeypatch.setattr 调用被激活,'new.pkl' 被赋值给变量 overwrite。
如果我这样更改 monkeypatch 命令的顺序:
monkeypatch.setattr('builtins.input', lambda new_name: new_filename)
monkeypatch.setattr('builtins.input', lambda overwrite: "n")
我得到一个断言错误,因为 'n' 被分配给变量 new_name 并且创建了一个名为 "n" 的文件。
如何获得预期的测试功能?
口译员:Python 3.8
from os.path import exists, join, dirname
import pickle
import pytest
def overwrite_file(filename):
# loop until overwrite existing file or input of a file name which does not exist
dump_file = False
while not dump_file:
if exists(filename):
overwrite = input(f"overwrite {filename} (y/n): ")
if overwrite in ["y", "Y"]:
dump_file = True
if overwrite in ["n", "N"]:
new_name = input("new filename: ")
filename = join(dirname(filename), new_name)
else:
dump_file = True
return filename
@pytest.fixture()
def pickle_test_env(tmpdir_factory):
a_dir = tmpdir_factory.mktemp('src_dir')
a_file = a_dir.join('already_there.pkl')
with open(a_file, "wb") as f:
pickle.dump({"C": 27.1, "S": -8.2, "T": 29.7}, f)
return a_dir
def test_new_filename_if_file_exists(pickle_test_env, monkeypatch):
""" is overwrite_file returning a valid new filename if filename exists
and should not be overwritten? """
filename = 'already_there.pkl'
new_filename = 'new.pkl'
assert exists(join(pickle_test_env, filename))
monkeypatch.setattr('builtins.input', lambda new_name: new_filename)
monkeypatch.setattr('builtins.input', lambda overwrite: "n")
assert overwrite_file(join(pickle_test_env, filename)) == join(pickle_test_env, new_filename)
最后一个猴子补丁将战胜所有其他猴子补丁,因此 input(f"overwrite {filename} (y/n): ")
正在获得 "n"
,input("new filename: ")
也是如此。为了以正确的顺序提供所需的输入,我们可以 monkeypatch 一个循环其响应的方法
responses = iter(['n', new_filename])
monkeypatch.setattr('builtins.input', lambda msg: next(responses))
请注意 responses
是一个迭代器对象——也就是说,对其调用 next()
将 return 列表中的下一个项目。如果调用 input()
的次数多于列表中的项目数,则会引发 StopIteration
。可以提供可选的默认值,避免 StopIteration
异常,并允许永远调用 input()
:
next(responses, '\n')
可能有一种更简洁的方法来为 input()
提供标准输入,但我现在不知所措。