web-dev-qa-db-fra.com

Se moquer d'une classe: Mock () ou patch ()?

J'utilise mock avec Python) et je me demandais laquelle de ces deux approches était la meilleure (lire: plus de Pythonic).

Méthode un : Créez simplement un objet fictif et utilisez-le. Le code ressemble à:

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)

Méthode deux : Utilisez patch pour créer une maquette. Le code ressemble à:

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

Les deux méthodes font la même chose. Je ne suis pas sûr des différences.

Quelqu'un pourrait-il m'éclairer?

103
Sardathrion

mock.patch est une créature très très différente de mock.Mock. patch remplace la classe par un objet fictif et vous permet de travailler avec l'instance fictive. Jetez un coup d'œil à cet extrait:

>>> 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 remplace MyClass de manière à vous permettre de contrôler l'utilisation de la classe dans les fonctions que vous appelez. Une fois que vous corrigez une classe, les références à cette classe sont complètement remplacées par l'instance fictive.

mock.patch est généralement utilisé lorsque vous testez quelque chose qui crée une nouvelle instance d'une classe à l'intérieur du test. mock.Mock _ instances sont plus claires et sont préférées. Si votre self.sut.something La méthode a créé une instance de MyClass au lieu de recevoir une instance en tant que paramètre, puis mock.patch serait approprié ici.

141
D.Shawley

J'ai un vidéo YouTube à ce sujet.

Réponse courte: utilisez mock lorsque vous transmettez la chose que vous voulez simuler, et patch si vous ne l'êtes pas. Parmi les deux, fake est fortement préféré car cela signifie que vous écrivez du code avec une injection de dépendance appropriée.

Exemple idiot:

# 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)
18
MikeTwo