Jak przetestować jednostkę (używając xUnit) klasy, która ma wewnętrzne metody prywatne, pola lub klasy zagnieżdżone? A może funkcja, która jest prywatna dzięki wewnętrznemu powiązaniu ( static
w C / C ++) lub jest w prywatnej ( anonimowej ) przestrzeni nazw?
Zmiana modyfikatora dostępu do metody lub funkcji wydaje się zła, aby móc uruchomić test.
java
unit-testing
tdd
Raedwald
źródło
źródło
Odpowiedzi:
Jeśli masz nieco starszą aplikację Java i nie możesz zmienić widoczności swoich metod, najlepszym sposobem na przetestowanie metod prywatnych jest użycie refleksji .
Wewnętrznie używamy pomocników do pobierania / ustawiania
private
iprivate static
zmiennych, a także wywoływaniaprivate
iprivate static
metod. Poniższe wzory pozwolą ci zrobić prawie wszystko, co jest związane z prywatnymi metodami i polami. Oczywiście nie można zmieniaćprivate static final
zmiennych poprzez odbicie.A dla pól:
źródło
Najlepszym sposobem przetestowania metody prywatnej jest inna metoda publiczna. Jeśli nie można tego zrobić, spełniony jest jeden z następujących warunków:
źródło
Kiedy mam prywatne metody w klasie, które są na tyle skomplikowane, że odczuwam potrzebę bezpośredniego przetestowania metod prywatnych, to jest zapach kodu: moja klasa jest zbyt skomplikowana.
Moje zwykłe podejście do rozwiązywania takich problemów polega na wprowadzeniu nowej klasy, która zawiera interesujące elementy. Często ta metoda i pola, z którymi wchodzi w interakcje, a może inna metoda lub dwie mogą zostać wyodrębnione do nowej klasy.
Nowa klasa ujawnia te metody jako „publiczne”, więc są one dostępne do testów jednostkowych. Zarówno nowa, jak i stara klasa są teraz prostsze niż klasa oryginalna, co jest dla mnie świetne (muszę zachować prostotę, inaczej się zgubię!).
Pamiętaj, że nie sugeruję, aby ludzie tworzyli zajęcia bez korzystania z mózgu! Chodzi o to, aby użyć siły testowania jednostkowego, aby pomóc ci znaleźć dobre nowe klasy.
źródło
W przeszłości korzystałem z refleksji, aby to zrobić dla Javy, i moim zdaniem był to duży błąd.
Ściśle rzecz biorąc, powinno nie być pisanie testów jednostkowych, które bezpośrednio testowania metod prywatnych. To, co powinieneś przetestować, to umowa publiczna, którą klasa ma z innymi przedmiotami; nigdy nie należy bezpośrednio testować wewnętrznych elementów obiektu. Jeśli inny programista chce wprowadzić niewielką wewnętrzną zmianę w klasie, która nie wpływa na zamówienie publiczne na zajęcia, musi zmodyfikować test oparty na refleksji, aby upewnić się, że działa. Jeśli robisz to wielokrotnie w trakcie projektu, testy jednostkowe przestają być użytecznym pomiarem stanu kodu i stają się przeszkodą w rozwoju oraz irytacją dla zespołu programistów.
Zamiast tego zalecam korzystanie z narzędzia pokrycia kodu, takiego jak Cobertura, aby upewnić się, że pisane testy jednostkowe zapewniają przyzwoite pokrycie kodu metodami prywatnymi. W ten sposób pośrednio testujesz, co robią prywatne metody, i utrzymujesz wyższy poziom zwinności.
źródło
Z tego artykułu: Testowanie metod prywatnych za pomocą JUnit i SuiteRunner (Bill Venners), w zasadzie masz 4 opcje:
źródło
Zasadniczo test jednostkowy ma na celu wykonanie publicznego interfejsu klasy lub jednostki. W związku z tym metody prywatne są szczegółami implementacji, których nie można oczekiwać bezpośrednio testować.
źródło
Tylko dwa przykłady, w których chciałbym przetestować metodę prywatną:
SecurityManager
nie jest skonfigurowane, aby temu zapobiec).Rozumiem pomysł testowania tylko „kontraktu”. Ale nie widzę, aby można było opowiadać się za nie testowaniem kodu - przebieg może się różnić.
Więc mój kompromis polega na komplikowaniu JUnits refleksją, a nie na naruszeniu bezpieczeństwa i zestawu SDK.
źródło
private
nie jest to jedyna alternatywa. Brak modyfikatora dostępupackage private
oznacza, że można go testować jednostkowo, dopóki test jednostkowy trwa w tym samym pakiecie.Metody prywatne są wywoływane przez metodę publiczną, więc dane wejściowe do metod publicznych powinny również testować metody prywatne wywoływane przez te metody publiczne. Gdy metoda publiczna zawiedzie, może to oznaczać awarię metody prywatnej.
źródło
Innym podejściem, którego użyłem, jest zmiana prywatnej metody na prywatną lub chronioną, a następnie uzupełnienie jej adnotacją @VisibleForTesting biblioteki Google Guava.
To powie każdemu, kto używa tej metody, aby zachował ostrożność i nie uzyskał bezpośredniego dostępu nawet w pakiecie. Również klasa testowa nie musi znajdować się fizycznie w tym samym pakiecie, ale w tym samym pakiecie w folderze testowym .
Na przykład, jeśli istnieje metoda, która ma zostać przetestowana,
src/main/java/mypackage/MyClass.java
wówczas należy wprowadzić wywołanie testowesrc/test/java/mypackage/MyClassTest.java
. W ten sposób masz dostęp do metody testowej w swojej klasie testowej.źródło
Aby przetestować starszy kod za pomocą dużych i dziwacznych klas, często bardzo pomocne jest przetestowanie jednej prywatnej (lub publicznej) metody, którą piszę teraz .
Korzystam z pakietu junitx.util.PrivateAccessor dla Java. Wiele pomocnych jedno-liniowych narzędzi umożliwiających dostęp do prywatnych metod i prywatnych pól.
źródło
Class
jako pierwszego parametru w tych metodach, masz dostęp tylko dostatic
członków.W Spring Framework możesz testować prywatne metody przy użyciu tej metody:
Na przykład:
źródło
Po wypróbowaniu rozwiązania Cem Catikkas za pomocą odbicia dla Javy, muszę powiedzieć, że jego rozwiązanie było bardziej eleganckie niż to tutaj opisałem. Jeśli jednak szukasz alternatywy dla używania refleksji i masz dostęp do testowanego źródła, nadal będzie to możliwe.
Możliwe są zalety testowania prywatnych metod klasy, szczególnie w przypadku programowania opartego na testach , w którym chciałbyś zaprojektować małe testy przed napisaniem jakiegokolwiek kodu.
Utworzenie testu z dostępem do prywatnych członków i metod może testować obszary kodu, które są trudne do ukierunkowania, szczególnie z dostępem tylko do metod publicznych. Jeśli metoda publiczna obejmuje kilka etapów, może składać się z kilku metod prywatnych, które można następnie przetestować indywidualnie.
Zalety:
Niedogodności:
Jeśli jednak ciągłe testowanie wymaga tej metody, może to być sygnał, że należy wyodrębnić metody prywatne, które można przetestować w tradycyjny, publiczny sposób.
Oto skomplikowany przykład tego, jak to by działało:
Klasa wewnętrzna zostałaby skompilowana
ClassToTest$StaticInnerTest
.Zobacz także: Java Tip 106: Statyczne klasy wewnętrzne dla zabawy i zysku
źródło
Jak powiedzieli inni ... nie testuj metod prywatnych bezpośrednio. Oto kilka myśli:
Uruchom obsługę kodu w testach jednostkowych. Jeśli zauważysz, że metody nie są w pełni przetestowane, dodaj je do testów, aby zwiększyć zasięg. Celuj w 100% pokrycie kodu, ale zdaj sobie sprawę, że prawdopodobnie go nie dostaniesz.
źródło
Metody prywatne są konsumowane przez metody publiczne. W przeciwnym razie są martwym kodem. Dlatego testujesz metodę publiczną, potwierdzając oczekiwane wyniki metody publicznej, a tym samym metod prywatnych, które zużywa.
Testowanie metod prywatnych należy przetestować poprzez debugowanie przed uruchomieniem testów jednostkowych na metodach publicznych.
Mogą być również debugowane przy użyciu programowania opartego na testach, debugując testy jednostkowe, dopóki wszystkie asercje nie zostaną spełnione.
Osobiście uważam, że lepiej jest tworzyć klasy przy użyciu TDD; tworzenie kodów pośredniczących metody publicznej, a następnie generowanie testów jednostkowych ze wszystkimi asercjami wcześniej zdefiniowanymi, więc oczekiwany wynik metody jest określany przed jej kodowaniem. W ten sposób nie pójdziesz niewłaściwą ścieżką dopasowania stwierdzeń testu jednostkowego do wyników. Twoja klasa jest wtedy solidna i spełnia wymagania, gdy wszystkie testy jednostkowe przejdą pomyślnie.
źródło
Jeśli używasz Springa, ReflectionTestUtils zapewnia kilka przydatnych narzędzi, które pomagają tutaj przy minimalnym wysiłku. Na przykład, aby skonfigurować próbkę na prywatnym elemencie, bez konieczności dodawania niepożądanego ustawienia publicznego:
źródło
Jeśli próbujesz przetestować istniejący kod, którego nie chcesz lub nie możesz zmienić, odbicie jest dobrym wyborem.
Jeśli projekt klasy jest nadal elastyczny, a masz skomplikowaną metodę prywatną, którą chciałbyś przetestować osobno, sugeruję, abyś wyciągnął ją do osobnej klasy i przetestował osobno. Nie musi to zmieniać publicznego interfejsu oryginalnej klasy; może wewnętrznie utworzyć instancję klasy pomocniczej i wywołać metodę pomocniczą.
Jeśli chcesz przetestować trudne warunki błędu wynikające z metody pomocniczej, możesz pójść o krok dalej. Wyodrębnij interfejs z klasy pomocnika, dodaj publiczny moduł pobierający i ustawiający do klasy oryginalnej, aby wstrzyknąć klasę pomocnika (używaną przez interfejs), a następnie wstrząśnij próbną wersję klasy pomocnika do klasy oryginalnej, aby sprawdzić, jak klasa oryginalna reaguje na wyjątki od pomocnika. Takie podejście jest również pomocne, jeśli chcesz przetestować oryginalną klasę bez testowania również klasy pomocniczej.
źródło
Testowanie metod prywatnych przerywa enkapsulację klasy, ponieważ za każdym razem, gdy zmieniasz wewnętrzną implementację, łamiesz kod klienta (w tym przypadku testy).
Więc nie testuj prywatnych metod.
źródło
Odpowiedź ze strony FAQ JUnit.org :
PS Jestem głównym współpracownikiem Dp4j , zapytaj mnie, czy potrzebujesz pomocy. :)
źródło
Jeśli chcesz przetestować prywatne metody starszej aplikacji, w której nie możesz zmienić kodu, jedną z opcji dla Javy jest jMockit , który pozwoli ci tworzyć symulacje obiektu, nawet gdy są one prywatne dla klasy.
źródło
Deencapsulation.invoke(instance, "privateMethod", param1, param2);
Nie testuję metod prywatnych. Istnieje szaleństwo. Osobiście uważam, że powinieneś testować tylko publicznie udostępnione interfejsy (i obejmuje to metody chronione i wewnętrzne).
źródło
Jeśli używasz JUnit, spójrz na junit-addons . Ma możliwość zignorowania modelu bezpieczeństwa Java i dostępu do prywatnych metod i atrybutów.
źródło
Sugerowałbym trochę przeredagować kod. Kiedy musisz zacząć zastanawiać się nad użyciem refleksji lub innego rodzaju rzeczy, do testowania kodu coś jest nie tak.
Wspomniałeś o różnych rodzajach problemów. Zacznijmy od prywatnych pól. W przypadku pól prywatnych dodałbym nowy konstruktor i do niego wstrzyknąłem pola. Zamiast tego:
Użyłbym tego:
Nie będzie to problemem nawet w przypadku niektórych starszych kodów. Stary kod będzie używał pustego konstruktora, a jeśli zapytasz mnie, zrefaktoryzowany kod będzie wyglądał na czystszy, a będziesz mógł wstrzyknąć niezbędne wartości w teście bez refleksji.
Teraz o metodach prywatnych. Z mojego osobistego doświadczenia, kiedy musisz wprowadzić prywatną metodę do testowania, ta metoda nie ma nic wspólnego z tą klasą. Typowym wzorcem w takim przypadku byłoby zawinięcie go w interfejs, na przykład,
Callable
a następnie przekazanie tego interfejsu również w konstruktorze (z tą sztuczką wielokrotnego konstruktora):Przeważnie wszystko, co napisałem, wygląda na wzór wstrzyknięcia zależności. Z mojego osobistego doświadczenia jest to bardzo przydatne podczas testowania i myślę, że ten rodzaj kodu jest czystszy i łatwiejszy w utrzymaniu. To samo powiedziałbym o klasach zagnieżdżonych. Jeśli zagnieżdżona klasa zawiera ciężką logikę, lepiej byłoby przenieść ją jako prywatną klasę pakietu i wstrzyknąć do klasy, która tego potrzebuje.
Istnieje również kilka innych wzorców projektowych, których użyłem podczas refaktoryzacji i utrzymywania starszego kodu, ale wszystko zależy od przypadków testowania twojego kodu. Korzystanie z refleksji w większości nie stanowi problemu, ale jeśli masz mocno przetestowaną aplikację korporacyjną i testy są uruchamiane przed każdym wdrożeniem, wszystko staje się bardzo wolne (to po prostu denerwujące i nie lubię tego typu rzeczy).
Jest też zastrzyk setera, ale nie zalecałbym go używać. Lepiej trzymam się konstruktora i inicjuję wszystko, gdy jest to naprawdę konieczne, pozostawiając możliwość wstrzyknięcia niezbędnych zależności.
źródło
ClassToTest(Callable)
zastrzykiem. ToClassToTest
komplikuje sprawę. Nie komplikuj. To także wymaga od kogoś innego powiedzeniaClassToTest
czegoś, coClassToTest
powinno być szefem. Jest miejsce na zastrzykiwanie logiki, ale to nie wszystko. Właśnie sprawiłeś, że klasa jest trudniejsza w utrzymaniu, a nie łatwiejsza.X
nie zwiększaX
złożoności do tego stopnia, że może powodować więcej problemów ... a zatem wymaga dodatkowych testów ... które zostały wdrożone w sposób, który może powodować więcej problemów ... (nie jest to nieskończona pętla; każda iteracja jest prawdopodobnie mniejsza niż poprzednia, ale wciąż denerwująca)ClassToTest
utrzymanie klasy ? W rzeczywistości ułatwia to utrzymanie aplikacji. Co sugerujesz, tworząc nową klasę dla każdej innej wartości, której potrzebujesz,first
i zmiennych „drugich”?class A{ A(){} f(){} }
jest prostsze niżclass A { Logic f; A(logic_f) } class B { g() { new A( logic_f) } }
. Gdyby nie było to prawdą i gdyby prawdą było, że dostarczanie logiki klasy jako argumentów konstruktora było łatwiejsze do utrzymania, wówczas przekazalibyśmy całą logikę jako argumenty konstruktora. Po prostu nie rozumiem, jak można twierdzićclass B{ g() { new A( you_dont_know_your_own_logic_but_I_do ) } }
, że kiedykolwiek ułatwia A. Są przypadki, w których takie wstrzykiwanie ma sens, ale nie uważam twojego przykładu za „łatwiejszy w utrzymaniu”Dostęp do prywatnej metody można uzyskać tylko w ramach tej samej klasy. Nie ma więc możliwości przetestowania „prywatnej” metody klasy docelowej z żadnej klasy testowej. Wyjściem jest to, że możesz przeprowadzić testy jednostkowe ręcznie lub zmienić metodę z „prywatnej” na „chronioną”.
Następnie dostęp do chronionej metody można uzyskać tylko w tym samym pakiecie, w którym zdefiniowano klasę. Zatem testowanie chronionej metody klasy docelowej oznacza, że musimy zdefiniować klasę testową w tym samym pakiecie co klasa docelowa.
Jeśli wszystkie powyższe warunki nie odpowiadają twoim wymaganiom, użyj metody odbicia, aby uzyskać dostęp do prywatnej metody.
źródło
Jak wielu sugerowało powyżej, dobrym sposobem jest przetestowanie ich za pomocą publicznych interfejsów.
Jeśli to zrobisz, dobrym pomysłem jest skorzystanie z narzędzia do pokrycia kodu (takiego jak Emma), aby sprawdzić, czy twoje prywatne metody są w rzeczywistości wykonywane na podstawie testów.
źródło
Oto moja ogólna funkcja do testowania pól prywatnych:
źródło
Przykład poniżej;
Należy dodać następującą instrukcję importu:
Teraz możesz bezpośrednio przekazać obiekt, który ma metodę prywatną, nazwę metody do wywołania i dodatkowe parametry, jak poniżej.
źródło
Dzisiaj wypchnąłem bibliotekę Java, aby pomóc w testowaniu prywatnych metod i pól. Został zaprojektowany z myślą o systemie Android, ale naprawdę można go używać w każdym projekcie Java.
Jeśli masz kod z prywatnymi metodami lub polami lub konstruktorami, możesz użyć BoundBox . Robi dokładnie to, czego szukasz. Poniżej znajduje się przykład testu, który uzyskuje dostęp do dwóch prywatnych pól działania Androida, aby go przetestować:
BoundBox ułatwia testowanie prywatnych / chronionych pól, metod i konstruktorów. Możesz nawet uzyskać dostęp do rzeczy ukrytych przez dziedziczenie. Rzeczywiście, BoundBox łamie enkapsulację. Zapewni ci to dostęp poprzez refleksję, ALE wszystko jest sprawdzane w czasie kompilacji.
Jest idealny do testowania starszego kodu. Używaj go ostrożnie. ;)
https://github.com/stephanenicolas/boundbox
źródło
Najpierw odpowiem na pytanie: Dlaczego twoi prywatni członkowie potrzebują izolowanych testów? Czy są one tak złożone, zapewniając tak skomplikowane zachowania, że wymagają testów poza powierzchnią publiczną? To testy jednostkowe, a nie „liniowe”. Nie przejmuj się małymi rzeczami.
Jeśli są tak duże, wystarczająco duże, że każdy z tych prywatnych członków jest „jednostką” o dużej złożoności - rozważ refaktoryzację takich prywatnych członków z tej klasy.
Jeśli refaktoryzacja jest nieodpowiednia lub niewykonalna, czy możesz użyć wzorca strategii, aby zastąpić dostęp do tych prywatnych funkcji / klas członków podczas testowania jednostkowego? W teście jednostkowym strategia zapewniłaby dodatkowe sprawdzanie poprawności, ale w kompilacjach wersji byłaby to prosta transmisja.
źródło
Niedawno miałem ten problem i napisałem małe narzędzie o nazwie Picklock , które pozwala uniknąć problemów związanych z jawnym użyciem interfejsu API refleksji Java, dwa przykłady:
Metody wywoływania, np.
private void method(String s)
- przez odbicie JavaMetody
private void method(String s)
wywoływania , np. - przez PicklockUstawianie pól, np.
private BigInteger amount;
- przez odbicie JavaUstawianie pól, np.
private BigInteger amount;
- przez Picklockaźródło
PowerMockito jest do tego stworzony. Użyj zależności maven
To możesz zrobić
źródło