Reputation: 48
The module "overwrite_file" (see code example) asks for input of a new filename if the first user input is answered with "n"
In my test setup I use two consecutive monkeypatch.setattr calls to simulate the input. The result is an endless loop if i use the order as follws:
monkeypatch.setattr('builtins.input', lambda overwrite: "n")
monkeypatch.setattr('builtins.input', lambda new_name: new_filename)
The second monkeypatch.setattr call is activated and 'new.pkl' is assigned to the variable overwrite.
If I change the order of the monkeypatch commands as so:
monkeypatch.setattr('builtins.input', lambda new_name: new_filename)
monkeypatch.setattr('builtins.input', lambda overwrite: "n")
I get an AssertionError as 'n' is assigned to the variable new_name and a file calld "n" is created.
How do I get the intended test functionality?
Interpreter: 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)
Upvotes: 3
Views: 2710
Reputation: 6105
The last monkeypatch will win out against all the others, so input(f"overwrite {filename} (y/n): ")
is getting "n"
, and so is input("new filename: ")
. To provide the desired inputs in the correct order, we can monkeypatch a method that will cycle its responses
responses = iter(['n', new_filename])
monkeypatch.setattr('builtins.input', lambda msg: next(responses))
Note that responses
is an iterator object — that is, calling next()
on it will return the next item in the list. If input()
is called more times than there are items in the list, StopIteration
will be raised. An optional default value may be provided, avoiding the StopIteration
exception, and allowing input()
to be called forever and ever:
next(responses, '\n')
There may be a cleaner way to provide stdin to input()
, but I'm at a loss at the moment.
Upvotes: 7