Знущання над класом: Mock () або patch ()?


116

Я використовую макет з Python і цікавився, який із цих двох підходів кращий (читайте: більш пітонічний).

Перший спосіб : Просто створіть макетний об’єкт і скористайтеся цим. Код виглядає так:

def test_one (self):
    mock = Mock()
    mock.method.return_value = True 
    self.sut.something(mock) # This should called mock.method and checks the result. 
    self.assertTrue(mock.method.called)

Спосіб другий : використовуйте патч для створення макету. Код виглядає так:

@patch("MyClass")
def test_two (self, mock):
    instance = mock.return_value
    instance.method.return_value = True
    self.sut.something(instance) # This should called mock.method and checks the result. 
    self.assertTrue(instance.method.called)

Обидва методи роблять те саме. Я не впевнений у відмінностях.

Може хтось мене просвітить?


10
Як людина, яка ніколи не пробувала ні Mock (), ні патч, я відчуваю, що перша версія зрозуміліша і показує, що ви хочете зробити, хоча я не розумію фактичної різниці. Я не знаю, чи допоможе це чи ні, але я подумав, що може бути корисним передати, що може відчути непроспілений програміст.
Майкл Бреннан

2
@MichaelBrennan: Дякую за Ваш коментар. Це дійсно корисно.
Сардатріон - проти зловживань SE

Відповіді:


151

mock.patchце дуже інший характер, ніж mock.Mock. patch замінює клас об'єктом макету і дозволяє працювати з екземпляром макету. Погляньте на цей фрагмент:

>>> class MyClass(object):
...   def __init__(self):
...     print 'Created MyClass@{0}'.format(id(self))
... 
>>> def create_instance():
...   return MyClass()
... 
>>> x = create_instance()
Created MyClass@4299548304
>>> 
>>> @mock.patch('__main__.MyClass')
... def create_instance2(MyClass):
...   MyClass.return_value = 'foo'
...   return create_instance()
... 
>>> i = create_instance2()
>>> i
'foo'
>>> def create_instance():
...   print MyClass
...   return MyClass()
...
>>> create_instance2()
<mock.Mock object at 0x100505d90>
'foo'
>>> create_instance()
<class '__main__.MyClass'>
Created MyClass@4300234128
<__main__.MyClass object at 0x100505d90>

patchзамінює MyClassтаким чином, що дозволяє контролювати використання класу у функціях, які ви викликаєте. Після виправлення класу посилання на клас повністю замінюються екземпляром макету.

mock.patchзазвичай використовується, коли ви тестуєте щось, що створює новий екземпляр класу всередині тесту. mock.Mockекземпляри більш чіткі і переважні. Якщо ваш self.sut.somethingметод створив екземпляр MyClassзамість отримання екземпляра в якості параметра, то тут mock.patchбуло б доречно.


2
@ D.Shawley, як ми переходимо до класу, який інстанціюється в іншому класі, який потрібно пройти тестування.
ravi404

4
@ravz - дайте прочитати "Куди виправити " . Це одна з найскладніших речей, щоб налагодити належну роботу.
Д.Шоулі

Мій макетний тест схожий на метод другий . Я хочу, щоб екземпляр MyClass створив виняток. Я спробував і mock.side_effect і mock.return_value.side_effect, і ті не спрацювали. Що мені робити?
Хусейн

5
@ D.Shawley Посилання розірвано, його можна знайти зараз: "Куди
виправити

2
Щоб залатати об'єкт класу см stackoverflow.com/questions/8469680 / ...
storm_m2138

27

У мене на цьому відео YouTube .

Коротка відповідь: Використовуйте, mockколи ви передаєте те, що ви хочете знущатися, а patchякщо ні. Із двох мотивів настійно бажано, оскільки це означає, що ви пишете код із належним введенням залежності.

Дурний приклад:

# Use a mock to test this.
my_custom_tweeter(twitter_api, sentence):
    sentence.replace('cks','x')   # We're cool and hip.
    twitter_api.send(sentence)

# Use a patch to mock out twitter_api. You have to patch the Twitter() module/class 
# and have it return a mock. Much uglier, but sometimes necessary.
my_badly_written_tweeter(sentence):
    twitter_api = Twitter(user="XXX", password="YYY")
    sentence.replace('cks','x') 
    twitter_api.send(sentence)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.