Python знущається з кількох повернених значень


168

Я використовую 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) версія "введення". Тому в кінцевому рахунку я намагаюся знущатися над введенням користувачів. Я намагався використовувати список для повернення значення, але це не працює.

Видно, що якщо значення повернення щось недійсне, я просто отримаю тут нескінченний цикл. Тому мені потрібен спосіб врешті-решт змінити значення повернення, щоб мій тест фактично закінчився.

(Ще одним можливим способом відповісти на це питання може бути пояснення, як я міг імітувати введення користувача в тестовому модулі)


Не є дублем цього питання головним чином тому, що я не маю можливості змінювати дані.

Один із зауважень відповіді на це запитання - це те саме, але жодної відповіді / коментаря не надано.


3
response is not 'y' or 'n' or 'yes' or 'no'в НЕ робити те , що ви думаєте , що він робить. Див. Як перевірити одну змінну на кілька значень? і ви не повинні використовувати isдля порівняння рядкових значень, використовувати ==для порівняння значень , а не тотожностей об'єкта.
Martijn Pieters

Тут також будьте обережні. Здається, ви намагаєтесь isпорівняти рядкові букви. Не робіть цього. Те, що він працює (іноді), є лише деталізацією реалізації в CPython. Крім того, response is not 'y' or 'n' or 'yes' or 'no'напевно, не робиш того, що ти думаєш, що це ...
mgilson

Відповіді:


300

Ви можете призначити Iterable To 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')замість цього.


Чи є спосіб це зробити зі стандартом mock? Чи є спосіб використовувати патч з MagicMock, як я роблю зі стандартним макетом?
Нік Гумріч

@Humdinger: Це особливість Mockкласу stardard .
Martijn Pieters

17
Здається, що призначення списку працює лише з python 3. Тестування з python 2.7 Мені потрібно використовувати ітератор ( m.side_effect = iter(['foo', 'bar', 'baz'])).
user686249

1
@ user686249: Я дійсно можу це відтворити, тому що вивільнення методу створює lambda(функцію), а не a MagicMock. Функція об'єкт не може мати властивості, тому side_effectатрибут повинен бути ітерацію. Не варто припускати подібного методу. Краще використовувати mock.patch.object(requests.Session, 'post'); що призводить до отримання об'єкта патчера, який належним чином автоматично визначає метод та підтримує side_effectналежну підтримку .
Martijn Pieters

3
@ JoeMjr2: Коли ітератор вичерпаний, StopIterationйого піднімають. Ви можете використовувати будь-який ітератор, так що ви можете використовувати його itertools.chain(['Foo'], itertools.repeat('Bar'))для продукування Fooодин раз, а потім назавжди Bar.
Мартійн Пітерс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.