Uwaga moderatora: opublikowano już 39 odpowiedzi (niektóre zostały usunięte). Przed wysłaniem swoją odpowiedź, należy rozważyć, czy można dodać coś sensownego do dyskusji. Prawdopodobnie powtarzasz to, co ktoś już powiedział.
Czasami muszę stworzyć metodę prywatną w klasie publicznej, aby napisać dla niej kilka testów jednostkowych.
Zwykle dzieje się tak, ponieważ metoda zawiera logikę współużytkowaną przez inne metody w klasie, a testowanie logiki we własnym zakresie jest łatwiejsze. Innym powodem może być testowanie logiki używanej w wątkach synchronicznych bez martwienia się o problemy z wątkami. .
Czy inni to robią, bo tak naprawdę nie lubię tego robić? Osobiście uważam, że bonusy przeważają nad problemem upublicznienia metody, która tak naprawdę nie zapewnia żadnej usługi poza klasą ...
AKTUALIZACJA
Dzięki za odpowiedzi wszystkim, wydaje się, że wzbudził zainteresowanie ludzi. Myślę, że ogólna zgoda co do testowania powinna odbywać się za pośrednictwem publicznego interfejsu API, ponieważ jest to jedyny sposób, w jaki klasa będzie kiedykolwiek używana, i zgadzam się z tym. Kilka przypadków, o których wspomniałem powyżej, w których chciałbym to zrobić powyżej, były rzadkimi przypadkami i uważałem, że korzyści płynące z robienia tego były tego warte.
Widzę jednak, że wszyscy twierdzą, że tak naprawdę nigdy nie powinno tak się stać. A kiedy myślę o tym trochę więcej, myślę, że zmiana kodu w celu dostosowania go do testów jest złym pomysłem - w końcu przypuszczam, że testowanie jest w pewien sposób narzędziem wsparcia, a zmiana systemu na „wsparcie narzędzia wsparcia”, jeśli chcesz, jest rażąca zła praktyka.
źródło
@VisibileForTesting
adnotacje do tworzenia takich metod - zalecam to zrobić, aby przyczyna niewłaściwej metody byłaprivate
odpowiednio udokumentowana.Odpowiedzi:
Ogólnie rzecz biorąc, zazwyczaj jestem za refaktoryzacją kodu „produkcyjnego”, aby ułatwić testowanie. Nie sądzę jednak, żeby był to dobry telefon. Dobry test jednostkowy (zwykle) nie powinien dbać o szczegóły implementacji klasy, a jedynie o jej widoczne zachowanie. Zamiast wystawiać na próbę wewnętrzne stosy, możesz sprawdzić, czy klasa zwraca strony w oczekiwanej kolejności po wywołaniu
first()
lublast()
.Rozważmy na przykład ten pseudo-kod:
źródło
Osobiście wolałbym badanej jednostki używając API publicznych i nigdy bym na pewno sprawiają, że metoda prywatny publiczny prostu zrobić to łatwe do testu.
Jeśli naprawdę chcesz przetestować metodę prywatną w izolacji, w Javie możesz użyć Easymock / Powermock, aby to zrobić.
Musisz być pragmatyczny i powinieneś również zdawać sobie sprawę z powodów, dla których trudno jest przetestować.
„ Posłuchaj testów ” - jeśli trudno jest przetestować, czy to mówi coś o twoim projekcie? Czy mógłbyś zastanowić się, gdzie test dla tej metody byłby trywialny i łatwo objęty testowaniem za pośrednictwem publicznego interfejsu API?
Oto, co ma do powiedzenia Michael Feathers w „ Skutecznej pracy ze starszym kodem ”
źródło
Jak powiedzieli inni, w pewnym stopniu podejrzewa się, że w ogóle testuje się metody prywatne; przetestuj interfejs publiczny, a nie szczegóły dotyczące prywatnej implementacji.
To powiedziawszy, techniką, której używam, gdy chcę przetestować jednostkowo coś, co jest prywatne w języku C #, jest obniżenie poziomu ochrony dostępności z prywatnego na wewnętrzny, a następnie oznaczenie zestawu testującego jednostkę jako zestawu przyjaciela przy użyciu InternalsVisibleTo . Zespół testujący jednostki będzie wtedy mógł traktować elementy wewnętrzne jako publiczne, ale nie musisz się martwić o przypadkowe dodanie do swojej publicznej powierzchni.
źródło
Wiele odpowiedzi sugeruje jedynie przetestowanie interfejsu publicznego, ale IMHO jest to nierealne - jeśli metoda robi coś, co wymaga 5 kroków, będziesz chciał przetestować te pięć kroków osobno, a nie wszystkie razem. Wymaga to przetestowania wszystkich pięciu metod, które w innym przypadku (inne niż do testowania) mogłyby być
private
.Zwykłym sposobem testowania metod „prywatnych” jest nadanie każdej klasie własnego interfejsu i utworzenie metod „prywatnych”
public
, ale nie włączenie ich do interfejsu. W ten sposób można je jeszcze przetestować, ale nie nadmuchują interfejsu.Tak, spowoduje to wzdęcie pliku i klasy.
Tak, powoduje to, że specyfikatory
public
i sąprivate
zbędne.Tak, to boli w tyłek.
Jest to niestety jedna z wielu ofiar, które poświęcamy, aby kod był testowalny . Być może przyszły język (lub nawet przyszła wersja C # / Java) będzie zawierał funkcje ułatwiające testowanie klas i modułów; ale tymczasem musimy skakać przez te obręcze.
Są tacy, którzy twierdzą, że każdy z tych kroków powinien być własną klasą , ale nie zgadzam się - jeśli wszystkie mają ten sam stan, nie ma powodu, aby tworzyć pięć oddzielnych klas, w których byłoby pięć metod. Co gorsza, powoduje to wzdęcia plików i klas. Ponadto infekuje publiczne API twojego modułu - wszystkie te klasy muszą być koniecznie,
public
jeśli chcesz je przetestować z innego modułu (albo to, albo dołączyć kod testowy do tego samego modułu, co oznacza wysyłkę kodu testowego z produktem) .źródło
Test jednostkowy powinien przetestować zamówienie publiczne, jedyny sposób, w jaki klasę można wykorzystać w innych częściach kodu. Metodą prywatną są szczegóły implementacji, nie należy jej testować, o ile publiczny interfejs API działa poprawnie, implementacja nie ma znaczenia i można ją zmienić bez zmian w przypadkach testowych.
źródło
IMO, powinieneś napisać testy, nie poczyniąc głębokich założeń dotyczących tego, jak twoja klasa zaimplementowała się w środku. Prawdopodobnie zechcesz go później zrefaktoryzować przy użyciu innego modelu wewnętrznego, ale nadal zapewniając te same gwarancje, które daje poprzednia implementacja.
Mając to na uwadze, sugeruję, abyś skupił się na testowaniu, czy twoja umowa nadal obowiązuje, bez względu na wewnętrzne wdrożenie, które obecnie ma Twoja klasa. Testowanie publicznych interfejsów API oparte na właściwościach.
źródło
Co powiesz na to, aby pakiet był prywatny? Następnie kod testowy może go zobaczyć (i inne klasy w pakiecie), ale nadal jest ukryty przed użytkownikami.
Ale tak naprawdę nie powinieneś testować prywatnych metod. Są to szczegóły dotyczące wdrożenia, a nie część umowy. Wszystko, co robią, powinno być objęte wywoływaniem metod publicznych (jeśli mają tam kod, który nie jest wykonywany przez metody publiczne, to powinno pójść). Jeśli kod prywatny jest zbyt skomplikowany, klasa prawdopodobnie robi zbyt wiele rzeczy i chce refaktoryzować.
Upublicznienie metody jest dużym zaangażowaniem. Gdy to zrobisz, ludzie będą mogli z niego korzystać i nie będziesz mógł ich więcej zmieniać.
źródło
Aktualizacja : W wielu innych miejscach dodałem bardziej rozbudowaną i pełniejszą odpowiedź na to pytanie. Można to znaleźć na moim blogu .
Jeśli kiedykolwiek będę musiał podać coś do wiadomości publicznej, aby to przetestować, zwykle oznacza to, że testowany system nie przestrzega zasady pojedynczej odpowiedzialności . Dlatego brakuje klasy, którą należy wprowadzić. Po wyodrębnieniu kodu do nowej klasy, upublicznij go. Teraz możesz łatwo testować i śledzisz SRP. Twoja druga klasa musi po prostu przywołać tę nową klasę poprzez kompozycję.
Upublicznianie metod / stosowanie sztuczek językowych, takich jak oznaczanie kodu jako widoczne dla zespołów testowych, zawsze powinno być ostatecznością.
Na przykład:
Przeanalizuj to, wprowadzając obiekt sprawdzania poprawności.
Teraz wszystko, co musimy zrobić, to przetestować poprawność wywołania walidatora. Rzeczywisty proces sprawdzania poprawności (wcześniej prywatna logika) można przetestować w czystej izolacji. Nie będzie potrzeby konfigurowania złożonego testu, aby upewnić się, że ta weryfikacja zakończy się pomyślnie.
źródło
Kilka świetnych odpowiedzi. Jedną rzeczą, o której nie wspomniałem, jest to, że w przypadku rozwoju opartego na testach (TDD), prywatne metody są tworzone podczas fazy refaktoryzacji (spójrz na metodę wyodrębniania na przykład wzorca refaktoryzacji), a zatem powinny już mieć niezbędne pokrycie testowe . Jeśli zrobisz to poprawnie (i oczywiście dostaniesz mieszany zestaw opinii, jeśli chodzi o poprawność), nie powinieneś się martwić o podanie publicznej metody prywatnej tylko po to, aby ją przetestować.
źródło
Dlaczego nie podzielić algorytmu zarządzania stosami na klasę użyteczności? Klasa użyteczności może zarządzać stosami i zapewniać publiczne akcesoria. Testy jednostkowe można skoncentrować na szczegółach implementacji. Dogłębne testy dla algorytmicznie trudnych klas są bardzo pomocne w marszczeniu skrajnych przypadków i zapewnianiu zasięgu.
Wówczas twoja bieżąca klasa może czysto delegować się do klasy użyteczności bez ujawniania żadnych szczegółów implementacji. Jego testy będą dotyczyły wymogu podziału na strony, jak zalecili inni.
źródło
W Javie istnieje również opcja nadania pakietowi prywatności (tzn. Pominięcie modyfikatora widoczności). Jeśli testy jednostkowe znajdują się w tym samym pakiecie co testowana klasa, powinny one być w stanie zobaczyć te metody i jest nieco bezpieczniejsze niż upublicznienie metody.
źródło
Metody prywatne są zwykle używane jako metody „pomocnicze”. Dlatego zwracają tylko podstawowe wartości i nigdy nie działają na określonych instancjach obiektów.
Masz kilka opcji, jeśli chcesz je przetestować.
Alternatywnie możesz utworzyć nową klasę za pomocą metody pomocniczej jako metody publicznej, jeśli jest ona wystarczająco dobrym kandydatem do nowej klasy.
Jest tutaj bardzo dobry artykuł na ten temat.
źródło
Jeśli używasz C #, możesz ustawić metodę wewnętrzną. W ten sposób nie zanieczyszczasz publicznego interfejsu API.
Następnie dodaj atrybut do biblioteki dll
[assembly: InternalsVisibleTo („MyTestAssembly”)]
Teraz wszystkie metody są widoczne w projekcie MyTestAssembly. Może nie jest idealny, ale lepszy niż upublicznienie prywatnej metody tylko po to, by ją przetestować.
źródło
W razie potrzeby użyj refleksji, aby uzyskać dostęp do zmiennych prywatnych.
Ale tak naprawdę nie zależy ci na wewnętrznym stanie klasy, po prostu chcesz przetestować, czy publiczne metody zwracają to, czego oczekujesz w sytuacjach, które możesz przewidzieć.
źródło
Powiedziałbym, że to zły pomysł, ponieważ nie jestem pewien, czy dostaniesz jakąś korzyść i potencjalnie problemy w dalszej kolejności. Jeśli zmieniasz umowę wywołań, tylko w celu przetestowania metody prywatnej, nie testujesz klasy pod kątem jej użycia, ale tworzysz sztuczny scenariusz, którego nigdy nie zamierzałeś się wydarzyć.
Co więcej, deklarując metodę jako publiczną, co powiedzieć, że za sześć miesięcy (po zapomnieniu, że jedynym powodem upublicznienia metody jest testowanie), że ty (lub jeśli przekazałeś projekt) ktoś zupełnie inny wygrał nie używaj go, co może prowadzić do niezamierzonych konsekwencji i / lub koszmaru związanego z konserwacją.
źródło
jeśli chodzi o testy jednostkowe, zdecydowanie nie powinieneś dodawać więcej metod; Uważam, że lepiej byś zrobił przypadek testowy dotyczący twojej
first()
metody, który byłby wywoływany przed każdym testem; Następnie można wywołać wiele razy -next()
,previous()
ilast()
zobaczyć, czy rezultaty dopasować swoje oczekiwania. Myślę, że jeśli nie dodasz więcej metod do swojej klasy (tylko do celów testowych), trzymasz się zasady testowania „czarnej skrzynki”;źródło
Najpierw sprawdź, czy metodę należy wyodrębnić do innej klasy i podać do wiadomości publicznej. Jeśli tak nie jest, należy zabezpieczyć pakiet i napisać w Java adnotację @VisibleForTesting .
źródło
W swojej aktualizacji mówisz, że dobrze jest po prostu przetestować przy użyciu publicznego interfejsu API. W rzeczywistości są tutaj dwie szkoły.
Testowanie czarnej skrzynki
Szkoła czarnej skrzynki mówi, że klasę należy uważać za czarną skrzynkę, której nikt nie widzi w niej implementacji. Jedynym sposobem na przetestowanie tego jest publiczny interfejs API - tak jak użytkownik klasy będzie go używał.
testy białych skrzynek.
Szkoła białych skrzynek uważa za naturalne wykorzystanie wiedzy na temat implementacji klasy, a następnie przetestowanie klasy pod kątem jej prawidłowości.
Naprawdę nie mogę się przyłączyć do dyskusji. Pomyślałem, że byłoby interesujące wiedzieć, że istnieją dwa różne sposoby testowania klasy (lub biblioteki lub cokolwiek innego).
źródło
Są sytuacje, w których powinieneś to zrobić (np. Kiedy implementujesz skomplikowane algorytmy). Po prostu zrób to z pakietem prywatnym, a to wystarczy. Ale w większości przypadków prawdopodobnie masz zbyt złożone klasy, które wymagają podzielenia logiki na inne klasy.
źródło
Prywatne metody, które chcesz przetestować w izolacji, wskazują, że w twojej klasie jest ukryta inna „koncepcja”. Wyodrębnij tę „koncepcję” do jej własnej klasy i przetestuj jako oddzielną „jednostkę”.
Obejrzyj ten film, aby uzyskać naprawdę interesujące podejście do tego tematu.
źródło
Nigdy nie należy nigdy pozwalać testom na dyktowanie kodu. Nie mówię o TDD ani innych DD, mam na myśli dokładnie to, o co prosisz. Czy Twoja aplikacja potrzebuje tych metod, aby była publiczna. Jeśli tak, przetestuj je. Jeśli tak nie jest, to nie upubliczniaj ich tylko do testów. To samo dotyczy zmiennych i innych. Pozwól, aby potrzeby aplikacji podyktowały kod, a testy przetestuj, czy potrzeba jest spełniona. (Znowu nie mam na myśli testowania jako pierwszego, czy też nie, mam na myśli zmianę struktury klas, aby osiągnąć cel testowania).
Zamiast tego powinieneś „przetestować wyżej”. Przetestuj metodę wywołującą metodę prywatną. Ale twoje testy powinny testować potrzeby aplikacji, a nie „decyzje wdrożeniowe”.
Na przykład (pseudo kod bod tutaj);
Nie ma powodu, aby testować „dodaj”, można zamiast tego przetestować „książki”.
Nigdy nie pozwól, aby Twoje testy podejmowały decyzje dotyczące projektowania kodu za Ciebie. Sprawdź, czy uzyskasz oczekiwany wynik, a nie jak uzyskasz ten wynik.
źródło
Zgadzam się, że premie za testowanie tej jednostki przeważają nad problemami związanymi ze zwiększeniem widoczności niektórych członków. Nieznaczne ulepszenie polega na tym, aby był chroniony i wirtualny, a następnie zastąpić go w klasie testowej, aby go ujawnić.
Alternatywnie, jeśli jego funkcjonalność chcesz przetestować osobno, czy nie sugeruje to brakującego obiektu w twoim projekcie? Może mógłbyś umieścić go w osobnej klasie testowalnej ... wtedy twoja istniejąca klasa deleguje się tylko do instancji tej nowej klasy.
źródło
Ogólnie utrzymuję klasy testowe w tym samym projekcie / zestawie co klasy testowane.
W ten sposób potrzebuję tylko
internal
widoczności, aby funkcje / klasy mogły być testowane.To nieco komplikuje proces budowania, który musi odfiltrować klasy testowe. Osiągam to, nazywając wszystkie moje klasy testowe
TestedClassTest
i używając wyrażenia regularnego do filtrowania tych klas.Dotyczy to oczywiście tylko części C # / .NET pytania
źródło
Ja często dodać metody nazywanej coś podobnego
validate
,verify
,check
, etc, do klasy tak, że może być wezwany do sprawdzenia stanu wewnętrznego obiektu.Czasami ta metoda jest zapakowana w blok ifdef (piszę głównie w C ++), więc nie jest kompilowana do wydania. Ale często jest przydatne w wydaniu, aby zapewnić metody sprawdzania, które sprawdzają drzewa obiektów w programie.
źródło
Guava ma adnotację @VisibleForTesting do oznaczania metod, które mają większy zakres (pakietowy lub publiczny), niż w innym przypadku. Używam adnotacji @Private dla tego samego.
Podczas gdy publiczny interfejs API musi zostać przetestowany, czasem wygodnie i sensownie jest dostać się do rzeczy, które normalnie nie byłyby publiczne.
Kiedy:
wygląda na to, że religia przewyższa inżynierię.
źródło
Zazwyczaj zostawiam te metody jako
protected
i umieszczam test jednostkowy w tym samym pakiecie (ale w innym projekcie lub folderze źródłowym), gdzie mogą uzyskać dostęp do wszystkich chronionych metod, ponieważ moduł ładujący klasy umieści je w tej samej przestrzeni nazw.źródło
Nie, ponieważ istnieją lepsze sposoby na skórowanie tego kota.
Niektóre wiązki testów jednostkowych opierają się na makrach w definicji klasy, które automatycznie rozszerzają się, tworząc haki, gdy są wbudowane w trybie testowym. Bardzo styl C, ale działa.
Łatwiejszym idiomem OO jest uczynienie czegokolwiek, co chcesz przetestować, „chronionym” niż „prywatnym”. Wiązka testowa dziedziczy po testowanej klasie, a następnie może uzyskać dostęp do wszystkich chronionych elementów.
Lub wybierz opcję „przyjaciel”. Osobiście jest to funkcja C ++, którą lubię najmniej, ponieważ łamie ona zasady enkapsulacji, ale zdarza się, że jest to konieczne do tego, jak C ++ implementuje niektóre funkcje, więc hej ho.
W każdym razie, jeśli przeprowadzasz testy jednostkowe, równie prawdopodobne jest, że będziesz musiał wprowadzić wartości do tych członków. Białe pola tekstowe są całkowicie poprawne. I to naprawdę byłoby złamać enkapsulacji.
źródło
W .Net istnieje specjalna klasa o nazwie
PrivateObject
deign specjalnie, aby umożliwić ci dostęp do prywatnych metod klasy.Zobacz więcej na ten temat w MSDN lub tutaj na Przepełnienie stosu
(Zastanawiam się, jak dotąd nikt o tym nie wspominał.)
Są jednak sytuacje, których to nie wystarczy, w takim przypadku będziesz musiał użyć refleksji.
Nadal bym przestrzegał ogólnej rekomendacji, by nie testować metod prywatnych, jednak jak zwykle zawsze są wyjątki.
źródło
Jak zauważono w komentarzach innych, testy jednostkowe powinny koncentrować się na publicznym interfejsie API. Jednak poza zaletami / wadami i uzasadnieniem można wywoływać metody prywatne w teście jednostkowym za pomocą odbicia. Musisz oczywiście upewnić się, że zabezpieczenia JRE na to pozwalają. Wywoływanie metod prywatnych jest czymś, co wykorzystuje Spring Framework ze swoim ReflectionUtils (zobacz
makeAccessible(Method)
metodę).Oto mała przykładowa klasa z metodą instancji prywatnej.
I przykładowa klasa, która wykonuje metodę instancji prywatnej.
Wykonanie B, wydrukuje
Doing something private.
Jeśli naprawdę tego potrzebujesz, odbicie może być użyte w testach jednostkowych w celu uzyskania dostępu do metod instancji prywatnej.źródło
Osobiście mam te same problemy podczas testowania metod prywatnych, a to dlatego, że niektóre narzędzia testowe są ograniczone. Nie jest dobrze, aby twój projekt był napędzany ograniczonymi narzędziami, jeśli nie reagują na twoją potrzebę, zmień narzędzie, a nie projekt. Ponieważ pytając o C # nie mogę zaproponować dobrych narzędzi do testowania, ale dla Javy istnieją dwa potężne narzędzia: TestNG i PowerMock, i można znaleźć odpowiednie narzędzia do testowania dla platformy .NET
źródło