Mock vs MagicMock

138

Rozumiem, że MagicMock jest nadzbiorem Mocka, który automatycznie wykonuje „magiczne metody”, zapewniając w ten sposób bezproblemową obsługę list, iteracji itd. Więc jaki jest powód istnienia zwykłego Mocka ? Czy to nie tylko okrojona wersja MagicMock, którą można praktycznie zignorować? Czy klasa Mock zna jakieś sztuczki, które nie są dostępne w MagicMock ?

Vladimir Ignatov
źródło

Odpowiedzi:

99

Jaki jest powód istnienia zwykłego Mocka ?

Autor Mocka, Michael Foord, skierował bardzo podobne pytanie na Pycon 2011 (31:00) :

P: Dlaczego MagicMock został utworzony jako osobna rzecz, a nie tylko składając zdolność do domyślnego obiektu pozorowanego?

Odp .: Jedną rozsądną odpowiedzią jest to, że sposób działania MagicMock polega na tym, że wstępnie konfiguruje wszystkie te metody protokołów, tworząc nowe makiety i ustawiając je, więc jeśli każda nowa próba tworzy kilka nowych makiet i ustawia je jako metody protokołów, a następnie wszystkie te protokoły metody stworzyły więcej makiet i ustawiły je na swoich metodach protokołów, masz nieskończoną rekursję ...

A co jeśli chcesz, aby dostęp do makiety jako obiektu kontenera był błędem - nie chcesz, aby to działało? Jeśli każdy model ma automatycznie wszystkie metody protokołu, wykonanie tego staje się znacznie trudniejsze. Ponadto MagicMock wykonuje część tej wstępnej konfiguracji za Ciebie, ustawiając wartości zwracane, które mogą nie być odpowiednie, więc pomyślałem, że lepiej byłoby mieć tę wygodną, ​​która ma wszystko wstępnie skonfigurowane i dostępne dla Ciebie, ale możesz też wziąć zwykłą próbę obiekt i po prostu skonfiguruj magiczne metody, które chcesz istnieć ...

Prosta odpowiedź brzmi: po prostu używaj MagicMock wszędzie, jeśli chcesz takiego zachowania.

Ryne Everett
źródło
12
Myślę, że lepszą odpowiedzią jest: użyj MagicMock, jeśli wiesz, co robisz, w przeciwnym razie użyj Mocka.
laike9m
56

Z Mock Państwo mogą drwić magiczne metody, ale trzeba je zdefiniować. MagicMock ma „domyślne implementacje większości metod magicznych”. .

Jeśli nie musisz testować żadnych magicznych metod, Mock jest wystarczający i nie wnosi do testów wielu obcych rzeczy. Jeśli potrzebujesz przetestować wiele magicznych metod, MagicMock pozwoli Ci zaoszczędzić trochę czasu.

Sean Redmond
źródło
Rzeczywiście, przeczytałem już dokumentację. To nie odpowiada na moje pytanie - po co zawracać sobie głowę zwykłym Mockiem, jeśli MagicMock robi dokładnie to samo, a nawet znacznie więcej? W moich testach nie widzę żadnych obcych rzeczy - po prostu użyj innej nazwy i to wszystko. Więc gdzie jest haczyk?
Vladimir Ignatov
39
Testy powinny być minimalne, a pozorowane obiekty powinny być minimalnie funkcjonalne, aby mieć pewność, co dokładnie testujesz. Jeśli używasz MagicMock tylko dlatego, że robi więcej, ale nie testujesz w sposób jawny tego „więcej”, ryzykujesz, że test zakończy się niepowodzeniem z powodu domyślnego zachowania MagicMock. Ta awaria może odzwierciedlać coś więcej o domyślnych ustawieniach MagicMock niż to, co ma kpić. Co gorsza, ryzykujesz, że test zakończy się sukcesem, gdy powinien się nie udać. Ryzyko jest niewielkie, ale jeśli tak się stanie, spowoduje to stratę czasu.
Sean Redmond
1
Myślę o tym jak o używaniu zwykłego JS vs Jquery. Jasne, mógł użyć jQuery wykonywać wszystkie twe JS, ale w niektórych przypadkach po prostu chcesz użyć bardzo minimalny narzędzie wymagane do wykonania swojej pracy. Uważam, że te przypadki są zwykle albo bardzo proste, albo niezwykle złożone.
tuli się
49

Przede wszystkim MagicMockjest podklasą Mock.

class MagicMock(MagicMixin, Mock)

W rezultacie MagicMock zapewnia wszystko, co zapewnia Mock, a nawet więcej. Zamiast myśleć o Mocku jako o okrojonej wersji MagicMock, pomyśl o MagicMock jako rozszerzonej wersji Mocka. Powinno to odpowiedzieć na Twoje pytania o to, dlaczego Mock istnieje i co zapewnia Mock oprócz MagicMock.

Po drugie, MagicMock zapewnia domyślne implementacje wielu / większości magicznych metod, podczas gdy Mock nie. Zobacz tutaj, aby uzyskać więcej informacji na temat dostępnych metod magicznych.

Kilka przykładów dostarczonych metod magicznych:

>>> int(Mock())
TypeError: int() argument must be a string or a number, not 'Mock'
>>> int(MagicMock())
1
>>> len(Mock())
TypeError: object of type 'Mock' has no len()
>>> len(MagicMock())
0

I te, które mogą nie być tak intuicyjne (a przynajmniej dla mnie nie intuicyjne):

>>> with MagicMock():
...     print 'hello world'
...
hello world
>>> MagicMock()[1]
<MagicMock name='mock.__getitem__()' id='4385349968'>

Możesz "zobaczyć" metody dodane do MagicMock, gdy te metody są wywoływane po raz pierwszy:

>>> magic1 = MagicMock()
>>> dir(magic1)
['assert_any_call', 'assert_called_once_with', ...]
>>> int(magic1)
1
>>> dir(magic1)
['__int__', 'assert_any_call', 'assert_called_once_with', ...]
>>> len(magic1)
0
>>> dir(magic1)
['__int__', '__len__', 'assert_any_call', 'assert_called_once_with', ...]

Dlaczego więc nie używać MagicMock przez cały czas?

Pytanie do ciebie brzmi: czy zgadzasz się z domyślnymi implementacjami metod magii? Na przykład, czy mocked_object[1]można nie popełniać błędów? Czy nie przeszkadzają ci niezamierzone konsekwencje z powodu istniejących już implementacji magicznych metod?

Jeśli odpowiedź na te pytania brzmi „tak”, użyj programu MagicMock. W przeciwnym razie trzymaj się Mocka.

user650654
źródło
12

Oto, co mówi oficjalna dokumentacja Pythona :

W większości tych przykładów klasy Mock i MagicMock są wymienne. Ponieważ MagicMock jest klasą o większych możliwościach, domyślnie jest to rozsądna klasa.

user2916464
źródło
3

Znalazłem inny szczególny przypadek, w którym proste Mock może okazać się bardziej przydatne niż MagicMock:

In [1]: from unittest.mock import Mock, MagicMock, ANY
In [2]: mock = Mock()
In [3]: magic = MagicMock()
In [4]: mock.foo == ANY
Out[4]: True
In [5]: magic.foo == ANY
Out[5]: False

Porównywanie z ANYmoże być przydatne, na przykład porównując prawie każdy klucz między dwoma słownikami, w których pewna wartość jest obliczana za pomocą makiety.

Będzie to ważne, jeśli używasz Mock:


self.assertDictEqual(my_dict, {
  'hello': 'world',
  'another': ANY
})

podczas gdy podniesie, AssertionErrorjeśli użyłeśMagicMock

Manu
źródło