Testowanie prywatnych metod

[quote=Tubis]@drogus A czasem:

obj.send(:some_private_method)

Nie przestał działać z ruby1.9?[/quote]
Nie - z tego co pamiętam to dopiero w 2.0 ma być to możliwe tylko poprzez send.

Powiedzmy, że mam publiczną metodę, która woła inną prywatną. Fajny przejrzysty kod, metoda prywatna przetestowana. Nagle stwierdzam, że jednak zamiast jednej prywatnej metody wole ją rozbić na dwie mniejsze, jeszcze bardziej przejrzyste. Nadaję nazwy odpowiednie do ich zmienionej zawartości. Kod mam już na maksa czytelny, że bardziej się dosłownie nie da. Aaaale co się dzieje z testem? Failuje (nie znaleziona metoda)!!! …bo napisałem lepszy kod… :frowning:
Nie powinno to mieć wpływu na test tak samo jak zmiana nazwy jakiejkolwiek zmiennej w kodzie.

Metody prywatne podlegają zmianom, ale z tego względu, że są wołane z publicznych to tak na prawdę one mają bugi i to właśnie je należy testować. Oprócz tego, że poprawiają czytelność to cały czas ich kod jest częścią metody publicznej. Z punktu widzenia jakiegokolwiek klienta klasy (test też jest klientem) nie powinno mieć znaczenia to czy ich użyłem czy nie. A jeżeli okazuje się, że nawet sama zmiana nazwy prywatnej metody powoduje failujące testy to chyba coś tu śmierdzi “sprytnymi rozwiązaniami”.

Ruby dosyć mocno trzyma się zasad ukrywania informacji tak, że nawet w klasie, która ma pole tego samego typu co ona sama, nie można zawołać na nim prywatnej metody tej klasy. Dlaczego więc pozwalać na to w teście, który jest już całkiem obcy?

Powiedzmy, że mam publiczną metodę, która woła inną prywatną. Fajny przejrzysty kod, metoda prywatna przetestowana. Nagle stwierdzam, że jednak zamiast jednej prywatnej metody wole ją rozbić na dwie mniejsze, jeszcze bardziej przejrzyste. Nadaję nazwy odpowiednie do ich zmienionej zawartości. Kod mam już na maksa czytelny, że bardziej się dosłownie nie da. Aaaale co się dzieje z testem? Failuje (nie znaleziona metoda)!!! …bo napisałem lepszy kod… :([/quote]
tę logikę równie dobrze można zastosować do metody publicznej. poprawiłem nazwę (np. żeby była bardziej czytelna) - obiekt dalej robi to co robił (tj. działa poprawnie), ale co się dzieje z testem? Failure (nie znaleziona metoda)!!! …bo napisałem lepszy kod.
To jest popadanie ze skrajności w skrajność, obydwa krańce prowadzą do absurdu.
Łatwiej jest napisać test do małej metody. Małe metody są bardziej czytelne. Bardziej czytelny i przetestowany kod to kod lepszej jakości, a o to chyba chodzi, tak?

Poza tym jak zmieniasz klasę to nie udawaj że testy nie istnieją, jeżeli nie zaktualizowałeś testu aktualizując kod to Twoja wina i bardzo dobrze że będzie Fail.

Nie można tej logiki stosować do metod publicznych!!
Zmieniasz publiczny interfejs (nazwa, parametry, funkcjonalność) -> zmieniasz sposób w jaki korzysta się z metody. Jeżeli gdziekolwiek korzystasz z tej klasy -> zmieniasz wywołania. Testy korzystają z publicznej metody -> trzeba je zmienić razem z metodą, która się zmieniła. To normalne.
Z metod prywatnych nie korzysta nikt oprócz metody publicznej, która ją woła, co za tym idzie, nie trzeba zmieniać żadnych miejsc w kodzie gdzie wołasz tą metodę publiczną. Sytuacja jest całkiem inna.

[quote]To jest popadanie ze skrajności w skrajność, obydwa krańce prowadzą do absurdu.
Łatwiej jest napisać test do małej metody. Małe metody są bardziej czytelne. Bardziej czytelny i przetestowany kod to kod lepszej jakości, a o to chyba chodzi, tak?[/quote]
Trochę mi to przypomina “ten kod ma 100% line coverage - nie ma bata, żeby miał jakieś bugi”. Test, który jest wrażliwy na banalne refactoringi jak dla mnie jest daleki od ideału nawet jeżeli jest prosty i krótki. W sumie to zaczęło mnie teraz zastanawiać co tak właściwie sprawdza u Was test metody publicznej jeżeli osobno testujecie metodę prywatną? Piszecie asercje dla całości czy olewacie w nim tą część, która jest prywatna, a może w ogóle cały test? :wink:

Jak zmieniam prywatne rzeczy to testy tylko odpalam, żeby sprawdzić czy nic przez pomyłkę nie spieprzyłem. Nie aktualizuję wcześniej testów bo do metod prywatnych ich zwyczajnie nie mam. To właśnie problem po Twojej stronie, że musisz przy takich -zmianach- aktualizować testy.

Cały ten wątek nie ma najmniejszego sensu. Pytanie o to, czy testować metody prywatne czy nie jest z gruntu źle postawione. Testujesz funkcjonalność a nie metody. Może się zdarzyć że ta funkcjonaloność będzie w metodach publicznych (zazwyczaj), ale też może się zdarzyć sytuacja w której trzeba przetestować funkcjonalność metody prywatnej. Zazwyczaj prościej jest tą metodę prywatną wywołać wówczas pośrednio, nieczęsto zdarza mi się w teście wykonać obiekt.send(:prywatna_metoda), bo jest to podejrzany kod od początku.

Podam swój przykład:

metoda publiczna: podaj wyniki wyszukiwania
metoda prywatna 1: znajdź wyniki w lokalnej bazie ( jeżeli “świeże” -> zwróć)
metoda prywatna 2: znajdź w google ( jeżeli jest wynik -> zwróć)
metoda prywatna 3: znajdź w yahoo ( jeżeli jest wynik -> zwróć)
metoda prywatna 4: zapisz wynik w lokalnej bazie

Testuję metodę publiczną pod kątem zwrócenia stosownej odpowiedzi - prosty test.
Testuję metody prywatne pod kątem tego za co odpowiadają - też proste testy.

[quote=rui]metoda publiczna: podaj wyniki wyszukiwania
metoda prywatna 1: znajdź wyniki w lokalnej bazie ( jeżeli “świeże” -> zwróć)
metoda prywatna 2: znajdź w google ( jeżeli jest wynik -> zwróć)
metoda prywatna 3: znajdź w yahoo ( jeżeli jest wynik -> zwróć)
metoda prywatna 4: zapisz wynik w lokalnej bazie

Testuję metodę publiczną pod kątem zwrócenia stosownej odpowiedzi - prosty test.
Testuję metody prywatne pod kątem tego za co odpowiadają - też proste testy.[/quote]
To mi akurat wygląda na dobry przykład, żeby rozpakować do oddzielnej klasy (Search czy coś takiego).

[quote=rui]metoda publiczna: podaj wyniki wyszukiwania
metoda prywatna 1: znajdź wyniki w lokalnej bazie ( jeżeli “świeże” -> zwróć)
metoda prywatna 2: znajdź w google ( jeżeli jest wynik -> zwróć)
metoda prywatna 3: znajdź w yahoo ( jeżeli jest wynik -> zwróć)
metoda prywatna 4: zapisz wynik w lokalnej bazie[/quote]
Co tu dużo mówić, jaki kod takie testy :wink: drogus dobrze mówi :slight_smile: