Dlaczego Mockito nie kpi z metod statycznych?


267

Przeczytałem tutaj kilka wątków na temat metod statycznych i myślę, że rozumiem problemy, które mogą powodować niewłaściwe użycie / nadmierne użycie metod statycznych. Ale tak naprawdę nie do końca zrozumiałem, dlaczego trudno wyśmiewać metody statyczne.

Wiem, że inne fałszywe frameworki, takie jak PowerMock, mogą to zrobić, ale dlaczego Mockito nie może?

Czytam ten artykuł , ale autor wydaje się być religijnie przeciwny temu słowu static, może to moje słabe zrozumienie.

Świetne byłoby proste wyjaśnienie / link.


12
Na marginesie: PowerMock nie jest sam w sobie fałszywą biblioteką obiektów, po prostu dodaje te funkcje (kpiny i statystyki) do innych bibliotek. Używamy PowerMock + Mockito w pracy, które dobrze się unoszą.
Matthias,

Odpowiedzi:


238

Myślę, że powodem może być to, że fałszywe biblioteki obiektów zwykle tworzą symulacje poprzez dynamiczne tworzenie klas w czasie wykonywania (przy użyciu cglib ). Oznacza to, że albo implementują interfejs w czasie wykonywania (to robi EasyMock, jeśli się nie mylę), albo dziedziczą od klasy do kpiny (to właśnie robi Mockito, jeśli się nie mylę). Oba podejścia nie działają dla elementów statycznych, ponieważ nie można ich zastąpić za pomocą dziedziczenia.

Jedynym sposobem na wyśmiewanie statyki jest modyfikacja kodu bajtowego klasy w czasie wykonywania, co, jak sądzę, jest nieco bardziej zaangażowane niż dziedziczenie.

Zgaduję, że warto ...


7
Nawiasem mówiąc, to samo dotyczy wyśmiewania konstruktorów. Te też nie mogą zostać zmienione przez dziedziczenie.
Matthias,

11
Warto również dodać, że niektórzy zwolennicy TDD / TBD postrzegają brak statycznej metody i kpiny z konstruktorów jako dobrą rzecz. Twierdzą, że gdy musisz wyśmiewać metody statyczne lub konstruktory, jest to wskaźnik złego projektu klasy. Na przykład, postępując zgodnie z purystycznym podejściem do IoC przy składaniu modułów kodu, nigdy nawet nie będziesz musiał kpić ze statyki lub lekarzy w pierwszej kolejności (chyba że są one oczywiście częścią jakiegoś elementu czarnej skrzynki). Zobacz także giorgiosironi.blogspot.com/2009/11/…
Matthias

200
Uważam, że narzędzia kpienia powinny dać ci to, czego potrzebujesz, nie zakładając, że wiedzą, co jest dla ciebie lepsze. Na przykład, jeśli korzystałem z biblioteki innej firmy, która korzystała ze statycznego wywołania metody, którą musiałem wyśmiewać, byłoby miło móc to zrobić. Pomysł, że fałszywy framework nie zapewni ci pewnych możliwości, ponieważ jest postrzegany jako zły projekt, jest zasadniczo wadliwy.
Lo-Tan,

11
@ Lo-Tan - to tak, jakby powiedzieć, że język powinien być zdolny do wszystkiego, nie zakładając, że zna lepiej niż ty. To tylko twoja próżność, ponieważ wyglądają imponująco. Problem polega na tym, że bitwa „anty / pro static” nie jest jasna, podobnie jak ramy. Zgadzam się, że powinniśmy mieć jedno i drugie. Ale tam, gdzie fakty jasne, wolę ramy, które narzucają te fakty. To jeden ze sposobów uczenia się - narzędzia utrzymujące cię na drodze. Więc ty nie musisz. Ale teraz każda głowa makaronu może narzucić swój tak zwany „dobry projekt”. „Zasadniczo wadliwy” ...
nevvermind

13
@nevvermind Eh? Język wysokiego poziomu ma na celu pomóc ci i mieć niezbędne abstrakcje, abyś mógł skupić się na ważnych utworach. Biblioteka testowa to narzędzie - narzędzie, którego używam do tworzenia lepszej jakości i, mam nadzieję, lepiej zaprojektowanego kodu. Jaki jest sens biblioteki testowej / próbnej, gdy ma ona ograniczenia, które mogą oznaczać, że nie mogę jej użyć, gdy muszę zintegrować źle zaprojektowany kod innej osoby? Nie wydaje się dobrze przemyślane, podczas gdy dobre języki były .
Lo-Tan,

28

Jeśli musisz wyśmiewać metodę statyczną, jest to silny wskaźnik złego projektu. Zwykle kpisz z zależności testowanej klasy. Jeśli testowana klasa odwołuje się do metody statycznej - na przykład java.util.Math # sin - oznacza to, że testowana klasa potrzebuje dokładnie tej implementacji (na przykład dokładności w porównaniu do prędkości). Jeśli chcesz oderwać się od konkretnej implementacji zatoki, prawdopodobnie potrzebujesz interfejsu (widzisz, dokąd to zmierza)?


3
Cóż, zastosowałem metody statyczne, aby zapewnić abstrakcje na wysokim poziomie, takie jak „fasada trwałej statyczności”. Taka fasada chroni kod klienta przed złożonością i szczegółami niskiego poziomu interfejsu API ORM, zapewniając bardziej spójny i łatwy w użyciu interfejs API, jednocześnie zapewniając dużą elastyczność.
Rogério,

I dlaczego musisz się z tego kpić? Jeśli zależysz na metodzie statycznej, to „jednostka” lub „moduł” nie jest samą klasą, ale obejmuje również „fasadę trwałości statycznej”.
stycznia 11

88
To prawda, ale czasami możesz nie mieć wyboru, jeśli na przykład musisz wyśmiewać metodę statyczną, która należy do klasy innej firmy.
Stijn Geukens

6
To prawda, ale czasami mamy do czynienia z singletonami.
Manu Manjunath

Jedyną myślą, której nie można rozwiązać za pomocą abstrakcji, jest zbyt wiele poziomów abstrakcji ... Dodanie warstw abstrakcji zwiększa złożoność i często jest niepotrzebne. Myślę o przykładach, które widziałem w ramach, które próbują wyśmiewać System.currentTimeMillis () poprzez zawinięcie tego prostego wywołania w jedną klasę. W efekcie uzyskujemy klasę singleton na metodę zamiast po prostu metod - tylko w celu ułatwienia testowania. A potem, kiedy wprowadzacie niezależną osobę, która wywołuje metodę statyczną bezpośrednio zamiast przez opakowanie singletona, testy i tak kończą się niepowodzeniem ...
Ks. Jeremy Krieg,

5

Poważnie myślę, że to też zapach kodu, jeśli trzeba kpić z metod statycznych.

  • Statyczne metody dostępu do wspólnej funkcjonalności? -> Użyj instancji singletonu i wstrzyknij ją
  • Kod innej firmy? -> Zawiń go we własny interfejs / delegata (i jeśli to konieczne, uczyń go singletonem)

Jedyny raz, kiedy wydaje mi się to przesadą, to liby takie jak Guava, ale nie powinieneś kpić z tego rodzaju, ponieważ jest to część logiki ... (rzeczy takie jak Iterables.transform (..))
W ten sposób twój własny kod pozostaje czysty, możesz wyśmiewać wszystkie swoje zależności w czysty sposób i masz warstwę antykorupcyjną przeciwko zależnościom zewnętrznym. Widziałem PowerMocka w praktyce i wszystkie klasy, do których potrzebowaliśmy, były źle zaprojektowane. Również integracja PowerMock czasami powodowała poważne problemy
(np. Https://code.google.com/p/powermock/issues/detail?id=355 )

PS: To samo dotyczy metod prywatnych. Nie sądzę, żeby testy powinny wiedzieć o szczegółach prywatnych metod. Jeśli klasa jest tak złożona, że ​​kusi, aby kpić z prywatnych metod, prawdopodobnie jest to znak, aby ją podzielić ...


2
Singleton sprawi, że napotkasz różnego rodzaju problemy, szczególnie gdy zdasz sobie sprawę, że tak naprawdę potrzebujesz więcej niż jednej instancji, a teraz musisz przebudować cały system, aby tak się stało.
Ricardo Freitas,

Nie powiedziałem, że wszystkim polecam wzorzec Singleton. Chodziło mi o to, że gdybym musiał wybierać między statyczną klasą użyteczności a Singletonem oferującym tę samą funkcjonalność, wybrałbym Singleton. A jeśli klasa jest singletonem, czy nie, powinna być kontrolowana przez framework DI, w mojej klasie I @Inject SomeDependencyoraz w mojej konfiguracji, którą definiuję bind(SomeDependency.class).in(Singleton.class). Dlatego jeśli jutro nie będzie już Singletonem, zmieniam jedną konfigurację i to wszystko.
pete83

@ pete83 Słyszę cię bracie. Mam jednak problem z bibliotekami testowymi lub frameworkami wymagającymi od deweloperów zmiany projektu w celu spełnienia wymagań / limitów struktury testowej. To znaczy, że IMO stawia wózek przed koniem lub ogonem macha psem.
Matt Campbell

1
Ten argument nie ma dla mnie sensu. Wzory singletonów od lat nie sprzyjają, ze zbyt wielu powodów, by je tutaj wymieniać. Co stanowi „czysty” kod? Jeśli mam metodę instancji klasy, która wywołuje metodę statycznego pomocnika, która zwraca niektóre operacje We / Wy, dlaczego nie miałbym chcieć, aby była wyśmiewana w teście? A jak to słaby projekt? Wszystkie te wykręcające się wokół otaczające kpiące metody statyczne nie sumują się. Wyśmiewanie metody jest przeciwieństwem jej przetestowania. Jeśli jest to zbyt trudne do wdrożenia, po prostu powiedz to i
skończ

O rany, nigdy nie mówiłem o tym oldschoolowym wzorze Singletona, do którego wszyscy dzwonią Foo.getInstance()wszędzie. Właśnie napisałem singleton w odpowiedzi, aby odeprzeć argument „ale metoda statyczna nie wymaga tworzenia wielu obiektów opakowania”. Również koncepcyjnie dla mnie nie ma różnicy między metodą statyczną a metodą instancji w singletonie, tyle że nie można wyśmiewać tego współpracownika singletona. Ale singleton czy nie nie jest absolutnie tym, o co starałem się zrobić, chodzi o to, aby wstrzykiwać i wyśmiewać współpracowników i nie wywoływać metod statycznych, jeśli utrudnia to testowanie.
pete83

4

Mockito zwraca obiekty, ale statyczny oznacza „poziom klasy, a nie poziom obiektu”. Zatem mockito da wyjątek zerowy dla statycznego.


0

W niektórych przypadkach metody statyczne mogą być trudne do przetestowania, szczególnie jeśli trzeba je wyśmiewać, dlatego większość fałszywych ram nie obsługuje ich. Uważam, że ten post na blogu jest bardzo przydatny w określaniu, jak kpić z metod i klas statycznych.


1
Wyśmiewanie metod statycznych jest nawet łatwiejsze niż wyśmiewanie metod instancji (ponieważ nie ma instancji), gdy używa się odpowiedniego kpiącego interfejsu API.
Rogério,

To tak, jakby odpowiedzieć na pytanie samym pytaniem, dlatego trudno to zrobić, na co nie jest to odpowiedź.
Matthias

40
Zanotowałem to, ponieważ post na blogu zaleca kosztowne obejście (refaktoryzacja kodu produkcyjnego), zamiast faktycznie rozwiązać problem izolacji klasy od metod statycznych, z których się korzysta. IMO, narzędzie szydercze, które naprawdę wykonuje to zadanie, nie dyskryminuje żadnych metod; deweloper powinien mieć swobodę decydowania, czy użycie metod statycznych jest dobre, czy złe w danej sytuacji, zamiast być zmuszanym do przejścia jedną ścieżką.
Rogério,
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.