Pytanie Jak przetestować prywatną funkcję lub klasę, która ma prywatne metody, pola lub klasy wewnętrzne?


Jak mogę testować jednostkę (używając xUnit), która ma wewnętrzne prywatne metody, pola lub klasy zagnieżdżone? Lub funkcję, która staje się prywatna poprzez posiadanie wewnętrzne powiązanie (static w C / C ++) lub jest prywatny (anonimowy) przestrzeń nazw?

Wydaje się złe, aby zmienić modyfikator dostępu dla metody lub funkcji tylko po to, aby móc uruchomić test.


2268


pochodzenie


Najlepszym sposobem na przetestowanie metody prywatnej nie jest testowanie jej bezpośrednio - Surya
Sprawdź artykuł Testowanie prywatnych metod za pomocą JUnit i SuiteRunner. - Mouna Cheikhna
Nie zgadzam się. Metodę (publiczną), która jest długa lub trudna do zrozumienia, należy refaktoryzować. Byłoby szaleństwem nie testować małych (prywatnych) metod, które dostajesz, zamiast tylko publicznych. - Michael Piefel
Nie testowanie żadnych metod tylko dlatego, że widoczność jest głupia. Nawet test jednostkowy powinien być najmniejszym fragmentem kodu, a jeśli przetestujesz tylko publiczne metody, nigdy nie będziesz już na pewno, gdzie wystąpi błąd - ta metoda lub inna. - Dainius
Musisz przetestować klasę funkcjonalnośćnie jego realizacja. Chcesz przetestować prywatne metody? Przetestuj publiczne metody, które je wywołują. Jeśli funkcjonalność, którą oferuje klasa, jest dokładnie testowana, jej elementy wewnętrzne okazały się poprawne i niezawodne; nie musisz sprawdzać warunków wewnętrznych. Testy powinny zachować oddzielenie od testowanych klas. - Calimar41


Odpowiedzi:


Jeśli masz coś ze spuścizny Jawa aplikacji, a Ty nie możesz zmienić widoczności swoich metod, najlepszym sposobem testowania prywatnych metod jest użycie odbicie.

Wewnętrznie używamy pomocy, aby uzyskać / ustawić private i private static zmienne, a także wywołaj private i private static metody. Poniższe wzorce pozwolą ci zrobić prawie wszystko, co wiąże się z prywatnymi metodami i polami. Oczywiście nie możesz się zmienić private static final zmienne poprzez odbicie.

Method method = targetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

I dla pól:

Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);

Uwagi:
  1. targetClass.getDeclaredMethod(methodName, argClasses) pozwala ci zajrzeć private metody. To samo dotyczy    getDeclaredField.
  2. The setAccessible(true) jest wymagany do zabawy z szeregowymi.


1417



Przydatne, jeśli nie znasz API, ale jeśli masz do testowania prywatnych metod w ten sposób, jest coś z twoim projektem. Kolejny plakat mówi, że testowanie jednostkowe powinno sprawdzać kontrakt klasy: jeśli umowa jest zbyt szeroka i tworzy zbyt dużo systemu, to należy się zająć projektem. - andygavin
Bardzo przydatne. Korzystając z tego, należy pamiętać, że niepowodzenia byłyby złe, gdyby testy przeprowadzono po zaciemnieniu. - Rick Minerich
Przykładowy kod nie działał dla mnie, ale dzięki temu thigs był wyraźniejszy: java2s.com/Tutorial/Java/0125__Reflection/... - Rob
Znacznie lepsze niż bezpośrednie wykorzystanie refleksji byłoby użycie jakiejś biblioteki, na przykład Powermock. - Michael Piefel
Właśnie dlatego pomocna jest funkcja Test Driven Design. Pomaga ustalić, co należy ujawnić, aby potwierdzić zachowanie. - Thorbjørn Ravn Andersen


Najlepszym sposobem na przetestowanie metody prywatnej jest inna publiczna metoda. Jeśli nie można tego zrobić, wówczas spełniony jest jeden z następujących warunków:

  1. Prywatna metoda to martwy kod
  2. W pobliżu klasy testowany jest zapach
  3. Metoda, którą próbujesz przetestować, nie powinna być prywatna

507



Nie zgadzać się. Całkowicie poprawne jest posiadanie algorytmu w prywatnej metodzie, która wymaga więcej testów jednostkowych, niż jest to możliwe za pośrednictwem publicznych interfejsów klasy. - Mr. Shiny and New 安宇
Hey Mr Shiny Tak, ale wtedy będziesz miał kruche testy. Zobacz także numer 2 w mojej odpowiedzi. - Trumpi
Kruchy test jest testem, który zbyt łatwo zawodzi, gdy dochodzi do zmiany kodu. Zwykle dzieje się tak, gdy wynik testu jest oparty na tym, co robi metoda, a nie na oczekiwanych wynikach i efektach ubocznych przy danym zestawie danych wejściowych. W idealnej sytuacji zmiana kodu, który nie zmienia wyników, nie powinna przerwać testu. - aro_biz
@Pan. Błyszczące i nowe: jeśli twoja prywatna metoda jest wystarczająco złożona, aby uzasadnić niezależne testowanie jednostkowe, to jest wystarczająco złożona, aby mieć własną klasę. Klasa może być oczywiście wewnętrzna (tj. Niedostępna z innych pakietów). - sleske
@sleske, nie zgadzam się. Możesz mieć małą prywatną metodę, która z założenia powinna być prywatna. i wolę przetestować każdy mały kawałek kodu, niż przetestować tę prywatną metodę przez publiczną. Ten sposób testowania pójdzie w kierunku testu komponentów. - despot


Kiedy mam prywatne metody w klasie, które są wystarczająco skomplikowane, że czuję potrzebę bezpośredniego przetestowania prywatnych metod, to jest to zapach kodu: moja klasa jest zbyt skomplikowana.

Moim zwykłym podejściem do rozwiązywania takich problemów jest nauczenie się nowej klasy zawierającej interesujące fragmenty. Często ta metoda i pola z nią współdziałają, a być może inna metoda lub dwie mogą być wyodrębnione do nowej klasy.

Nowa klasa udostępnia te metody jako "publiczne", dzięki czemu są one dostępne do testowania jednostkowego. Nowe i stare klasy są teraz prostsze niż oryginalna klasa, co jest dla mnie świetne (muszę zachować prostotę lub zgubić się!).

Zauważ, że nie sugeruję, że ludzie tworzą klasy bez użycia mózgu! Chodzi o to, aby użyć siły testów jednostkowych, aby pomóc ci znaleźć dobre nowe klasy.


272



Pytanie brzmiało, jak przetestować prywatne metody. Mówisz, że zrobiłbyś dla tego nową klasę (i dodasz więcej złożoności) i po sugestii, by nie tworzyć nowej klasy. Jak testować prywatne metody? - Dainius
Myślę, że jeśli masz metodę, nie ma potrzeby tworzenia innej klasy tylko po to, aby móc przetestować tę metodę. Nie sądzę, aby tutaj projekt klas był kwestionowany. Zakładam więc, że autor robił wszystko, aby mieć odpowiedni projekt wcześniej, więc wprowadzając inną klasę dla jednej metody, po prostu zwiększasz złożoność. Oczywiście, gdy masz wystarczająco złożony program, nie będzie żadnych oczywistych błędów. - Dainius
@Dainius: Nie sugeruję tworzenia nowej klasy wyłącznie więc możesz przetestować tę metodę. Sugeruję, że pisanie testów może pomóc ci ulepszyć twój projekt: dobre projekty są łatwe do przetestowania. - Jay Bazuzi
Ale zgadzasz się, że dobry OOD naraziłby (publicznie) tylko te metody, które są niezbędne, aby ta klasa / obiekt działał poprawnie? wszystkie inne powinny być prywatne / protectec. Tak więc w niektórych prywatnych metodach pojawi się pewna logika, a IMO testowanie tych metod tylko poprawi jakość oprogramowania. Oczywiście zgadzam się, że jeśli jakiś fragment kodu jest złożony, powinien być podzielony na osobne metody / klasy. - Dainius
@Danius: Dodawanie nowej klasy, w większości przypadków zmniejszyć złożoność. Po prostu zmierz go. Testowalność w niektórych przypadkach walczy z OOD. Nowoczesne podejście jest faworyzowane pod względem testowalności. - AlexWien


używałem odbicie zrobić to w przeszłości dla Javy i moim zdaniem był to wielki błąd.

Ściśle mówiąc, powinieneś nie pisać testy jednostkowe, które bezpośrednio testują prywatne metody. Co ty powinien testowanie to kontrakt publiczny, który klasa ma z innymi obiektami; nie powinieneś nigdy bezpośrednio testować wewnętrznych elementów obiektu. Jeśli inny programista chce wprowadzić niewielką wewnętrzną zmianę w klasie, co nie ma wpływu na umowę o zamówieniach publicznych, musi zmodyfikować test oparty na refleksji, aby upewnić się, że działa. Jeśli robisz to wielokrotnie w ramach projektu, testy jednostkowe przestają być użytecznym pomiarem zdrowia kodu i stają się przeszkodą w rozwoju i irytują zespół programistów.

Zamiast tego zalecam używanie narzędzia do pokrycia kodu, takiego jak Cobertura, aby upewnić się, że testowane jednostki testują odpowiedni poziom kodu w prywatnych metodach. W ten sposób pośrednio sprawdzasz, co robią prywatne metody i utrzymujesz wyższy poziom zręczności.


229



+1 do tego. Moim zdaniem jest to najlepsza odpowiedź na to pytanie. Testując prywatne metody testujesz implementację. To pokonuje cel testów jednostkowych, polegający na testowaniu wejść / wyjść kontraktu klasowego. Test powinien tylko wystarczająco dużo wiedzy na temat implementacji, aby kpić z metod, które wywołuje na swoich zależnościach. Nic więcej. Jeśli nie możesz zmienić swojej implementacji bez konieczności zmiany testu - jest szansa, że ​​twoja strategia testowa jest słaba. - Colin M
@Colin M To naprawdę nie jest to o co pyta;) Pozwól mu się zdecydować, nie znasz projektu. - Aaron Marcus
Nie do końca prawda. Potrzeba wiele wysiłku, aby przetestować niewielką część metody prywatnej za pomocą metody publicznej, która ją wykorzystuje. Metoda publiczna może wymagać znacznej konfiguracji zanim dotrzesz do linii, która wywołuje twoją metodę prywatną - ACV


Z tego artykułu: Testowanie prywatnych metod za pomocą JUnit i SuiteRunner (Bill Venners), w zasadzie masz 4 opcje:

  • Nie testuj prywatnych metod.
  • Daj dostęp do pakietu metod.
  • Użyj zagnieżdżonej klasy testowej.
  • Użyj refleksji.

187



@ user86614 Umieszczenie kodu testowego w kodzie produkcyjnym nie jest dobrym pomysłem. - Jimmy T.
@ Jimmy. Zależy od tego, dla kogo przeznaczony jest "kod produkcyjny". Wywołałbym kod produkowany dla aplikacji stacjonujących w sieci VPN, których docelowi użytkownicy są sysadminami, aby byli kodami produkcyjnymi. - Pacerier
@Pacerier Co masz na myśli? - Jimmy T.
Piąta opcja, jak wspomniano powyżej, testuje publiczną metodę, która wywołuje metodę prywatną? Poprawny? - user2441441
@LorenzoLerate Wcześniej zmagałem się z tym problemem, ponieważ zajmowałem się programowaniem embedded i chciałbym, aby moje oprogramowanie było jak najmniejsze. Ograniczenia rozmiarów i wydajność to jedyny powód, jaki mogę wymyślić.


Zasadniczo test jednostkowy ma na celu wykorzystanie publicznego interfejsu klasy lub jednostki. W związku z tym metody prywatne są szczegółami implementacji, których nie należy oczekiwać w sposób jawny.


96



To najlepsza odpowiedź IMO, lub jak zwykle się mówi, zachowanie testowe, a nie metody. Testy jednostkowe nie zastępują metryk kodu źródłowego, narzędzi do analizy kodów statycznych i recenzji kodu. Jeśli metody prywatne są tak złożone, że wymagają odseparowania testów, prawdopodobnie wymagają refaktoryzacji, a nie więcej testów. - Dan Haynes
więc nie pisz prywatnych metod, po prostu stwórz 10 innych małych klas? - razor


Tylko dwa przykłady, gdzie chciałbym przetestować prywatną metodę:

  1. Procedury odszyfrowywania - Wolałbym nie chcesz, aby były one widoczne dla każdego, kto tylko zobaczy ze względu na testowanie, w przeciwnym razie każdy może użyj ich do odszyfrowania. Ale oni są nieodłączny od kodu, skomplikowany, i muszą zawsze działać (oczywistym wyjątkiem jest refleksja, która może być używana do przeglądania nawet prywatnych metod w większości przypadków, kiedy SecurityManager nie jest skonfigurowany, aby temu zapobiec).
  2. Tworzenie zestawu SDK dla społeczności konsumpcja. Tutaj społeczeństwo przyjmuje zupełnie inne znaczenie, od tego jest kodem, który może ujrzeć cały świat (nie tylko wewnętrzne dla mojej aplikacji). włożyłem kodować do prywatnych metod, jeśli nie chcą, aby użytkownicy SDK to zobaczyli - ja nie widzę tego po prostu jako zapachu kodu jak działa programowanie SDK. Ale z Oczywiście nadal muszę przetestować mój prywatne metody, i gdzie są funkcjonalność mojego SDK w rzeczywistości zyje.

Rozumiem ideę testowania tylko "umowy". Ale nie widzę, żeby można było zalecać, by nie testować kodu - twój przebieg może się różnić.

Mój kompromis polega więc na komplikowaniu JUnitsa na odbiciu, a nie na naruszaniu moich zabezpieczeń i SDK.


56



Chociaż nigdy nie należy upubliczniać metody, aby ją przetestować, private nie jest jedyną alternatywą. Brak modyfikatora dostępu package private i oznacza, że ​​możesz go przetestować tak długo, jak długo test twojej jednostki znajduje się w tym samym pakiecie. - ngreen
@ngreen to wszystko prawda, ale nie o to chodzi. OP prosi o testowanie prywatnych metod. Domyślnie nie jest tożsamy ​​z prywatnym; jak już wspomniano, możesz łatwo zobaczyć domyślną metodę dostępu z klasy, po prostu deklarując tę ​​klasę w tym samym pakiecie. Z prywatnym dostępem potrzebujesz refleksji, która jest zupełnie inną grą w piłkę. - Richard Le Mesurier
Komentowałem twoją odpowiedź, w szczególności punkt 1, a nie OP. Nie ma potrzeby, aby metoda była prywatna tylko dlatego, że nie chcesz, aby była publiczna. - ngreen
@ngreen true thx - Byłem leniwy ze słowem "public". Zaktualizowałem odpowiedź, dodając publiczny, chroniony, domyślny (i wspominając o refleksji). Chodzi mi o to, że istnieje jakiś powód, aby jakiś kod był tajny, ale nie powinno to uniemożliwiać nam jego przetestowania. - Richard Le Mesurier
Dzięki za dawanie prawdziwych przykładów wszystkim. Większość odpowiedzi jest dobra, ale z teoretycznego punktu widzenia. W prawdziwym świecie musimy ukryć implementację przed kontraktem i nadal musimy to testować. Czytając te wszystkie, myślę, że być może powinien to być zupełnie nowy poziom testów o innej nazwie - Z. Khullah


Prywatne metody są wywoływane metodą publiczną, więc dane wejściowe do twoich publicznych metod powinny również testować prywatne metody, które są wywoływane przez te publiczne metody. Gdy metoda publiczna zawodzi, może to oznaczać niepowodzenie w metodzie prywatnej.


53



Prawdziwy fakt. Problem polega na tym, że implementacja jest bardziej skomplikowana, np. Jeśli jedna metoda publiczna wywoła kilka prywatnych metod. Który z nich się nie udał? Lub jeśli jest to skomplikowana metoda prywatna, której nie można ujawnić ani przenieść na nową klasę - Z. Khullah
Wspomniałeś już, dlaczego należy testować prywatną metodę. Powiedziałeś "to może być", więc nie jesteś pewien. IMO, czy metoda powinna zostać przetestowana, jest ortogonalna do jej poziomu dostępu. - Wei Qiu


Innym podejściem, które stosowałem, jest zmiana metody prywatnej na pakiet prywatną lub chronioną, a następnie uzupełnienie jej @VisibleForTesting adnotacja biblioteki Google Guava.

Dzięki temu ktokolwiek użyje tej metody, aby zachować ostrożność i nie mieć do niej bezpośredniego dostępu, nawet w pakiecie. Również klasa testowa nie musi być w tym samym pakiecie fizycznie, ale w tym samym pakiecie pod test teczka.

Na przykład, jeśli jest metoda do testowania src/main/java/mypackage/MyClass.java następnie twoje wezwanie testowe powinno zostać złożone src/test/java/mypackage/MyClassTest.java. W ten sposób uzyskasz dostęp do metody testowej w swojej klasie testowej.


30



Nie wiedziałem o tym, jest to ciekawe, wciąż myślę, że jeśli potrzebujesz tego rodzaju annotty, masz problemy z projektowaniem. - Seb
Dla mnie to jak mówienie zamiast dawania jednorazowego klucza do strażnika ognia, aby przetestować wiertarkę pożarową, zostawiasz otwarte drzwi wejściowe z napisem na drzwiach - "odblokowany do testowania - jeśli nie jesteś z naszej firmy, proszę nie wpisywać ". - Fr Jeremy Krieg


Aby przetestować starszy kod z dużymi i dziwacznymi klasami, często bardzo pomocne jest przetestowanie jednej prywatnej (lub publicznej) metody, którą piszę teraz.

używam junitx.util.PrivateAccessor-package dla Java. Mnóstwo pomocnych jednolinijkowych do uzyskiwania dostępu do prywatnych metod i prywatnych pól.

import junitx.util.PrivateAccessor;

PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);

28



Pamiętaj, aby pobrać całe dodatki JUnit (sourceforge.net/projects/junit-addons), a nie SourceAce-Recommended Project PrivateAccessor. - skia.heliou
I upewnij się, że kiedy używasz Class jako pierwszy parametr w tych metodach, do którego uzyskujesz dostęp static członków. - skia.heliou