Czasami prywatne funkcje modułu lub klasy są po prostu jeszcze do wyodrębnienia wewnętrznymi jednostkami funkcjonalności, które mogą zasługiwać na własne testy. Dlaczego więc ich nie przetestować? My będziemy pisać testy do nich później, jeśli / kiedy są one wyodrębnione. Dlaczego więc nie napisać testów teraz, gdy są one nadal częścią tego samego pliku?
Aby zademonstrować:
Najpierw napisałem module_a
. Teraz chcę na to napisać testy. Chciałbym przetestować funkcję „prywatną” _private_func
. Nie rozumiem, dlaczego nie napisałbym dla niego testu, jeśli później mógłbym zrefaktoryzować go do własnego modułu wewnętrznego, a następnie napisać dla niego testy.
Załóżmy, że mam moduł z następującymi funkcjami (może to być również klasa):
def public_func(a):
b = _do_stuff(a)
return _do_more_stuff(b)
_do_stuff
i _do_more_stuff
są „prywatnymi” funkcjami modułu.
Rozumiem pomysł, że powinniśmy testować tylko interfejs publiczny, a nie szczegóły implementacji. Oto jednak:
_do_stuff
i _do_more_stuff
zawierają większość funkcjonalności modułu. Każdy z nich może być funkcją publiczną innego, „wewnętrznego” modułu. Ale nie są one jeszcze rozwinięte i wystarczająco duże, aby można je było wyodrębnić do osobnych plików.
Testowanie tych funkcji wydaje się słuszne, ponieważ są ważnymi jednostkami funkcjonalności. Gdyby były one w różnych modułach jako funkcje publiczne, przetestowalibyśmy je. Dlaczego więc nie przetestować ich, gdy nie są jeszcze (lub nigdy) wyodrębnione do innego pliku?
Odpowiedzi:
Potrzeba testowania nie jest tym samym, co potrzeba bycia publicznym.
Nietrywialny kod wymaga testowania niezależnie od narażenia. Zachowanie niepubliczne nie musi istnieć, nie mówiąc już o testowaniu.
Te sprzeczne widoki mogą prowadzić do upublicznienia każdej funkcji lub odmowy przypisania kodu do funkcji, chyba że będzie ona publiczna.
To nie jest odpowiedź. Bądź gotów tworzyć prywatne funkcje pomocnicze. Przetestuj je za pomocą publicznego interfejsu, który wykorzystuje go w jak największym stopniu.
Jeśli testowanie przez interfejs publiczny nie wykonuje dostatecznie funkcji prywatnej, funkcja prywatna próbuje na to pozwolić.
Sprawdzanie poprawności może pomóc zawęzić zakres funkcji prywatnych. Jeśli nie możesz przejść wartości null przez interfejs publiczny, nadal możesz zgłosić wyjątek, jeśli taki zostanie.
Dlaczego powinieneś? Po co testować to, czego nigdy nie zobaczysz? Ponieważ rzeczy się zmieniają. Może być teraz prywatny, ale później publiczny. Kod wywołujący może ulec zmianie. Kod, który wyraźnie odrzuca wartość null, zapewnia prawidłowe użycie i oczekiwany stan.
Oczywiście zero może być w porządku. To tylko przykład tutaj. Ale jeśli czegoś oczekujesz, warto wyjaśnić to oczekiwanie.
To może nie był rodzaj testu, o którym myślałeś, ale mam nadzieję, że będziesz gotowy stworzyć prywatne funkcje pomocnicze, gdy będzie to właściwe.
Chęć testowania jest dobra, ale nie powinna być siłą napędową w projektowaniu publicznego interfejsu API. Zaprojektuj publiczny interfejs API, aby był łatwy w użyciu. Prawdopodobnie nie będzie, jeśli każda funkcja jest publiczna. Interfejs API powinien być czymś, co ludzie mogą zrozumieć, jak korzystać bez zanurzania się w kodzie. Nie zostawiaj takich ludzi zastanawiających się, do czego służy ta dziwna funkcja pomocnika.
Ukrywanie funkcji pomocnika publicznego w module wewnętrznym jest próbą respektowania potrzeby czystego interfejsu API podczas wystawiania pomocników do testowania. Nie powiem, że to źle. Być może robisz pierwszy krok w kierunku innej warstwy architektonicznej. Ale proszę, opanuj sztukę testowania funkcji prywatnych pomocników za pomocą funkcji publicznych, które ich używają jako pierwsze. W ten sposób nie będziesz nadmiernie używać tego obejścia.
źródło
Krótka odpowiedź: nie
Dłuższa odpowiedź: tak, ale za pośrednictwem publicznego „API” twojej klasy
Cała idea prywatnych członków klasy polega na tym, że reprezentują one funkcjonalność, która powinna być niewidoczna poza „jednostką” kodu, bez względu na to, jak duża ma być ta jednostka. W kodzie obiektowym jednostka często kończy się klasą.
Powinieneś tak zaprojektować swoją klasę, aby możliwe było wywołanie całej prywatnej funkcjonalności poprzez różne kombinacje stanu wejściowego. Jeśli okaże się, że nie ma stosunkowo prostego sposobu, aby to zrobić, prawdopodobnie jest to wskazówka, że twój projekt wymaga bliższej uwagi.
Po wyjaśnieniu pytania jest to tylko kwestia semantyki. Jeśli dany kod może działać jako oddzielna samodzielna jednostka i jest testowany tak, jakby był kodem publicznym, nie widzę żadnej korzyści z przeniesienia go do samodzielnego modułu. Obecnie służy to jedynie wprowadzeniu w błąd przyszłych programistów (w tym ciebie, za 6 miesięcy), co do tego, dlaczego pozornie publiczny kod jest ukryty w innym module.
źródło
Chodzi o to, że funkcje prywatne są ukrytymi szczegółami implementacji, które można dowolnie zmieniać bez zmiany publicznego interfejsu API. Twój przykładowy kod:
Jeśli masz serię testów, które tylko używają
public_func
, to jeśli przepisujesz to na:wtedy, dopóki wynik zwrotu dla określonej wartości
a
pozostanie taki sam, wszystkie testy będą dobre. Jeśli wynik zwrotu ulegnie zmianie, test zakończy się niepowodzeniem, co podkreśla fakt, że coś się zepsuło.To wszystko dobrze: statyczny publiczny interfejs API; dobrze zamknięte wewnętrzne funkcjonowanie; i solidne testy.
Jednakże, jeśli napisałeś testy dla
_do_stuff
lub_do_more_stuff
następnie dokonałeś powyższej zmiany, miałbyś teraz kilka zepsutych testów, nie dlatego, że zmieniono funkcjonalność, ale dlatego, że zmieniła się implementacja tej funkcjonalności. Testy te wymagałyby przepisania, aby pracować z nowymi funkcjami, ale po ich uruchomieniu można jedynie wiedzieć, że testy te działały z nowymi funkcjami. Utracilibyście oryginalne testy, więc nie wiedzielibyście, czy zachowaniepublic_func
się zmieniło, więc testy byłyby w zasadzie bezużyteczne.To zła rzecz: zmieniający się interfejs API; odsłonięte elementy wewnętrzne ściśle połączone z testami; i kruche testy, które zmieniają się, gdy tylko wprowadzane są zmiany w implementacji.
Więc nie, nie testuj funkcji prywatnych. Zawsze.
źródło