Макет против MagicMock

138

Насколько я понимаю, MagicMock - это надмножество Mock, которое автоматически выполняет «магические методы», таким образом, беспрепятственно обеспечивая поддержку списков, итераций и так далее ... Тогда в чем причина существования простого Mock ? Разве это не просто урезанная версия MagicMock, которую можно практически игнорировать? Имеет ли Mock знать класс любые трюки, которые не доступны в MagicMock ?

Владимир Игнатов
источник

Ответы:

99

В чем причина существования простого Mock ?

Автор Mock, Майкл Фурд, обратился к очень похожему вопросу на Pycon 2011 (31:00) :

В: Почему MagicMock был создан отдельно, а не просто складывал способность в макет объекта по умолчанию?

A: Один разумный ответ заключается в том, что MagicMock работает так, что он предварительно конфигурирует все эти методы протокола, создавая новые Mocks и устанавливая их, поэтому, если каждый новый mock создает кучу новых mock и устанавливает их как методы протокола, а затем все эти протоколы методы создали кучу других макетов и установили их на свои методы протокола, у вас есть бесконечная рекурсия ...

Что если вы хотите, чтобы доступ к макету как к объекту контейнера был ошибкой - вы не хотите, чтобы это работало? Если у каждого макета автоматически есть каждый метод протокола, тогда это становится намного сложнее. Кроме того, MagicMock делает некоторые из этих предварительных настроек для вас, устанавливая возвращаемые значения, которые могут не подходить, поэтому я подумал, что было бы лучше иметь этот удобный, который имеет все предварительно сконфигурированные и доступные для вас, но вы также можете взять обычный макет объект и просто настроить магические методы, которые вы хотите существовать ...

Ответ прост: просто используйте MagicMock везде, если вы хотите именно такого поведения.

Райн Эверетт
источник
12
Я думаю, что лучший ответ: используйте MagicMock, если вы знаете, что делаете, в противном случае используйте Mock.
laike9m
56

С Mock вы можете издеваться над магическими методами, но вы должны определить их. MagicMock имеет «стандартные реализации большинства магических методов». ,

Если вам не нужно тестировать какие-либо магические методы, Mock подходит и не вносит много посторонних вещей в ваши тесты. Если вам нужно протестировать множество магических методов, MagicMock сэкономит вам время.

Шон Редмонд
источник
Конечно же, я уже прочитал документацию. Это не отвечает на мой вопрос - зачем беспокоиться о простом Mock, если MagicMock делает то же самое и намного больше? Я не вижу ничего постороннего в моих тестах - просто использую другое имя и все. Так в чем же подвох?
Владимир Игнатов
39
Тесты должны быть минимальными, а фиктивные объекты должны быть минимально функциональными, чтобы вы точно знали, что тестируете. Если вы используете MagicMock только потому, что он делает больше, но вы явно не тестируете все это, вы рискуете потерпеть неудачу из-за поведения MagicMock по умолчанию. Этот сбой может отражать нечто большее по умолчанию в MagicMock, чем то, что он должен издеваться. Еще хуже, вы рискуете испытания следующего , когда он должен не увенчались успехом. Риск невелик, но если это произойдет, это потратит много времени.
Шон Редмонд
1
Я думаю об этом, как об использовании простого JS против 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.

user650654
источник
12

Вот что говорится в официальной документации Python :

В большинстве этих примеров классы Mock и MagicMock являются взаимозаменяемыми. Поскольку MagicMock является более способным классом, он делает разумным использование по умолчанию.

user2916464
источник
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

Manu
источник