czym jest (małpa-)łatanie w Pythonie?
(monkey-) patching jest techniką zmiany zachowania kodu bez zmiany jego źródła. Odbywa się to w trybie runtime, zazwyczaj przez nadpisywanie atrybutów istniejących obiektów. Obiekt może być jakąś instancją, klasą lub nawet modułem. Technika ta jest najczęściej (ab) używana do testów, gdy nie możemy przejść mocks w prosty sposób.
kolejnym imponującym przykładem jest biblioteka gevent, która zamienia kod Synchroniczny w asynchroniczny za pomocą Monkey-patchingu.,
porównajmy go za pomocą wrk:
Gevent stworzył bibliotekę przyjazną koroutine i dzięki współbieżności umożliwił naszemu serwerowi przykładowemu obsługę ponad 13,5 razy większej liczby żądań na sekundę.
w końcu mamy program, który ma współbieżność opartą na koroutine (taka sama zasada jak w asyncio lub node.js), ale jego kod nadal wygląda jak synchroniczny. Nie potrzebujemy specjalnych, kooperacyjnych bibliotek ani asynchronicznych słów kluczowych w naszym kodzie. Prawie jak magia.
łatka w testach
Python zawiera narzędzie do łatania, czyli unittest.mock.patch., Domyślnym sposobem jej użycia jest udekorowanie naszej funkcji testowej. Załóżmy, że mamy widok Django, który wygląda tak…
i chcielibyśmy go przetestować. Zauważamy, że posiada zależność-ApiClient z innego modułu. Jeśli chcemy przetestować widok get_stats w przewidywalny, niezawodny sposób, musimy użyć test-double zamiast ApiClient. Nie ma jednak prostego sposobu, aby to zrobić. Jeśli został przekazany do get_stats jako argument, możemy po prostu przekazać Mock.,
1
2
3
4
|
z Def get_stats(żądanie, api_client_class):
…
api_client = api_client_class ()
…,
|
…ale tak nie jest. Nadal możemy użyć patch decorator, choć!
to jeszcze nie koniec, ale jeśli umieścimy debuggera w teście, zauważymy, że ApiClient.get_stats_for jest teraz MagicMock:
oznacza to, że nasze wyśmiewanie zakończyło się sukcesem. Problematyczne uzależnienie zastąpiliśmy makietą. Przy okazji, jeśli szukasz najlepszych praktyk korzystania z mocks, sprawdź mój (prawie) definitive guide o wyśmiewanie w Pythonie lub dlaczego wyśmiewanie może być niebezpieczne, gdy nadużywane.,
teraz test nadal nie powiedzie się, ponieważ get_stats otrzymuje MagicMock, podczas gdy oczekuje słownika. Musimy sparametryzować makietę. Możemy to zrobić przekazując drugi argument @patch:
Patch bez dekoratora
patch może być również użyty jako żłobek kontekstowy. Wynikiem powrotu będzie wstawiona w miejscu łatania atrybutu makieta:
„Patch Pythona nie działa!– – jak to zrobić dobrze?
czasami pojawia się sytuacja, kiedy pomimo obecności patch decoratora lub context managera, zależność będzie wyglądała tak, jakby w ogóle nie była łatana., Krótko mówiąc, może to być spowodowane tym, że istnieje wiele istniejących odniesień do rzeczy, którą próbujesz naprawić. Testowany kod używa jednego, ale udało Ci się naprawić drugi. Operacja zakończyła się sukcesem, ale pacjent zmarł. Co robić?
krótko mówiąc, musisz upewnić się, że łatasz to samo odniesienie, którego używa testowany kod.
Zobacz gdzie łatać sekcję unittest.przykładowa dokumentacja, aby uzyskać więcej szczegółów. Alternatywnie możesz użyć sprytnej alternatywy dla patcha, czyli patcha.obiekt.
patch.obiekt-prostszy do poprawnego
patch.,obiekt jest martwy prosty w użyciu – wystarczy zaimportować obiekt, którego atrybut chcesz łatać i zastosować łatkę.obiekt:
Jeśli chcesz użyć patcha.obiekt dla metody, importujesz klasę. Jeśli chcesz łatać.obiekt funkcji lub całej klasy, importuje moduł, w którym żyją.
czy należy łatać?
(małpa-) łatanie powinno być stosowane oszczędnie. To Twoja ostatnia deska ratunku. W moim kodzie nie mam innej opcji poza patchem dzięki dependency injection.
w dłuższej perspektywie cena za takie sztuczki jest bardzo, bardzo wysoka., Łatanie często oznacza dotykanie i zmienianie szczegółów implementacji w sposób nieprzewidziany przez autorów. Wprowadza to dodatkowe sprzężenie z rzeczami, które nie powinny go mieć. Oznacza to, że trudniej będzie je zmienić.
Jeśli naprawdę musisz, łataj tylko publiczne API innej biblioteki lub modułu w kodzie.
Dodaj komentarz