Как смоделировать два последовательных ввода с консоли с помощью pytest monkeypatch - PullRequest
0 голосов
/ 30 января 2020

Модуль "overwrite_file" (см. Пример кода) запрашивает ввод нового имени файла, если первый пользовательский ввод отвечает "n"

В моей тестовой настройке я использую два последовательных вызова monkeypatch.setattr для смоделировать ввод. Результатом будет бесконечный l oop, если я использую следующий порядок:

monkeypatch.setattr('builtins.input', lambda overwrite: "n")
monkeypatch.setattr('builtins.input', lambda new_name: new_filename)

Второй вызов monkeypatch.setattr активирован, и переменной newwap назначена переменная new.pkl.

Если я изменяю порядок команд monkeypatch следующим образом:

monkeypatch.setattr('builtins.input', lambda new_name: new_filename)
monkeypatch.setattr('builtins.input', lambda overwrite: "n")

Я получаю AssertionError, поскольку 'n' присваивается переменной new_name и создается файл calld "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)

1 Ответ

1 голос
/ 31 января 2020

Последний обезьян-патч выиграет у всех остальных, поэтому 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() для него вернет следующий пункт в списке. Если input() вызывается больше раз, чем есть элементы в списке, StopIteration будет повышен. Может быть предоставлено необязательное значение по умолчанию, избегая исключения StopIteration и позволяя вызывать input() всегда и всегда:

next(responses, '\n')

Может быть более чистый способ предоставления stdin для input(), но сейчас я в растерянности.

...