Mockowanie klasy: Mock () czy patch ()?

116

Używam makiety w Pythonie i zastanawiałem się, które z tych dwóch podejść jest lepsze (czytaj: bardziej pythonowe).

Metoda pierwsza : po prostu utwórz pozorowany obiekt i użyj go. Kod wygląda następująco:

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)

Metoda druga : użyj łatki, aby utworzyć makietę. Kod wygląda następująco:

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

Obie metody robią to samo. Nie jestem pewien różnic.

Czy ktoś mógłby mnie oświecić?

Sardathrion - przeciwko nadużyciom SE
źródło
10
Jako osoba, która nigdy nie próbowała ani Mock (), ani patcha, uważam, że pierwsza wersja jest bardziej przejrzysta i pokazuje, co chcesz zrobić, mimo że nie rozumiem rzeczywistej różnicy. Nie wiem, czy to pomoże, czy nie, ale pomyślałem, że przydatne może być przekazanie, co może czuć niewtajemniczony programista.
Michael Brennan
2
@MichaelBrennan: Dziękuję za komentarz. Rzeczywiście jest to przydatne.
Sardathrion - przeciwko nadużyciom SE

Odpowiedzi:

151

mock.patchjest zupełnie innym stworzeniem niż mock.Mock. patch zamienia klasę na obiekt pozorowany i umożliwia pracę z instancją pozorowaną. Spójrz na ten fragment:

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

patchzastępuje MyClassw sposób, który pozwala kontrolować użycie klasy w wywoływanych funkcjach. Po załataniu klasy odwołania do klasy są całkowicie zastępowane przez pozorowaną instancję.

mock.patchjest zwykle używany podczas testowania czegoś, co tworzy nową instancję klasy w teście. mock.Mockwystąpienia są bardziej przejrzyste i preferowane. Jeśli twoja self.sut.somethingmetoda utworzyła instancję MyClasszamiast odbierania instancji jako parametru, mock.patchbyłoby to odpowiednie tutaj.

D.Shawley
źródło
2
@ D.Shawley, jak załatać klasę utworzoną wewnątrz innej klasy, która musi być w trakcie testowania.
ravi404
4
@ravz - przeczytaj „Where to Patch” . Jest to jedna z trudniejszych rzeczy do prawidłowego działania.
D.Shawley
Mój test próbny jest podobny do metody drugiej . Chcę, aby instancja MyClass zgłosiła wyjątek. Wypróbowałem zarówno mock.side_effect, jak i mock.return_value.side_effect i te nie działały. Co ja robię?
Hussain
5
@ D.Shawley Link jest uszkodzony, można go znaleźć teraz tutaj: „Where to Patch”
RazerM
2
Aby załatać obiekt klasy, zobacz stackoverflow.com/questions/8469680/ ...
storm_m2138
27

Mam na ten temat film z YouTube .

Krótka odpowiedź: używaj, mockgdy mijasz rzecz, z której chcesz się wyśmiać, a patchjeśli nie. Spośród tych dwóch zdecydowanie preferowany jest mock, ponieważ oznacza to, że piszesz kod z odpowiednim wstrzyknięciem zależności.

Głupi przykład:

# 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)
MikeTwo
źródło