Макет проти MagicMock


138

Я розумію, що MagicMock - це набір Mock, який автоматично виконує "магічні методи", таким чином, безперешкодно забезпечуючи підтримку списків, ітерацій і так далі ... Тоді в чому причина простого Mock ? Хіба це не просто знята версія MagicMock, яку практично можна ігнорувати? Чи знає клас Mock якісь хитрощі, які недоступні в MagicMock ?

Відповіді:


99

У чому причина існування простого Мока ?

Автор Мока Майкл Форд вирішив дуже схоже питання на Pycon 2011 (31:00) :

Питання: Чому MagicMock зробив окрему річ, а не просто скласти здатність в об'єкт макету за замовчуванням?

Відповідь: Один розумний відповідь полягає в тому, що MagicMock працює в тому, що він попередньо конфігурує всі ці методи протоколу, створюючи нові Mocks і встановлюючи їх, тож якщо кожен новий макет створював купу нових макетів і встановлював їх як протокольні методи, а потім усі ці протоколи методи створили купу більше глузувань і встановили їх на протокольні методи, у вас є нескінченна рекурсія ...

Що робити, якщо ви хочете отримати доступ до вашого макету як об'єкта контейнера помилково - ви не хочете, щоб це працювало? Якщо кожен макет автоматично отримує кожен метод протоколу, то це стає набагато складніше. Крім того, MagicMock виконує деякі з цих попередніх налаштувань для вас, встановлюючи значення повернення, які можуть бути невідповідними, тому я подумав, що було б краще мати цю зручність, у якій все налаштовано і доступно для вас, але ви також можете скористатися звичайним макетом об'єкт і просто налаштувати магічні методи, які ви хочете існувати ...

Проста відповідь: просто використовуйте MagicMock скрізь, якщо така поведінка ви хочете.


12
Я думаю, що краща відповідь: Використовуйте MagicMock, якщо ви знаєте, що робите, інакше використовуйте Mock.
laike9m

56

За допомогою Mock ви можете знущатися над магічними методами, але їх потрібно визначити. MagicMock має "реалізацію за замовчуванням більшості магічних методів". .

Якщо вам не потрібно перевіряти ніяких магічних методів, Mock є адекватним і не приносить у ваші тести багато сторонніх речей. Якщо вам потрібно перевірити багато магічних методів, MagicMock заощадить вам час.


Звичайно, я вже прочитав документацію. Це не дає відповіді на моє запитання - навіщо турбуватися з простим Mock, якщо MagicMock робить саме той самий плюс набагато більше? У своїх тестах я не бачу сторонніх речей - просто використовуйте іншу назву, і все. То де ж улов?
Володимир Ігнатов

39
Тести повинні бути мінімальними, а макетні об’єкти повинні бути мінімально функціональними, щоб ви точно були впевнені, що ви тестуєте. Якщо ви використовуєте MagicMock тільки тому, що це більше, але ви не явно тестуєте все те, що "більше", ви ризикуєте не виконати тест через поведінку MagicMock за замовчуванням. Цей збій може дещо відображати параметри за замовчуванням MagicMock, ніж те, про що слід знущатися. Ще гірше, ви ризикуєте успішним випробуванням, коли він мав би бути невдалим. Ризик невеликий, але якщо це станеться, він витратить багато вашого часу.
Шон Редмонд

1
Я думаю про це як простий JS vs Jquery. Звичайно, ви можете використовувати Jquery, щоб виконати всі свої JS, але в деяких випадках вам просто потрібно використовувати той самий мінімальний інструмент, необхідний для виконання роботи. Я вважаю, що випадки зазвичай є надзвичайно простими або надзвичайно складними.
притупляється

49

Для початку MagicMock- це підклас Mock.

class MagicMock(MagicMixin, Mock)

Як результат, MagicMock надає все, що надає Mock та багато іншого. Замість того, щоб думати про Mock як про зняту версію MagicMock, думайте про MagicMock як розширену версію Mock. Це має вирішити ваші запитання щодо того, чому Mock існує і що надає Mock поверх MagicMock.

По-друге, MagicMock забезпечує реалізацію за замовчуванням багатьох / найбільш магічних методів, тоді як Mock - ні. Дивіться тут для отримання додаткової інформації про надані магічні методи.

Деякі приклади наданих магічних методів:

>>> int(Mock())
TypeError: int() argument must be a string or a number, not 'Mock'
>>> int(MagicMock())
1
>>> len(Mock())
TypeError: object of type 'Mock' has no len()
>>> len(MagicMock())
0

І це можуть бути не такі інтуїтивні (принаймні, не інтуїтивні для мене):

>>> with MagicMock():
...     print 'hello world'
...
hello world
>>> MagicMock()[1]
<MagicMock name='mock.__getitem__()' id='4385349968'>

Ви можете "побачити" методи, додані до MagicMock, коли ці методи викликаються вперше:

>>> magic1 = MagicMock()
>>> dir(magic1)
['assert_any_call', 'assert_called_once_with', ...]
>>> int(magic1)
1
>>> dir(magic1)
['__int__', 'assert_any_call', 'assert_called_once_with', ...]
>>> len(magic1)
0
>>> dir(magic1)
['__int__', '__len__', 'assert_any_call', 'assert_called_once_with', ...]

Отже, чому б не використовувати MagicMock весь час?

Питання до вас: Чи добре ви з реалізацією магічних методів за замовчуванням? Наприклад, чи добре, mocked_object[1]щоб не помилитися? Ви не в порядку з будь-якими непередбачуваними наслідками через те, що чарівні способи реалізації вже є?

Якщо відповідь на ці питання - так, то продовжуйте використовувати MagicMock. Інакше дотримуйтесь Mock.



3

Я знайшов ще один конкретний випадок, коли простий Mock може виявитися кориснішим, ніж MagicMock:

In [1]: from unittest.mock import Mock, MagicMock, ANY
In [2]: mock = Mock()
In [3]: magic = MagicMock()
In [4]: mock.foo == ANY
Out[4]: True
In [5]: magic.foo == ANY
Out[5]: False

Порівняння проти ANYможе бути корисним, наприклад, порівнянням майже кожної клавіші між двома словниками, де деяке значення обчислюється за допомогою макету.

Це буде дійсно, якщо ви використовуєте Mock:


self.assertDictEqual(my_dict, {
  'hello': 'world',
  'another': ANY
})

при цьому він збільшить показник, AssertionErrorякщо ви звиклиMagicMock

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.