Czy powinienem testować metody prywatne, czy tylko publiczne? [Zamknięte]

348

Przeczytałem ten post o tym, jak przetestować metody prywatne. Zwykle ich nie testuję, ponieważ zawsze myślałem, że szybsze jest testowanie tylko metod publicznych, które będą wywoływane spoza obiektu. Czy testujesz prywatne metody? Czy zawsze powinienem je testować?

Patrick Desjardins
źródło
„Czy powinienem przetestować prywatnych pomocników?” Tak. „Czy powinienem bezpośrednio testować prywatnych pomocników?” Zależy to, ogólnie od tego, czy można je łatwo przetestować za pomocą interfejsu publicznego, po co testować je bezpośrednio? Jeśli testowanie wszystkich aspektów pomocników za pomocą interfejsu publicznego staje się skomplikowane, to czy komponent przeżył swoje istnienie jako pojedynczej jednostki?
Mihai Danila

Odpowiedzi:

328

Nie testuję jednostkowo metod prywatnych. Metoda prywatna to szczegół implementacji, który powinien być ukryty dla użytkowników klasy. Testowanie prywatnych metod przerywa enkapsulację.

Jeśli stwierdzę, że prywatna metoda jest ogromna, złożona lub na tyle ważna, że ​​wymaga własnych testów, po prostu umieszczam ją w innej klasie i udostępniam tam publicznie ( Method Object ). Następnie mogę łatwo przetestować metodę wcześniej prywatną, ale teraz publiczną, która teraz żyje w swojej własnej klasie.

jop
źródło
88
Nie zgadzam się. Najlepiej jest napisać szybki test przed rozpoczęciem kodowania funkcji. Pomyśl o typowych danych wejściowych i wynikach. Napisz test (który nie powinien zająć więcej niż kilka sekund) i koduj, dopóki test nie zostanie poprawnie wykonany. Nie ma powodu, aby porzucać ten styl pracy dla metod prywatnych.
Frank
254
Mówienie, że prywatne metody nie wymagają testowania, jest jak powiedzenie, że samochód jest w porządku, o ile jeździ dobrze i nie ma znaczenia, co jest pod maską. Ale czy nie byłoby miło wiedzieć, że jakiś kabel w środku zaczyna się poluzować - nawet jeśli użytkownik niczego nie zauważa? Jasne, możesz upublicznić wszystko, ale o co chodzi? Zawsze będziesz potrzebować prywatnych metod.
Frank
37
„Metoda prywatna to szczegół implementacji, który powinien być ukryty dla użytkowników klasy”. ale czy testy są naprawdę po tej samej stronie interfejsu klasy, co „zwykli” użytkownicy (środowiska wykonawcze)? ;)
mlvljr
34
Niebezpieczeństwo wyciągnięcia wszystkiego, co chcesz przetestować do innej klasy, polega na tym, że możesz skończyć z nadmierną inżynierią produktu i mieć milion komponentów wielokrotnego użytku, które nigdy nie zostaną ponownie wykorzystane.
Occulus
44
Porównanie fragmentu kodu z samochodem jest błędne; z czasem kod „nie psuje się ”, jest wieczny . Jeśli testowanie interfejsu publicznego idzie tylko tak daleko, aby stwierdzić, że „ wygląda dobrze ”, to testowanie kodu publicznego jest niewystarczające. W takim przypadku osobne testowanie metod prywatnych nie zakończy pełnego testu bez względu na to, jak bardzo się starasz. Skoncentruj się na wyczerpującym przetestowaniu całego kodu publicznego, korzystając ze znajomości wewnętrznych zasad działania kodu, aby stworzyć odpowiednie scenariusze.
rustyx
293

Jaki jest cel testowania?

Większość dotychczasowych odpowiedzi mówi, że metody prywatne to szczegóły implementacji, które nie mają (a przynajmniej nie powinny) mieć znaczenia, o ile interfejs publiczny jest dobrze przetestowany i działa. Jest to absolutnie poprawne, jeśli jedynym celem testowania jest zagwarantowanie działania interfejsu publicznego .

Osobiście moim podstawowym zastosowaniem do testów kodu jest zapewnienie, aby przyszłe zmiany kodu nie powodowały problemów i pomoc w moich wysiłkach debugowania, jeśli tak się stanie. Uważam, że testowanie metod prywatnych tak dokładnie, jak interfejs publiczny (jeśli nie bardziej!) Wspiera ten cel.

Zastanów się: masz publiczną metodę A, która wywołuje metodę prywatną B. Zarówno A, jak i B korzystają z metody C. C ulega zmianie (być może przez ciebie, być może przez dostawcę), powodując, że A zaczyna nieudane testy. Czy nie byłoby przydatne posiadanie testów dla B, nawet jeśli są one prywatne, abyś wiedział, czy problem dotyczy użycia A przez C, B przez C, czy obu?

Testowanie metod prywatnych stanowi również wartość dodaną w przypadkach, w których testowe pokrycie interfejsu publicznego jest niepełne. Chociaż jest to sytuacja, której na ogół chcemy uniknąć, testowanie jednostek wydajności zależy zarówno od testów wykrywających błędy, jak i związanych z nimi kosztów rozwoju i utrzymania tych testów. W niektórych przypadkach korzyści ze 100% pokrycia testowego można uznać za niewystarczające, aby uzasadnić koszty tych testów, powodując luki w pokryciu testowym interfejsu publicznego. W takich przypadkach dobrze ukierunkowany test metody prywatnej może być bardzo skutecznym dodatkiem do bazy kodu.

Dave Sherohman
źródło
72
Problem polega na tym, że te „przyszłe zmiany kodu” niezmiennie oznaczają refaktoryzację wewnętrznych mechanizmów niektórych klas. Dzieje się tak często, że pisanie testów stanowi barierę dla refaktoryzacji.
Outlaw Programmer,
40
Ponadto, jeśli ciągle zmieniasz testy jednostkowe, straciłeś całą spójność w testowaniu, a nawet potencjalnie możesz tworzyć błędy w samych testach jednostkowych.
17 z 26
6
@ 17 Jeśli testy i implementacja zostaną zmodyfikowane synchronicznie (jak się wydaje, tak powinno być), problemów będzie znacznie mniej.
mlvljr
11
@Sauronlord, Powodem, dla którego testujesz metody prywatne, jest to, że jeśli testujesz tylko metody publiczne, kiedy test się nie powiedzie, nie wiemy bezpośrednio, gdzie leży główna przyczyna niepowodzenia. Może być w jednym testDoSomething()lub testDoSomethingPrivate(). To sprawia, że ​​test jest mniej wartościowy. . Oto więcej powodów, dla których warto przetestować prywatny stackoverflow.com/questions/34571/… :
Pacerier
3
@Pacerier Istnieje również różnica między testowaniem kodu a ciągłym automatycznym procesem testowym. Oczywiście powinieneś upewnić się, że twoja metoda prywatna działa, ale nie powinieneś mieć testów łączących cię z metodą prywatną, ponieważ nie jest to przypadek użycia oprogramowania.
Didier A.,
150

Zazwyczaj podążam za radą Dave'a Thomasa i Andy'ego Hunta w ich książce Pragmatic Unit Testing :

Ogólnie rzecz biorąc, nie chcesz zerwać żadnej enkapsulacji dla celów testowych (lub, jak zwykła mówić mama, „nie ujawniaj swoich szeregowych!”). Przez większość czasu powinieneś być w stanie przetestować klasę, stosując jej publiczne metody. Jeśli znaczna funkcjonalność jest ukryta za prywatnym lub chronionym dostępem, może to być znak ostrzegawczy, że istnieje inna klasa, która próbuje się wydostać.

Ale czasami nie mogę się powstrzymać od testowania prywatnych metod, ponieważ daje mi to poczucie, że buduję całkowicie solidny program.

Rosellyne Worrall
źródło
9
Poleciłbym wyłączenie testów jednostkowych ukierunkowanych na metody prywatne. Są sprzężeniem kodu i będą obciążać przyszłe prace związane z refaktoryzacją, a nawet czasami przeszkadzają w dodawaniu lub modyfikowaniu funkcji. Dobrze jest napisać dla nich test w trakcie ich wdrażania, jako zautomatyzowany sposób na potwierdzenie, że prace wdrożeniowe działają, ale utrzymanie testów jako regresji nie jest korzystne.
Didier A.
61

Czuję się zmuszony do testowania funkcji prywatnych, ponieważ coraz częściej stosuję się do jednego z naszych najnowszych zaleceń dotyczących kontroli jakości w naszym projekcie:

Nie więcej niż 10 w złożoności cyklicznej na funkcję.

Teraz efektem ubocznym egzekwowania tej polityki jest to, że wiele moich bardzo dużych funkcji publicznych jest podzielonych na wiele bardziej ukierunkowanych, lepiej nazwanych funkcji prywatnych .
Funkcja publiczna wciąż istnieje (oczywiście), ale jest zasadniczo zredukowana do wszystkich tych prywatnych „podfunkcji”

To jest naprawdę fajne, ponieważ callstack jest teraz znacznie łatwiejszy do odczytania (zamiast błędu w dużej funkcji, mam błąd w podfunkcji z nazwą poprzednich funkcji w callstacku, aby pomóc mi zrozumieć „jak się tam dostałem”)

Jednak teraz wydaje się łatwiejsze testowanie jednostkowe tych funkcji prywatnych i pozostawienie testowania dużej funkcji publicznej jakimś testom „integracyjnym”, w którym należy zająć się scenariuszem.

Tylko moje 2 centy.

VonC
źródło
2
aby zareagować na @jop, nie odczuwam potrzeby eksportowania tych funkcji prywatnych (utworzonych z powodu podziału zbyt dużej cyklicznej złożonej funkcji publicznej) do innej klasy. Lubię mieć je wciąż ściśle związane z funkcją publiczną, w tej samej klasie. Ale wciąż testowane jednostkowo.
VonC
2
Z mojego doświadczenia wynika, że ​​te prywatne metody są tylko metodą użyteczności, która jest ponownie wykorzystywana przez te publiczne metody. Czasami wygodniej jest podzielić oryginalną klasę na dwie (lub trzy) bardziej spójne klasy, dzięki czemu te prywatne metody są publiczne w swoich klasach, a zatem można je przetestować.
jop
7
nie, w moim przypadku te nowe funkcje prywatne są naprawdę częścią większego algorytmu reprezentowanego przez funkcję publiczną. Ta funkcja jest podzielona na mniejsze części, które nie są użyteczne, ale etapy większego procesu. Stąd potrzeba ich testowania jednostkowego (zamiast testowania całego algo naraz)
VonC
Dla osób zainteresowanych złożonością cykliczną dodałem pytanie na ten temat: stackoverflow.com/questions/105852/…
VonC
Ups, adres URL pytania zmienił się z powodu literówki w tytule! stackoverflow.com/questions/105852/…
VonC
51

Tak, testuję funkcje prywatne, ponieważ chociaż są one testowane za pomocą metod publicznych, miło jest w TDD (Test Driven Design) przetestować najmniejszą część aplikacji. Ale funkcje prywatne nie są dostępne, gdy jesteś w klasie jednostek testowych. Oto, co robimy, aby przetestować nasze prywatne metody.

Dlaczego mamy prywatne metody?

Funkcje prywatne istnieją głównie w naszej klasie, ponieważ chcemy stworzyć czytelny kod w naszych metodach publicznych. Nie chcemy, aby użytkownik tej klasy wywoływał te metody bezpośrednio, ale za pomocą naszych metod publicznych. Ponadto nie chcemy zmieniać ich zachowania podczas rozszerzania klasy (w przypadku ochrony), dlatego jest to prywatna.

Kiedy kodujemy, używamy projektowania testowego (TDD). Oznacza to, że czasami natrafiamy na element prywatny, który chcemy przetestować. Funkcje prywatne nie są testowane w phpUnit, ponieważ nie możemy uzyskać do nich dostępu w klasie Test (są prywatne).

Uważamy, że tutaj są 3 rozwiązania:

1. Możesz przetestować swoich szeregowych za pomocą metod publicznych

Zalety

  • Proste testowanie jednostkowe (nie wymaga „włamań”)

Niedogodności

  • Programista musi zrozumieć metodę publiczną, a on chce tylko przetestować metodę prywatną
  • Nie testujesz najmniejszej testowalnej części aplikacji

2. Jeśli wartość prywatna jest tak ważna, być może jest to kod, aby utworzyć dla niej nową osobną klasę

Zalety

  • Możesz zmienić to na nową klasę, ponieważ jeśli jest to tak ważne, inne klasy również mogą jej potrzebować
  • Jednostka testowana jest teraz metodą publiczną, więc można ją przetestować

Niedogodności

  • Nie chcesz tworzyć klasy, jeśli nie jest ona potrzebna i jest używana tylko przez klasę, z której pochodzi metoda
  • Potencjalna utrata wydajności z powodu dodatkowego obciążenia

3. Zmień modyfikator dostępu na (końcowy) chroniony

Zalety

  • Testujesz najmniejszą testowalną część aplikacji. Podczas korzystania z ostatecznej ochrony funkcja nie będzie nadpisywana (tak jak prywatna)
  • Bez utraty wydajności
  • Bez dodatkowych kosztów ogólnych

Niedogodności

  • Zmieniasz prywatny dostęp do chronionego, co oznacza, że ​​jest dostępny dla jego dzieci
  • Nadal potrzebujesz klasy próbnej w klasie testowej, aby z niej korzystać

Przykład

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

Nasza jednostka testowa może teraz wywołać test_sleepWithSuspect, aby przetestować naszą dawną funkcję prywatną.

eddy147
źródło
eddy147, naprawdę podoba mi się koncepcja testowania chronionych metod za pomocą próbnych. Dzięki!!!!
Theodore R. Smith
15
Chciałbym tylko zaznaczyć, że w oryginalnym opisie TDD w testach jednostkowych jednostką jest klasa , a nie metoda / funkcja. Kiedy więc wspominasz o „testowaniu najmniejszej części aplikacji”, błędem jest odwoływanie się do najmniejszej testowalnej części jako metody. Jeśli użyjesz tej logiki, równie dobrze możesz mówić o jednym wierszu kodu zamiast o całym bloku kodu.
Matt Quigley,
@Matt Jednostka pracy może wskazywać na klasę, ale także na jedną metodę.
eddy147,
4
@ eddy147 Testy jednostkowe to Test Driven Development, w którym jednostka została zdefiniowana jako klasa. Jak to ma miejsce w przypadku The Internets, semantyka rozwinęła się i oznacza wiele rzeczy (tj. Zapytaj 2 osoby, jaka jest różnica między testowaniem jednostkowym a testem integracji, a otrzymasz 7 odpowiedzi). TDD miał być sposobem na pisanie oprogramowania opartego na zasadach SOLID, w tym na pojedynczej odpowiedzialności, w którym klasa ma jedną odpowiedzialność i nie powinna mieć dużej cyklicznej złożoności. W TDD piszesz swoją klasę i testujesz razem, zarówno jednostkę. Prywatne metody są enkapsulowane, nie mają odpowiedniego testu jednostkowego.
Matt Quigley,
„Kiedy kodujemy, używamy projektowania testowego (TDD). Oznacza to, że czasami natkniemy się na część funkcji, która jest prywatna i chcemy przetestować”. Zdecydowanie nie zgadzam się z tym stwierdzeniem, proszę zobaczyć moją odpowiedź poniżej w celu uzyskania dalszych szczegółów. TDD nie oznacza, że ​​musisz testować prywatne metody. Możesz przetestować metody prywatne: i to twój wybór, ale to nie TDD zmusza cię do zrobienia czegoś takiego.
Matt Messersmith,
41

Nie lubię testować prywatnej funkcjonalności z kilku powodów. Są one następujące (są to główne punkty dla osób TLDR):

  1. Zazwyczaj, gdy masz ochotę przetestować prywatną metodę klasy, jest to zapach projektowy.
  2. Możesz je przetestować za pomocą publicznego interfejsu (w taki sposób chcesz je przetestować, ponieważ w ten sposób klient będzie je wywoływał / używał). Możesz uzyskać fałszywe poczucie bezpieczeństwa, widząc zielone światło na wszystkich pozytywnych testach dla twoich prywatnych metod. O wiele lepiej / bezpieczniej jest testować najnowsze przypadki prywatnych funkcji za pośrednictwem publicznego interfejsu.
  3. Ryzykujesz poważne powielanie testów (testy, które wyglądają / czują się bardzo podobnie), testując prywatne metody. Ma to poważne konsekwencje, gdy zmienią się wymagania, ponieważ przerwie się o wiele więcej testów niż to konieczne. Może także postawić cię w sytuacji, w której trudno jest dokonać refaktoryzacji z powodu twojego zestawu testowego ... co jest ostateczną ironią, ponieważ zestaw testowy jest po to, aby pomóc ci bezpiecznie przeprojektować i refaktoryzować!

Wyjaśnię każdy z nich konkretnym przykładem. Okazuje się, że 2) i 3) są dość ściśle powiązane, więc ich przykład jest podobny, chociaż uważam je za osobne powody, dla których nie powinieneś testować metod prywatnych.

Są chwile, w których testowanie prywatnych metod jest odpowiednie, ważne jest, aby zdawać sobie sprawę z wyżej wymienionych wad. Omówię to później bardziej szczegółowo.

Zastanawiam się również, dlaczego TDD nie jest usprawiedliwieniem do testowania prywatnych metod na samym końcu.

Refaktoryzacja wyjścia ze złego projektu

Jednym z najczęstszych (anty) paternów, które widzę, jest to, co Michael Feathers nazywa klasą „Góry lodowej” (jeśli nie wiesz, kim jest Michael Feathers, idź kupić / przeczytaj jego książkę „Skutecznie współpracując ze Starszym Kodem”. osoba, o której warto wiedzieć, czy jesteś profesjonalnym inżynierem / programistą). Istnieją inne (anty) wzorce, które powodują pojawienie się tego problemu, ale jest to zdecydowanie najbardziej powszechny, z jakim się zetknąłem. Klasy „Góra lodowa” mają jedną metodę publiczną, a pozostałe są prywatne (dlatego kuszące jest przetestowanie metod prywatnych). Nazywa się to klasą „Góra lodowa”, ponieważ zwykle występuje samotna metoda publiczna, ale reszta funkcji jest ukryta pod wodą w postaci prywatnych metod.

Oceniacz reguł

Na przykład możesz przetestować GetNextToken(), wywołując go kolejno i sprawdzając, czy zwraca oczekiwany wynik. Funkcja taka jak ta gwarantuje test: takie zachowanie nie jest trywialne, szczególnie jeśli twoje reguły tokenizacji są złożone. Udawajmy, że to nie jest aż tak skomplikowane, a my chcemy po prostu wplatać tokeny oddzielone spacją. Więc piszesz test, może wygląda to tak (jakiś agnostyczny kod psuedo, mam nadzieję, że pomysł jest jasny):

TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    re = RuleEvaluator(input_string);

    ASSERT re.GetNextToken() IS "1";
    ASSERT re.GetNextToken() IS "2";
    ASSERT re.GetNextToken() IS "test";
    ASSERT re.GetNextToken() IS "bar";
    ASSERT re.HasMoreTokens() IS FALSE;
}

Cóż, to naprawdę ładnie wygląda. Chcemy mieć pewność, że utrzymamy to zachowanie podczas wprowadzania zmian. Ale GetNextToken()jest funkcją prywatną ! Nie możemy więc przetestować tego w ten sposób, ponieważ nawet się nie skompiluje (zakładając, że używamy języka, który faktycznie wymusza publiczny / prywatny, w przeciwieństwie do niektórych języków skryptowych, takich jak Python). Ale co ze zmianą RuleEvaluatorklasy, aby postępowała zgodnie z zasadą pojedynczej odpowiedzialności (zasada pojedynczej odpowiedzialności)? Na przykład wydaje się, że analizator składni, tokenizer i ewaluator są zablokowane w jednej klasie. Czy nie lepiej byłoby po prostu rozdzielić te obowiązki? Ponadto, jeśli utworzysz Tokenizerklasę, wówczas będą to metody publiczne HasMoreTokens()i GetNextTokens(). RuleEvaluatorKlasa mogłaby miećTokenizerobiekt jako członek. Teraz możemy zachować taki sam test jak powyżej, z tym wyjątkiem, że testujemy Tokenizerklasę zamiast RuleEvaluatorklasy.

Oto, jak może to wyglądać w UML:

Refaktoryzacja reguły ewaluacyjnej

Zauważ, że ten nowy projekt zwiększa modułowość, więc możesz potencjalnie ponownie użyć tych klas w innych częściach twojego systemu (zanim nie mogłeś, metody prywatne nie są z definicji wielokrotnego użytku). Jest to główna zaleta zepsucia RuleEvaluator wraz ze zwiększoną zrozumiałością / lokalizacją.

Test wyglądałby bardzo podobnie, tyle że faktycznie skompilowałby się tym razem, ponieważ GetNextToken()metoda jest teraz publicznie dostępna w Tokenizerklasie:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS FALSE;
}

Testowanie komponentów prywatnych za pomocą publicznego interfejsu i unikanie powielania testów

Nawet jeśli nie uważasz, że możesz podzielić swój problem na mniejszą liczbę komponentów modułowych (co możesz zrobić w 95% przypadków, jeśli tylko spróbujesz to zrobić), możesz po prostu przetestować funkcje prywatne za pośrednictwem publicznego interfejsu. Często członkowie prywatni nie są warci testowania, ponieważ będą testowani przez interfejs publiczny. Często widzę testy, które wyglądają bardzo podobnie, ale testują dwie różne funkcje / metody. Ostatecznie dzieje się tak, że gdy wymagania się zmieniają (i zawsze tak się dzieje), masz teraz 2 zepsute testy zamiast 1. A jeśli naprawdę przetestowałeś wszystkie swoje prywatne metody, możesz mieć więcej takich jak 10 zepsutych testów zamiast 1. W skrócie , testowanie funkcji prywatnych (przy użyciuFRIEND_TESTlub upublicznienie ich lub użycie refleksji), które w innym przypadku mogłyby zostać przetestowane przez interfejs publiczny, mogą spowodować powielenie testu . Naprawdę tego nie chcesz, ponieważ nic nie boli bardziej niż zestaw testów, który cię spowalnia. Ma skrócić czas opracowywania i koszty konserwacji! W przypadku testowania metod prywatnych, które w innym przypadku byłyby testowane za pomocą interfejsu publicznego, zestaw testów może równie dobrze zrobić coś przeciwnego i aktywnie zwiększyć koszty konserwacji i wydłużyć czas programowania. Kiedy upubliczniasz funkcję prywatną lub jeśli używasz czegoś takiego FRIEND_TESTi / lub refleksji, zwykle na dłuższą metę zwykle będziesz żałować.

Rozważ następującą możliwą implementację Tokenizerklasy:

wprowadź opis zdjęcia tutaj

Powiedzmy, że SplitUpByDelimiter()odpowiada za zwrócenie tablicy tak, aby każdy element w tablicy był tokenem. Co więcej, powiedzmy, że GetNextToken()jest to po prostu iterator tego wektora. Twój publiczny test może wyglądać następująco:

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS false;
}

Udawajmy, że mamy narzędzie, którego nazywa Michael Feather . To narzędzie pozwala dotykać części prywatnych innych osób. Przykład pochodzi FRIEND_TESTz googletestu lub refleksji, jeśli język go obsługuje.

TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);
    result_array = tokenizer.SplitUpByDelimiter(" ");

    ASSERT result.size() IS 4;
    ASSERT result[0] IS "1";
    ASSERT result[1] IS "2";
    ASSERT result[2] IS "test";
    ASSERT result[3] IS "bar";
}

Powiedzmy teraz, że wymagania się zmieniają, a tokenizacja staje się znacznie bardziej złożona. Decydujesz, że prosty ogranicznik łańcucha nie wystarczy i potrzebujesz Delimiterklasy do obsługi zadania. Oczywiście, będziesz oczekiwać, że jeden test się zepsuje, ale ten ból nasila się, gdy testujesz funkcje prywatne.

Kiedy testowanie metod prywatnych może być właściwe?

W oprogramowaniu nie ma „jednego rozmiaru dla wszystkich”. Czasami w porządku (i właściwie idealnym) jest „łamanie zasad”. Zdecydowanie opowiadam się za nie testowaniem prywatnej funkcjonalności, kiedy możesz. Są dwie główne sytuacje, kiedy myślę, że jest w porządku:

  1. Pracowałem intensywnie ze starszymi systemami (dlatego jestem wielkim fanem Michaela Feathersa) i mogę śmiało powiedzieć, że czasami najbezpieczniej jest po prostu przetestować prywatną funkcjonalność. Może to być szczególnie pomocne przy wprowadzaniu „testów charakteryzacyjnych” do linii podstawowej.

  2. Spieszysz się i musisz zrobić najszybszą możliwą rzecz tu i teraz. Na dłuższą metę nie chcesz testować prywatnych metod. Powiem jednak, że refaktoryzacja zazwyczaj zajmuje trochę czasu, aby rozwiązać problemy projektowe. A czasem musisz wysłać za tydzień. W porządku: zrób szybki i brudny test i przetestuj prywatne metody za pomocą narzędzia do omijania, jeśli uważasz, że jest to najszybszy i najbardziej niezawodny sposób na wykonanie zadania. Ale zrozumcie, że to, co zrobiliście, nie było optymalne na dłuższą metę, i proszę rozważyć powrót do tego (lub, jeśli zostało zapomniane, ale zobaczycie to później, naprawcie to).

Prawdopodobnie są inne sytuacje, w których jest w porządku. Jeśli uważasz, że to w porządku i masz dobre uzasadnienie, zrób to. Nikt cię nie powstrzymuje. Pamiętaj tylko o potencjalnych kosztach.

Wymówka TDD

Nawiasem mówiąc, naprawdę nie lubię ludzi używających TDD jako wymówki do testowania prywatnych metod. Ćwiczę TDD i nie sądzę, żeby TDD zmusiło cię do tego. Możesz najpierw napisać test (dla interfejsu publicznego), a następnie napisać kod, aby spełnić ten interfejs. Czasami piszę test interfejsu publicznego i spełnię go, pisząc jedną lub dwie mniejsze metody prywatne (ale nie testuję metod prywatnych bezpośrednio, ale wiem, że działają lub mój test publiczny nie powiedzie się ). Jeśli będę musiał przetestować przypadki tej prywatnej metody, napiszę całą masę testów, które trafią je za pośrednictwem mojego publicznego interfejsu.Jeśli nie możesz dowiedzieć się, jak trafić na skrajne skrzynie, jest to mocny znak, że musisz przeforsować małe komponenty za pomocą własnych metod publicznych. To znak, że funkcje prywatne robisz za dużo i poza zakresem klasy .

Czasami też zdarza mi się, że piszę test, który jest w tej chwili zbyt duży, by go przeżuć, i dlatego myślę: „eh wrócę do tego testu później, gdy będę miał więcej interfejsu API do pracy” (I Skomentuję to i zachowam w pamięci). W tym miejscu wielu deweloperów, których spotkałem, zacznie pisać testy ich prywatnej funkcjonalności, używając TDD jako kozła ofiarnego. Mówią „och, no cóż, potrzebuję innego testu, ale aby napisać ten test, będę potrzebować tych prywatnych metod. Dlatego, ponieważ nie mogę napisać żadnego kodu produkcyjnego bez napisania testu, muszę napisać test dla metody prywatnej ”. Ale to, co naprawdę muszą zrobić, to przefakturowanie na mniejsze komponenty wielokrotnego użytku zamiast dodawania / testowania szeregu prywatnych metod do ich obecnej klasy.

Uwaga:

Niedawno odpowiedziałem na podobne pytanie dotyczące testowania metod prywatnych za pomocą GoogleTest . Przeważnie zmodyfikowałem tę odpowiedź, aby była bardziej niezależna od języka.

PS Oto odpowiedni wykład na temat zajęć z góry lodowej i narzędzi do omijania autorstwa Michaela Feathersa: https://www.youtube.com/watch?v=4cVZvoFGJTU

Matt Messersmith
źródło
Problem z listowaniem „potencjalnie możesz ponownie użyć tych klas w innych częściach twojego systemu” jako zaletą jest to, że czasami oznaczam funkcję jako prywatną, ponieważ nie chcę, aby była używana przez inne części system. Jest to problem specyficzny dla języka: najlepiej byłoby prywatny dla „modułu”, ale jeśli język tego nie obsługuje (np. PHP), moja klasa reprezentuje moduł, a nie jednostkę: metody prywatne są kodem wielokrotnego użytku z własnymi umowami, ale mogą być ponownie wykorzystane tylko w tej klasie.
IMSoP
Rozumiem, co mówisz, ale podoba mi się sposób, w jaki społeczność Python rozwiązuje ten problem. Jeśli nazywasz danego „prywatnego” członka wiodącym _, oznacza to „hej, to jest„ prywatny ”. Możesz go użyć, ale pełne ujawnienie, nie został zaprojektowany do ponownego użycia i powinieneś go użyć tylko, jeśli naprawdę wiesz co robisz ". Możesz zastosować to samo podejście w dowolnym języku: upublicznij tych członków, ale oznacz ich wiodącym _. A może te funkcje naprawdę powinny być prywatne i po prostu przetestowane za pomocą publicznego interfejsu (więcej informacji znajduje się w odpowiedzi). To przypadek po przypadku, brak ogólnej zasady
Matt Messersmith
26

Myślę, że najlepiej jest po prostu przetestować publiczny interfejs obiektu. Z punktu widzenia świata zewnętrznego liczy się tylko zachowanie interfejsu publicznego i do tego powinny być kierowane testy jednostkowe.

Gdy masz już napisane solidne testy jednostkowe dla obiektu, nie musisz wracać i zmieniać tych testów tylko dlatego, że zmieniła się implementacja interfejsu. W tej sytuacji zrujnowałeś spójność testów jednostkowych.

17 z 26
źródło
21

Jeśli twoja metoda prywatna nie jest testowana przez wywołanie metod publicznych, to co ona robi? Mówię prywatnie nie chroniony lub przyjaciel.

chrissie1
źródło
3
Dziękuję Ci. Jest to zaskakująco niedoceniany komentarz i szczególnie istotny, nawet po prawie 8 latach od jego napisania.
Sauronlord,
1
Z tego samego powodu można argumentować, aby testować oprogramowanie tylko z interfejsu użytkownika (testowanie na poziomie systemu), ponieważ jakoś każda funkcja w oprogramowaniu byłaby w jakiś sposób stamtąd uruchamiana.
Dirk Herrmann
18

Jeśli metoda prywatna jest dobrze zdefiniowana (tj. Ma funkcję, która jest testowalna i nie ma zmieniać się w czasie), to tak. Testuję wszystko, co da się przetestować tam, gdzie ma to sens.

Na przykład biblioteka szyfrująca może ukrywać fakt, że wykonuje szyfrowanie blokowe za pomocą prywatnej metody, która szyfruje tylko 8 bajtów naraz. Napisałbym do tego test jednostkowy - nie ma się to zmienić, nawet jeśli jest ukryte, a jeśli się zepsuje (na przykład z powodu przyszłych ulepszeń wydajności), chcę wiedzieć, że zepsuła się prywatna funkcja, nie tylko zepsuła się jedna z funkcji publicznych.

Przyspiesza późniejsze debugowanie.

-Adam

Adam Davis
źródło
1
W takim przypadku, czy nie ma sensu przenosić tej prywatnej metody do innej klasy, a następnie po prostu ustawić ją jako publiczną lub publiczną statyczną?
Outlaw Programmer,
+1 Jeśli nie przetestujesz funkcji prywatnego członka, a test interfejsu publicznego nie powiedzie się, otrzymasz wynik równoważny z czymś, co jest zepsute, nie mając pojęcia, co to jest.
Olumide
12

Jeśli rozwijasz się pod kontrolą testową (TDD), przetestujesz swoje prywatne metody.

Jader Dias
źródło
2
Wyodrębnisz
Josh Johnson,
4
Nieprawda, testujesz swoje metody publiczne, a po zdaniu testów wyodrębniasz kod z metod publicznych na metody prywatne, wykonując krok „czyszczenia”. Testowanie prywatnych metod jest złym pomysłem, ponieważ utrudnia zmianę sposobu implementacji (co jeśli pewnego dnia chcesz zmienić sposób, w jaki coś robisz, powinieneś być w stanie to zmienić i uruchomić wszystkie testy, a jeśli twój nowy sposób wykonywania wszystko jest w porządku, że powinni przejść, nie chciałbym zmieniać wszystkich moich prywatnych testów).
Tesseract,
1
@Tesseract, gdybym mógł głosować twój komentarz więcej niż raz, zrobiłbym to. „... powinieneś być w stanie to zmienić i uruchomić wszystkie swoje testy, a jeśli twój nowy sposób robienia rzeczy jest prawidłowy, powinien przejść pomyślnie” TO jest jedna z głównych zalet testów jednostkowych. Umożliwiają ci pewną refaktoryzację. Możesz całkowicie zmienić wewnętrzne prywatne działania swojej klasy i (bez przepisywania wszystkich testów jednostkowych) masz pewność, że niczego nie zepsułeś, ponieważ wszystkie (istniejące) testy jednostkowe (w interfejsie publicznym) nadal się sprawdzają.
Lee,
Nie zgadzam się, zobacz moją odpowiedź poniżej
Matt Messersmith
11

Nie jestem ekspertem w tej dziedzinie, ale testy jednostkowe powinny testować zachowanie, a nie implementację. Metody prywatne są ściśle częścią wdrożenia, więc IMHO nie powinno być testowane.

maxbog
źródło
Gdzie następnie testowane jest wdrożenie? Jeśli niektóre funkcje korzystają z buforowania, czy jest to szczegół implementacji, a buforowanie nie jest testowane?
Dirk Herrmann
11

Testujemy metody prywatne na podstawie wnioskowania, co oznacza, że ​​szukamy całkowitego pokrycia testu klasy co najmniej 95%, ale nasze testy wymagają jedynie metod publicznych lub wewnętrznych. Aby uzyskać zasięg, musimy wykonać wiele połączeń z podmiotami publicznymi / wewnętrznymi w zależności od różnych możliwych scenariuszy. To sprawia, że ​​nasze testy są bardziej skupione na celu testowanego kodu.

Odpowiedź Trumpiego na post, który podałeś, jest najlepsza.

Tom Carr
źródło
9

Testy jednostkowe, jak sądzę, służą do testowania metod publicznych. Twoje metody publiczne używają metod prywatnych, więc pośrednio są one również testowane.

scubabbl
źródło
7

Długo zastanawiałem się nad tym problemem, szczególnie próbując swoich sił w TDD.

Natknąłem się na dwa posty, które moim zdaniem wystarczająco dokładnie rozwiązują ten problem w przypadku TDD.

  1. Testowanie metod prywatnych, TDD i testowania opartego na testach
  2. Rozwój oparty na testach nie jest testowaniem

W podsumowaniu:

  • W przypadku korzystania z technik programowania (projektowania) opartego na testach, prywatne metody powinny powstać tylko podczas procesu przefaktoryzowania już działającego i przetestowanego kodu.

  • Ze względu na naturę procesu każda prosta funkcja implementacji wyodrębniona z dokładnie przetestowanej funkcji będzie autotestem (tj. Zasięgiem testów pośrednich).

Wydaje mi się wystarczająco jasne, że na początku kodowania większość metod będzie funkcjami wyższego poziomu, ponieważ zawierają w sobie / opisują projekt.

Dlatego te metody będą dostępne publicznie, a ich testowanie będzie dość łatwe.

Prywatne metody pojawią się później, gdy wszystko będzie działało dobrze, a my zajmiemy się faktoringiem ze względu na czytelność i czystość .

dkinzer
źródło
6

Jak wspomniano powyżej: „Jeśli nie przetestujesz swoich prywatnych metod, skąd wiesz, że się nie złamią?”

To jest poważny problem. Jednym z głównych punktów testów jednostkowych jest wiedzieć, gdzie, kiedy i jak coś się zepsuło jak najszybciej. W ten sposób zmniejsza się znaczna ilość prac rozwojowych i zapewniania jakości. Jeśli wszystko, co jest testowane, to opinia publiczna, to nie masz uczciwego opisu i opisu elementów wewnętrznych klasy.

Znalazłem jeden z najlepszych sposobów, aby to zrobić, po prostu dodaj odwołanie do testu do projektu i umieść testy w klasie równoległej do metod prywatnych. Wprowadź odpowiednią logikę kompilacji, aby testy nie były wbudowane w końcowy projekt.

Następnie masz wszystkie zalety przetestowania tych metod i możesz znaleźć problemy w ciągu kilku sekund w stosunku do minut lub godzin.

Podsumowując, tak, jednostka przetestuj swoje prywatne metody.

Adron
źródło
2
Nie zgadzam się. „Jeśli nie przetestujesz swoich prywatnych metod, skąd wiesz, że się nie złamią?” : Wiem o tym, ponieważ jeśli moje prywatne metody zostaną zepsute, testy, które testują moje publiczne metody oparte na tych prywatnych metodach, zakończą się niepowodzeniem. Nie chcę zmieniać moich testów za każdym razem, gdy zmieniam zdanie na temat implementacji metod publicznych. Uważam również, że głównym celem testów jednostkowych jest nie wiedzieć konkretnie, który wiersz kodu jest wadliwy, raczej pozwala on być mniej lub bardziej pewny, że niczego nie złamałeś podczas wprowadzania zmian (do metod prywatnych).
Tesseract,
6

Nie powinieneś . Jeśli twoje prywatne metody mają wystarczającą złożoność, którą należy przetestować, powinieneś umieścić je w innej klasie. Zachowaj wysoką spójność , klasa powinna mieć tylko jeden cel. Klasowy interfejs publiczny powinien wystarczyć.

fernandezdavid7
źródło
3

Jeśli nie przetestujesz swoich prywatnych metod, skąd wiesz, że się nie złamią?

Billy Jo
źródło
19
Pisząc poprzez testy swoich publicznych metod.
scubabbl
3
Te prywatne metody są rzekomo wywoływane przez publiczne metody klasy. Więc po prostu przetestuj metody publiczne, które wywołują metody prywatne.
jop
1
Jeśli twoje metody publiczne działają poprawnie, to oczywiście prywatne metody, do których mają dostęp, działają poprawnie.
17 z 26
Jeśli testy twoich publicznych metod nie powiodą się, natychmiast wiesz, że coś jest nie tak na niższym poziomie w twoim obiekcie / komponencie / itp.
Rob
3
To naprawdę miłe, jednak wiemy, że jest to funkcja wewnętrzna, a nie tylko funkcji zewnętrznych, który wybuchł (lub odwrotnie, że funkcje wewnętrzne są w porządku i można skupić się na zewnątrz).
Adam Davis,
2

To oczywiście zależy od języka. W przeszłości z c ++ zadeklarowałem klasę testową jako klasę zaprzyjaźnioną. Niestety wymaga to, aby Twój kod produkcyjny wiedział o klasie testowej.

dvorak
źródło
5
Słowo kluczowe znajomego zasmuca mnie.
Rob
Nie stanowi to problemu, jeśli klasa testowa jest zaimplementowana w innym projekcie. Ważne jest to, że kod produkcyjny nie odwołuje się do klasy testowej.
Olumide
2

Rozumiem punkt widzenia, w którym prywatne metody są uważane za szczegóły implementacji, a następnie nie muszą być testowane. I trzymałbym się tej zasady, gdybyśmy musieli rozwijać się poza obiektem. Ale my, czy jesteśmy rodzajem ograniczonych programistów, którzy rozwijają się tylko poza obiektami, nazywając tylko swoje metody publiczne? Czy też faktycznie rozwijamy również ten obiekt? Ponieważ nie jesteśmy zobowiązani do programowania obiektów zewnętrznych, prawdopodobnie będziemy musieli wywołać te prywatne metody w nowe, opracowywane przez nas metody publiczne. Czy nie byłoby wspaniale wiedzieć, że prywatna metoda jest odporna na wszelkie przeciwności?

Wiem, że niektórzy ludzie mogą odpowiedzieć, że jeśli opracowujemy inną metodę publiczną w tym obiekcie, to ten powinien zostać przetestowany i to wszystko (prywatna metoda mogłaby żyć bez testu). Ale dotyczy to również wszelkich publicznych metod obiektu: podczas opracowywania aplikacji internetowej wszystkie publiczne metody obiektu są wywoływane z metod kontrolerów, a zatem mogą być uważane za szczegóły implementacji dla kontrolerów.

Dlaczego więc przeprowadzamy testy jednostkowe obiektów? Ponieważ jest to naprawdę trudne, nie można nie mieć pewności, że testujemy metody kontrolerów przy użyciu odpowiednich danych wejściowych, które uruchomią wszystkie gałęzie kodu źródłowego. Innymi słowy, im wyżej znajdujemy się na stosie, tym trudniej jest przetestować wszystkie zachowania. Podobnie jest w przypadku metod prywatnych.

Dla mnie granica między metodami prywatnymi i publicznymi jest psychologicznym kryterium, jeśli chodzi o testy. Najważniejsze dla mnie kryteria to:

  • czy metoda jest wywoływana więcej niż raz z różnych miejsc?
  • czy metoda jest wystarczająco zaawansowana, aby wymagać testów?
Olivier Pichon
źródło
1

Jeśli stwierdzę, że prywatna metoda jest ogromna, złożona lub wystarczająco ważna, aby wymagać własnych testów, po prostu umieszczam ją w innej klasie i udostępniam tam publicznie (Method Object). Następnie mogę łatwo przetestować wcześniej prywatną, ale teraz publiczną metodę, która teraz żyje w swojej własnej klasie.

Andy
źródło
1

Nigdy nie rozumiem pojęcia testu jednostkowego, ale teraz wiem, jaki jest jego cel.

Test jednostkowy nie jest pełnym testem . Nie zastępuje to kontroli jakości ani testu ręcznego. Koncepcja TDD w tym aspekcie jest błędna, ponieważ nie można przetestować wszystkiego, w tym metod prywatnych, ale także metod wykorzystujących zasoby (zwłaszcza zasoby, nad którymi nie mamy kontroli). TDD opiera całą swoją jakość na czymś, czego nie można osiągnąć.

Test jednostkowy jest raczej testem przestawnym Zaznaczasz jakiś dowolny punkt przestawny, a wynik przestawienia powinien pozostać taki sam.

magallanes
źródło
1

Publiczny vs. prywatny nie jest przydatnym rozróżnieniem dla tego, do czego api mogą zadzwonić z twoich testów, ani metoda vs. klasa. Większość testowalnych jednostek jest widoczna w jednym kontekście, ale ukryta w innych.

Liczy się ochrona i koszty. Musisz zminimalizować koszty przy jednoczesnym osiągnięciu celów pokrycia twojego projektu (linia, gałąź, ścieżka, blok, metoda, klasa, klasa równoważności, przypadek użycia ... cokolwiek decyduje zespół).

Dlatego używaj narzędzi, aby zapewnić zasięg, i zaprojektuj testy tak, aby powodowały jak najniższe koszty (krótko- i długoterminowe ).

Nie rób testów droższych niż to konieczne. Jeśli testowanie publicznych punktów wejścia jest najtańsze, zrób to. Jeśli testowanie metod prywatnych jest najtańsze, zrób to.

W miarę zdobywania doświadczenia możesz lepiej przewidywać, kiedy warto dokonać refaktoryzacji, aby uniknąć długoterminowych kosztów utrzymania testów.

tkruse
źródło
0

Jeśli metoda jest wystarczająco znacząca / wystarczająco złożona, zwykle sprawię, że będzie „chroniona” i przetestuję ją. Niektóre metody pozostaną prywatne i przetestowane pośrednio jako część testów jednostkowych dla metod publicznych / chronionych.


źródło
1
@VisibleForTesting jest do tego adnotacją. Nie rozluźniam enkapsulacji do testowania, a raczej korzystam z dp4j.com
simpatico
0

Widzę, że wiele osób ma takie samo zdanie: test na poziomie publicznym. ale czy nie to robi nasz zespół ds. kontroli jakości? Testują wejście i oczekiwane wyjście. Jeśli jako programiści testujemy tylko metody publiczne, po prostu ponawiamy zadanie kontroli jakości i nie dodajemy żadnej wartości poprzez „testowanie jednostkowe”.

aemorales1
źródło
Obecny trend polega na zmniejszaniu lub braku zespołu ds. Kontroli jakości. Te testy jednostkowe stają się testami automatycznymi, które są uruchamiane za każdym razem, gdy inżynier wciska kod w gałęzi master. Nawet w przypadku kontroli jakości nie ma możliwości przetestowania całej aplikacji tak szybko, jak testy automatyczne.
Patrick Desjardins,
0

Odpowiedź na „Czy powinienem przetestować metody prywatne?” jest czasami". Zazwyczaj powinieneś testować na interfejsie swoich klas.

  • Jednym z powodów jest to, że funkcja nie wymaga podwójnego pokrycia.
  • Innym powodem jest to, że jeśli zmienisz metody prywatne, będziesz musiał zaktualizować każdy test dla nich, nawet jeśli interfejs Twojego obiektu w ogóle się nie zmienił.

Oto przykład:

class Thing
  def some_string
    one + two
  end

  private 

  def one
    'aaaa'
  end

  def two
    'bbbb'
  end

end


class RefactoredThing
def some_string
    one + one_a + two + two_b
  end

  private 

  def one
    'aa'
  end

  def one_a
    'aa'
  end

  def two
    'bb'
  end

  def two_b
    'bb'
  end
end

W RefactoredThingmasz teraz 5 testów, z których 2 trzeba było zaktualizować do refactoring, ale funkcjonalność Twojego obiektu naprawdę nie uległa zmianie. Powiedzmy, że rzeczy są bardziej skomplikowane i masz jakąś metodę, która określa kolejność danych wyjściowych, na przykład:

def some_string_positioner
  if some case
  elsif other case
  elsif other case
  elsif other case
  else one more case
  end
end

To nie powinno być uruchamiane przez zewnętrznego użytkownika, ale twoja klasa enkapsulacji może być zbyt ciężka, aby uruchamiać w niej tyle logiki w kółko. W takim przypadku może wolisz wyodrębnić to do osobnej klasy, dać tej klasie interfejs i przetestować ją.

I na koniec, powiedzmy, że twój główny obiekt jest bardzo ciężki, a metoda jest dość mała i naprawdę musisz upewnić się, że dane wyjściowe są prawidłowe. Myślisz: „Muszę przetestować tę prywatną metodę!”. Czy zdajesz sobie sprawę, że możesz uczynić obiekt lżejszym, przekazując część ciężkiej pracy jako parametr inicjalizacji? Następnie możesz podać coś lżejszego i przetestować na tym.

rozjaśnia
źródło
0

Nie. Dlaczego nie powinieneś testować prywatnych metod ? a ponadto popularna frameworka mockingowa, taka jak Mockito, nie zapewnia wsparcia dla testowania metod prywatnych.

Cammando
źródło
0

Jednym z głównych punktów jest

Jeśli przeprowadzamy testy w celu zapewnienia poprawności logiki, a logika prywatna przenosi logikę, powinniśmy ją przetestować. Czyż nie Dlaczego więc to pomijamy?

Pisanie testów w oparciu o widoczność metod jest całkowicie nieistotnym pomysłem.

Odwrotnie

Z drugiej strony, wywoływanie metody prywatnej poza oryginalną klasą jest głównym problemem. Istnieją również ograniczenia wyśmiewania prywatnej metody w niektórych narzędziach wyśmiewania. (Np .: Mockito )

Chociaż istnieją narzędzia takie jak Power Mock, które to obsługują, jest to niebezpieczna operacja. Powodem jest zhakowanie JVM, aby to osiągnąć.

Jednym z obejść, które można zrobić, jest (jeśli chcesz napisać przypadki testowe dla metod prywatnych)

Zadeklaruj te prywatne metody jako chronione . Ale może to nie być wygodne w kilku sytuacjach.

Supun Wijerathne
źródło
0

Nie chodzi tylko o publiczne lub prywatne metody lub funkcje, ale o szczegóły implementacji. Funkcje prywatne to tylko jeden aspekt szczegółów implementacji.

Testowanie jednostkowe to w końcu podejście polegające na testowaniu białych skrzynek. Na przykład ten, kto korzysta z analizy pokrycia w celu zidentyfikowania części kodu, które do tej pory były zaniedbywane w testowaniu, zapozna się ze szczegółami implementacji.

A) Tak, powinieneś testować szczegóły implementacji:

Pomyśl o funkcji sortowania, która ze względu na wydajność używa prywatnej implementacji BubbleSort, jeśli jest do 10 elementów, oraz prywatnej implementacji innego podejścia sortowania (powiedzmy, heapsort), jeśli jest więcej niż 10 elementów. Publiczny interfejs API to funkcja sortowania. Jednak Twój zestaw testów lepiej wykorzystuje wiedzę, że w rzeczywistości są używane dwa algorytmy sortowania.

W tym przykładzie z pewnością można wykonać testy publicznego interfejsu API. Wymagałoby to jednak posiadania wielu przypadków testowych, które wykonują funkcję sortowania z więcej niż 10 elementami, tak aby algorytm heapsortowy był wystarczająco dobrze przetestowany. Samo istnienie takich przypadków testowych wskazuje, że zestaw testów jest powiązany ze szczegółami implementacji funkcji.

Jeśli szczegóły implementacji funkcji sortowania zmienią się, być może w ten sposób, że granica między dwoma algorytmami sortowania zostanie przesunięta lub że heapsort zostanie zastąpiony przez scalesort lub cokolwiek innego: istniejące testy będą nadal działać. Ich wartość jest jednak wątpliwa i prawdopodobnie trzeba je przerobić, aby lepiej przetestować zmienioną funkcję sortowania. Innymi słowy, konieczne będą prace konserwacyjne pomimo faktu, że testy odbywały się na publicznym interfejsie API.

B) Jak przetestować szczegóły implementacji

Jednym z powodów, dla których wiele osób twierdzi, że nie należy testować funkcji prywatnych ani szczegółów implementacji, jest to, że bardziej prawdopodobne jest, że szczegóły implementacji ulegną zmianie. To wyższe prawdopodobieństwo zmiany jest przynajmniej jednym z powodów ukrywania szczegółów implementacji za interfejsami.

Załóżmy teraz, że implementacja za interfejsem zawiera większe części prywatne, dla których indywidualne testy interfejsu wewnętrznego mogą być opcją. Niektórzy twierdzą, że te części nie powinny być testowane, gdy są prywatne, należy je zamienić w coś publicznego. Po upublicznieniu testowanie jednostkowe tego kodu byłoby w porządku.

Jest to interesujące: chociaż interfejs był wewnętrzny, prawdopodobnie mógł się zmienić, ponieważ był szczegółem implementacji. Biorąc ten sam interfejs, upubliczniając, dokonuje on pewnej magicznej transformacji, a mianowicie przekształca go w interfejs, który prawdopodobnie nie ulegnie zmianie. Oczywiście w tej argumentacji jest pewna wada.

Niemniej jednak kryje się za tym pewna prawda: podczas testowania szczegółów implementacji, w szczególności przy użyciu interfejsów wewnętrznych, należy dążyć do korzystania z interfejsów, które prawdopodobnie pozostaną stabilne. To, czy dany interfejs będzie prawdopodobnie stabilny, nie jest jednak rozstrzygalne na podstawie tego, czy jest on publiczny, czy prywatny. W projektach ze świata, nad którymi pracuję od jakiegoś czasu, interfejsy publiczne również dość często się zmieniają, a wiele prywatnych interfejsów pozostaje niezmienionych od wieków.

Nadal dobrą zasadą jest używanie „drzwi frontowych jako pierwszych” (patrz http://xunitpatterns.com/Principles%20of%20Test%20Automation.html ). Pamiętaj jednak, że nazywa się to „najpierw drzwiami wejściowymi”, a nie „tylko drzwiami wejściowymi”.

C) Podsumowanie

Przetestuj również szczegóły implementacji. Preferuj testowanie na stabilnych interfejsach (publicznych lub prywatnych). Jeśli szczegóły implementacji ulegną zmianie, należy również zweryfikować testy publicznego interfejsu API. Przekształcenie czegoś prywatnego w publiczne nie zmienia magicznie jego stabilności.

Dirk Herrmann
źródło
0

Tak, w miarę możliwości powinieneś przetestować prywatne metody. Dlaczego? Aby uniknąć niepotrzebnej eksplozji w przestrzeni stanów przypadków testowych, które ostatecznie ostatecznie niejawnie testują wielokrotnie te same funkcje prywatne na tych samych danych wejściowych. Wytłumaczmy dlaczego na przykładzie.

Rozważ następujący lekko wymyślony przykład. Załóżmy, że chcemy publicznie udostępnić funkcję, która przyjmuje 3 liczby całkowite i zwraca wartość true tylko wtedy, gdy te 3 liczby całkowite są liczbą pierwszą. Możemy to zaimplementować w następujący sposób:

public bool allPrime(int a, int b, int c)
{
  return andAll(isPrime(a), isPrime(b), isPrime(c))
}

private bool andAll(bool... boolArray)
{
  foreach (bool b in boolArray)
  {
    if(b == false) return false;
  }
  return true;
}

private bool isPrime(int x){
  //Implementation to go here. Sorry if you were expecting a prime sieve.
}

Teraz, gdybyśmy przyjęli ścisłe podejście, że tylko funkcje publiczne powinny być testowane, moglibyśmy tylko testować, allPrimea nie isPrimelub andAll.

Jako tester, możemy być zainteresowany w pięciu możliwości każdego argumentu: < 0, = 0, = 1, prime > 1, not prime > 1. Ale żeby być dokładnym, musielibyśmy także zobaczyć, jak każda kombinacja argumentów gra razem. To 5*5*5znaczy = 125 przypadków testowych, które musielibyśmy dokładnie przetestować tę funkcję, zgodnie z naszymi intuicjami.

Z drugiej strony, gdybyśmy mogli przetestować funkcje prywatne, moglibyśmy objąć tyle samo terenu przy mniejszej liczbie przypadków testowych. Potrzebowalibyśmy tylko 5 przypadków testowych, aby przetestować isPrimena tym samym poziomie, co nasza poprzednia intuicja. A zgodnie z hipotezą małego zakresu zaproponowaną przez Daniela Jacksona musielibyśmy przetestować andAllfunkcję tylko na małej długości, np. 3 lub 4. To byłoby co najwyżej 16 testów. Łącznie 21 testów. Zamiast 125. Oczywiście prawdopodobnie chcielibyśmy przeprowadzić kilka testów allPrime, ale nie czulibyśmy się zobowiązani do wyczerpującego omówienia wszystkich 125 kombinacji scenariuszy wejściowych, o których mówiliśmy, że nam zależy. Tylko kilka szczęśliwych ścieżek.

Z pewnością wymyślony przykład, ale był konieczny do jasnej demonstracji. A wzór rozciąga się na prawdziwe oprogramowanie. Funkcje prywatne są zwykle elementami składowymi najniższego poziomu, a zatem są często łączone razem w celu uzyskania logiki wyższego poziomu. Oznacza to, że na wyższych poziomach mamy więcej powtórzeń rzeczy z niższych poziomów ze względu na różne kombinacje.

Colm Bhandal
źródło
Po pierwsze, nie musisz testować takich kombinacji z czystymi funkcjami, jak pokazano. Połączenia isPrimesą naprawdę niezależne, więc testowanie każdej kombinacji na ślepo jest dość bezcelowe. Po drugie, oznaczenie czystej funkcji o nazwie isPrimeprivate narusza tak wiele reguł projektowania, że ​​nawet nie wiem od czego zacząć. isPrimepowinna bardzo wyraźnie być funkcją publiczną. Biorąc to pod uwagę, rozumiem, co mówisz, bez względu na ten bardzo słaby przykład. Jest to jednak oparte na założeniu, że chcesz przeprowadzać testy kombinacji, gdy w prawdziwych systemach oprogramowania rzadko jest to dobry pomysł.
Matt Messersmith,
Matt tak, przykład nie jest idealny, dam ci to. Ale zasada powinna być oczywista.
Colm Bhandal
Założeniem nie jest dokładnie to, że chcesz przeprowadzać testy kombinacji. Musisz to zrobić, jeśli ograniczysz się do testowania tylko publicznych elementów układanki. Są przypadki, w których chcesz ustawić funkcję czystą jako prywatną, aby stosować się do właściwych zasad enkapsulacji. I ta czysto prywatna funkcja może być używana przez funkcje publiczne. W kombinacyjny sposób, być może z innymi czysto prywatnymi funkcjami. W takim przypadku, zgodnie z dogmatem, że nie będziesz testować w trybie prywatnym, będziesz zmuszony do testowania kombinacji funkcji publicznej zamiast testowania modułowego komponentów prywatnych.
Colm Bhandal
0

Możesz także ustawić metodę pakietu na prywatną, tzn. Domyślną, i powinieneś być w stanie przetestować ją jednostkowo, chyba że jest wymagana prywatność.

Jogesh
źródło