Python отображает несколько возвращаемых значений

Я использую pythons mock.patch и хотел бы изменить возвращаемое значение для каждого вызова. Вот предостережение: функция патчей не имеет входов, поэтому я не могу изменить возвращаемое значение на основе ввода.

Вот мой код для справки.

def get_boolean_response():
    response = io.prompt('y/n').lower()
    while response not in ('y', 'n', 'yes', 'no'):
        io.echo('Not a valid input. Try again'])
        response = io.prompt('y/n').lower()

    return response in ('y', 'yes')

Мой тестовый код:

@mock.patch('io')
def test_get_boolean_response(self, mock_io):
    #setup
    mock_io.prompt.return_value = ['x','y']
    result = operations.get_boolean_response()

    #test
    self.assertTrue(result)
    self.assertEqual(mock_io.prompt.call_count, 2)

io.prompt - это просто независимая от платформы (python 2 и 3) версия "ввода". Поэтому в конечном итоге я пытаюсь издеваться над пользователями. Я попытался использовать список для возвращаемого значения, но это не работает.

Вы можете видеть, что если возвращаемое значение является чем-то недействительным, я просто получу бесконечный цикл здесь. Поэтому мне нужен способ в конечном итоге изменить возвращаемое значение, так что мой тест на самом деле заканчивается.

(еще один возможный способ ответить на этот вопрос может состоять в том, чтобы объяснить, как я мог имитировать ввод пользователя в модульном тесте)


Не дублировать этот вопрос в основном потому, что у меня нет возможности изменять входные данные.

Один из комментариев ответа на этот вопрос соответствует тем же самым строкам, но ответа/комментария не было предоставлено.

Ответ 1

Вы можете назначить iterable на side_effect, а макет будет возвращать следующее значение в последовательности каждый раз, когда он вызывается

>>> from unittest.mock import Mock
>>> m = Mock()
>>> m.side_effect = ['foo', 'bar', 'baz']
>>> m()
'foo'
>>> m()
'bar'
>>> m()
'baz'

Цитирование Mock() документации:

Если side_effect является итерируемым, каждый вызов макета возвращает следующее значение из итерабельного.

Как в стороне, тест response is not 'y' or 'n' or 'yes' or 'no' будет не работать; вы спрашиваете, истинно ли выражение (response is not 'y'), или 'y' истинно (всегда случай, непустая строка всегда истинна) и т.д. Различные выражения с обеих сторон операторов or независимы. См. Как проверить одну переменную на несколько значений?

Вы также не должны использовать is для тестирования строки. Интерпретатор CPython может повторно использовать строковые объекты при определенных обстоятельствах, но это не поведение, на которое вы должны рассчитывать.

Как таковой, используйте:

response not in ('y', 'n', 'yes', 'no')

вместо; это будет использовать тесты равенства (==), чтобы определить, ссылается ли response на строку с тем же содержимым (значением).

То же самое относится к response == 'y' or 'yes'; вместо этого используйте response in ('y', 'yes').