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


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.


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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.