Dlaczego prywatne testy jednostkowe są uważane za złą praktykę?

18

Kontekst:

Obecnie pracuję nad małym projektem w Pythonie. Zazwyczaj tworzę swoje klasy za pomocą kilku udokumentowanych metod publicznych, ale głównie zajmuję się koncepcjami wysokiego poziomu (co użytkownik klasy powinien wiedzieć i stosować) oraz szeregiem ukrytych (zaczynających się od podkreślenia) metod, które są odpowiedzialne za przetwarzanie złożone lub niskiego poziomu.

Wiem, że testy są niezbędne, aby zapewnić zaufanie do kodu i upewnić się, że żadna późniejsza modyfikacja nie złamała poprzedniego zachowania.

Problem:

Aby zbudować publiczne metody wyższego poziomu na zaufanej bazie, ogólnie testuję metody prywatne. Łatwiej jest mi ustalić, czy modyfikacja kodu wprowadziła regresje i gdzie. Oznacza to, że te testy wewnętrzne mogą naruszyć niewielkie zmiany i będą musiały zostać naprawione / wymienione

Ale wiem też, że prywatna metoda testowania jednostkowego jest co najmniej sporną koncepcją lub częściej uważaną za złą praktykę. Powodem jest to, że należy przetestować tylko zachowanie publiczne ( zob. )

Pytanie:

Dbam o przestrzeganie najlepszych praktyk i chciałbym zrozumieć:

  • dlaczego stosowanie testów jednostkowych metod prywatnych / ukrytych jest złe (jakie jest ryzyko)?
  • jakie są najlepsze praktyki, gdy metody publiczne mogą korzystać z niskiego poziomu i / lub złożonego przetwarzania?

Precyzja:

  • Nie jest to jak pytanie. Python nie ma prawdziwej koncepcji prywatności, a ukrytych metod po prostu nie ma na liście, ale można ich użyć, gdy znasz ich nazwę
  • Nigdy nie uczono mnie zasad i schematów programowania: moje ostatnie zajęcia pochodzą z lat 80-tych ... Nauczyłem się języków głównie metodą prób i niepowodzeń oraz referencji w Internecie (od wielu lat mój ulubiony jest Stack Exchange)
Serge Ballesta
źródło
2
Możliwy duplikat testowania chronionych metod prywatnych
Greg Burghardt,
2
OP, gdzie słyszałeś lub czytałeś, że testowanie prywatnych metod zostało uznane za „złe”? Istnieją różne sposoby testu jednostkowego. Zobacz Testowanie jednostkowe, testowanie czarnej skrzynki i testowanie białej skrzynki .
John Wu,
@JohnWu: Znam różnicę między testami White-box i Black-box. Ale nawet w testach White-box wygląda na to, że potrzeba przetestowania metod prywatnych jest wskazówką dla problemu projektowego. Moje pytanie jest próbą zrozumienia, jakie są najlepsze ścieżki, kiedy tam upadam ...
Serge Ballesta,
2
Znowu, gdzie słyszałeś lub czytałeś, że nawet w testach White-box potrzeba testowania metod prywatnych jest wskazówką dla problemu projektowego? Chciałbym zrozumieć uzasadnienie tego przekonania przed próbą odpowiedzi.
John Wu
@SergeBallesta, innymi słowy, umieść odniesienia do artykułów, które sprawiły, że uwierzyłeś, że testowanie prywatnych metod jest złą praktyką. Następnie wyjaśnij nam, dlaczego im uwierzyłeś.
Laiv

Odpowiedzi:

18

Kilka powodów:

  1. Zwykle, gdy masz ochotę przetestować prywatną metodę klasy, jest to zapach projektowy (klasa lodowa, niewystarczająca ilość elementów publicznych wielokrotnego użytku itp.). W grze prawie zawsze występuje jakiś „większy” problem.

  2. Możesz je przetestować za pomocą interfejsu publicznego (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 podobne), 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 ze względu na zestaw testowy ... co jest największą ironią, ponieważ zestaw testowy jest po to, aby pomóc ci bezpiecznie przeprojektować i refaktoryzować!

Wskazówka, jeśli nadal masz ochotę przetestować części prywatne (nie używaj jej, jeśli Ci to przeszkadza, i YMMV, ale w przeszłości działało to dobrze dla mnie): czasami pisanie testów jednostkowych dla funkcji prywatnych, aby się upewnić działają dokładnie tak , jak myślisz, że mogą być cenne (szczególnie jeśli nie znasz języka). Jednak po upewnieniu się, że działają, usuń testy i zawsze upewnij się, że publiczne testy są solidne i zostaną wyłapane, jeśli ktoś zrobi rażącą zmianę wspomnianej funkcji prywatnej.

Kiedy testować prywatne metody: Ponieważ ta odpowiedź stała się (nieco) popularna, czuję się zobowiązany do wspomnienia, że ​​„najlepsza praktyka” jest zawsze taka: „najlepsza praktyka”. Nie oznacza to, że powinieneś robić to dogmatycznie lub na ślepo. Jeśli uważasz, że powinieneś przetestować swoje prywatne metody i mieć uzasadniony powód (np. Piszesz testy charakteryzujące starszą aplikację), to przetestuj swoje prywatne metody . Szczególne okoliczności zawsze przeważają nad jakąkolwiek ogólną zasadą lub najlepszą praktyką. Pamiętaj tylko o niektórych rzeczach, które mogą pójść nie tak (patrz wyżej).

Mam odpowiedź, która szczegółowo omawia to na SO, której nie powtórzę tutaj: /programming/105007/should-i-test-private-methods-or-only-public-ones/ 47401015 # 47401015

Matt Messersmith
źródło
4
Powód 1: Mglisty. Powód 2: Co jeśli twoja metoda prywatnego pomocnika nie powinna być częścią publicznego API? Powód 3: Nie, jeśli właściwie zaprojektujesz swoją klasę. Twoja ostatnia wskazówka: dlaczego miałbym usuwać doskonale dobry test, który dowodzi, że metoda, którą napisałem, działa?
Robert Harvey,
4
@RobertHarvey Powód 2: będąc pośrednio dostępnym za pośrednictwem publicznego interfejsu API! = Będąc częścią publicznego interfejsu API. Jeśli Twoja prywatna funkcja nie jest testowalna za pośrednictwem publicznego interfejsu API, być może jest to martwy kod i powinna zostać usunięta? Albo twoja klasa jest rzeczywiście górą lodową (powód 1) i powinna zostać zrefaktoryzowana.
Frax,
5
@RobertHarvey, jeśli nie możesz przetestować funkcji prywatnej za pośrednictwem publicznego interfejsu API, usuń ją, ponieważ nie służy ona żadnemu przydatnemu celowi.
David Arno
1
@RobertHarvey 1: Zapachy projektowe są zawsze nieco subiektywne / mgliste, więc pewne. Ale wymieniłem kilka konkretnych przykładów anty-wzorów, a moja odpowiedź SO zawiera więcej szczegółów. 2: Prywatne metody nie mogą być częścią publicznego API (z definicji: są prywatne) ... więc nie sądzę, żeby twoje pytanie miało sens. Próbuję dojść do tego, że jeśli masz coś takiego bin_search(arr, item)(publiczny) i bin_search(arr, item, low, hi)(prywatny, istnieje wiele sposobów wyszukiwania bin), to wszystko, czego potrzebujesz do przetestowania, to publiczne ( bin_search(arr, item))
Matt Messersmith
1
@RobertHarvey 3: Po pierwsze powiedziałem ryzyko , a nie gwarancja . Po drugie, twierdzenie, że „działa, jeśli robisz to poprawnie” jest samospełniające się. Na przykład: „Możesz napisać system operacyjny w jednej funkcji, jeśli zrobisz to poprawnie ”. To nie jest fałsz: ale też nie jest użyteczne. O wskazówce: usunąłbyś go, ponieważ jeśli twoja implementacja się zmieni (tzn. Chcesz zamienić prywatny impl), wtedy twój zestaw testów wejdzie ci w drogę (będziesz miał nieudany test, w którym nie powinieneś).
Matt Messersmith,
14

Biorąc pod uwagę, że jednym z głównych celów testów jednostkowych jest to, że można przefakturować elementy wewnętrzne programu, a następnie być w stanie zweryfikować, czy nie złamano jego funkcjonalności, jest to odwrotne od zamierzonego, jeśli testy jednostkowe działają na tak drobnym poziomie szczegółowości, że każda zmiana kodu programu wymaga przepisania testów.

Pete
źródło
Nie jestem pewien, dlaczego Twoja odpowiedź została odrzucona. Jest krótki, do rzeczy i otrzymuje odpowiedź w 100% poprawną.
David Arno,
3
@DavidArno: Może dlatego, że testowanie prywatnych metod nie ma tak naprawdę wiele wspólnego z dokładnością testu. Ma wszystko, co dotyczy sprzężenia ze szczegółami implementacji.
Robert Harvey,
11

Pisanie testów jednostkowych dla metod prywatnych wiąże testy jednostkowe ze szczegółami implementacji.

Testy jednostkowe powinny przetestować zachowanie klasy na zewnętrznej powierzchni klasy (jest to publiczny interfejs API). Testy jednostkowe nie powinny wiedzieć nic o wnętrznościach klasy. Pisanie testów jednostkowych na podstawie szczegółów implementacyjnych klasy wiąże ręce w przypadku refaktoryzacji. Refaktoryzacja prawie na pewno przerwie te testy, ponieważ nie są one częścią stabilnego interfejsu API.

To powiedziawszy dlaczego może chcesz pisać testy jednostkowe dla metod prywatnych?

Istnieje naturalne napięcie między testami jednostkowymi a przyrostowym rozwojem. Twórcy oprogramowania korzystający z REPL (pętla odczytu-ewaluacji-wydruku) mogą zaświadczyć, jak produktywne może być szybkie pisanie i testowanie niewielkich fragmentów funkcjonalności podczas „rozwijania” klasy lub funkcji. Jedynym dobrym sposobem na zrobienie tego w środowiskach, w których przeprowadzane są testy jednostkowe, jest napisanie testów jednostkowych dla metod prywatnych, ale jest to bardzo trudne. Testy jednostkowe wymagają czasu, aby napisać, potrzebujesz rzeczywistej metody testowania, a Twoja platforma testowa musi obsługiwać możliwość zachowania metody prywatnej, aby nie zanieczyszczała twojego zewnętrznego API.

Niektóre ekosystemy, takie jak C # i .NET, mają sposoby na tworzenie środowisk podobnych do REPL (narzędzia takie jak Linqpad to robią), ale ich użyteczność jest ograniczona, ponieważ nie masz dostępu do swojego projektu. Bezpośrednie okno w Visual Studio jest niewygodne; wciąż nie ma pełnej Intellisense, musisz używać w pełni kwalifikowanych nazw i uruchamia kompilację za każdym razem, gdy go używasz.

Robert Harvey
źródło
4
@ user949300 Dyskusja na ten temat nie popadnie w prawdziwy błąd Szkota , ale jest wiele źle napisanych testów wszelkiego rodzaju. Z perspektywy testów jednostkowych powinieneś przetestować kontrakt publiczny swojej metody bez znajomości wewnętrznych szczegółów implementacji. Nie zawsze twierdzenie, że określona zależność została nazwana X razy, jest zawsze błędne: są sytuacje, w których ma to sens. Musisz tylko upewnić się, że jest to informacja, którą faktycznie chcesz przekazać w umowie testowanego urządzenia.
Vincent Savard,
3
@DavidArno: [wzrusza ramionami] Robię to już od jakiegoś czasu. Testy jednostkowe dla metod prywatnych zawsze działały dla mnie dobrze, dopóki Microsoft nie przestał obsługiwać obiektów proxy w swoich ramach testowych. W rezultacie nic nigdy nie wybuchło. Nigdy nie rozerwałem dziury we wszechświecie, pisząc test na prywatną metodę.
Robert Harvey,
2
@DavidArno: Dlaczego miałbym rezygnować z korzystania z doskonale dobrej techniki, która zapewnia mi korzyści, tylko dlatego, że ktoś w Internecie twierdzi, że jest to zły pomysł bez podania uzasadnienia?
Robert Harvey,
2
Główną korzyścią, jaką czerpię z testów jednostkowych, jest zapewnienie mi „siatki bezpieczeństwa”, która pozwala mi majstrować przy swoim kodzie i mieć pewność, że moje zmiany nie wprowadzają regresji. W tym celu testowanie metod prywatnego pomocnika ułatwia znalezienie takich regresji. Kiedy refaktoryzuję metodę prywatnego pomocnika i wprowadzam błąd logiczny, przerywam testy specyficzne dla tej prywatnej metody. Gdyby moje testy jednostkowe były bardziej ogólne i testowały tylko interfejs tej jednostki kodu, problem byłby znacznie bardziej niejasny.
Alexander - Przywróć Monikę
2
@Frax Pewnie, że tak, ale zgodnie z tą logiką powinienem zrezygnować z testów jednostkowych na rzecz testów integracyjnych w całym systemie. W końcu „w większości przypadków powinieneś być w stanie zmodyfikować te testy, aby przetestować to samo zachowanie”
Alexander - Przywróć Monikę
6

Z mojego doświadczenia wynika, że ​​jednostka testująca klasy wewnętrzne, metody zwykle oznaczają, że muszę wyjąć przetestowane funkcje, klasy. Aby stworzyć kolejny poziom abstrakcji.

Prowadzi to do lepszego przestrzegania zasady jednolitej odpowiedzialności.

Robert Andrzejuk
źródło
To powinna być zaakceptowana odpowiedź.
Jack Aidley
0

Myślę, że to dobre pytanie, ponieważ ujawnia typowy problem podczas testowania zasięgu. Ale dobra odpowiedź powinna powiedzieć, że pytanie nie jest do końca prawda, ponieważ w teorii, powinno nie być w stanie przetestować jednostkę prywatnych metod. Dlatego są prywatne .

Może lepszym pytaniem byłoby „Co powinienem zrobić, gdy chcę przetestować metody prywatne?”, a odpowiedź jest w pewnym stopniu oczywista: należy je udostępnić w sposób umożliwiający testowanie. To niekoniecznie oznacza, że ​​powinieneś upublicznić metodę i to wszystko. Najprawdopodobniej będziesz chciał zrobić wyższą abstrakcję; przenieś do innej biblioteki lub interfejsu API, abyś mógł przeprowadzić testy na tej bibliotece, bez ujawniania tej funkcji w głównym interfejsie API.

Pamiętaj, że istnieje powód, dla którego twoje metody mają różne poziomy dostępności i zawsze powinieneś pomyśleć o tym, w jaki sposób Twoje klasy będą używane na końcu.

Juan Carlos Eduardo Romaina Ac
źródło
Próbowałem rozwiązać ten problem w swoim pytaniu, mówiąc, że Python nie ma prawdziwej koncepcji prywatności, a ukrytych metod po prostu nie ma na liście, ale można ich użyć, gdy znasz ich nazwę
Serge Ballesta