Deridere una classe: Mock () o patch ()?


116

Sto usando mock con Python e mi chiedevo quale di questi due approcci sia migliore (leggi: più pitonico).

Metodo uno : crea semplicemente un oggetto fittizio e usalo. Il codice ha questo aspetto:

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)

Metodo due : usa la patch per creare un mock. Il codice ha questo aspetto:

@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)

Entrambi i metodi fanno la stessa cosa. Non sono sicuro delle differenze.

Qualcuno potrebbe illuminarmi?


10
Essendo una persona che non ha mai provato né Mock () né patch, sento che la prima versione è più chiara e mostra quello che vuoi fare, anche se non capisco l'effettiva differenza. Non so se questo sia di aiuto o meno, ma ho pensato che potesse essere utile per trasmettere ciò che potrebbe sentire un programmatore non iniziato.
Michael Brennan

2
@MichaelBrennan: grazie per il tuo commento. È davvero utile.
Sardathrion - contro l'abuso di SE il

Risposte:


151

mock.patchè una creatura molto molto diversa da mock.Mock. patch sostituisce la classe con un oggetto fittizio e consente di lavorare con l'istanza fittizia. Dai un'occhiata a questo frammento:

>>> 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>

patchsostituisce MyClassin un modo che ti consente di controllare l'utilizzo della classe nelle funzioni che chiami. Dopo aver applicato una patch a una classe, i riferimenti alla classe vengono completamente sostituiti dall'istanza fittizia.

mock.patchviene solitamente utilizzato quando si esegue il test di qualcosa che crea una nuova istanza di una classe all'interno del test. mock.Mockle istanze sono più chiare e sono preferite. Se il tuo self.sut.somethingmetodo ha creato un'istanza MyClassinvece di ricevere un'istanza come parametro, allora mock.patchsarebbe appropriato qui.


2
@ D.Shawley come applichiamo una patch a una classe istanziata all'interno di un'altra classe che deve essere sottoposta a test.
ravi404

4
@ravz - leggi "Where to Patch" . Questa è una delle cose più difficili da far funzionare correttamente.
D.Shawley

Il mio test di simulazione è simile al metodo due . Voglio che l'istanza MyClass sollevi un'eccezione. Ho provato sia mock.side_effect che mock.return_value.side_effect e quelli non hanno funzionato. Cosa faccio?
Hussain

5
@ D.Shawley Il collegamento è interrotto, può essere trovato qui ora: "Where to Patch"
RazerM

2
Per correggere un oggetto di classe vedere stackoverflow.com/questions/8469680/...
storm_m2138

27

Ho un video di YouTube su questo.

Risposta breve: usa mockquando passi la cosa che vuoi essere derisa e patchse non lo sei. Dei due, mock è fortemente preferito perché significa che stai scrivendo codice con una corretta iniezione delle dipendenze.

Esempio sciocco:

# 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.