Doszedłem do tego etapu w moim życiu, w którym postanowiłem zaczac pisać testy jednostkowe. Zacząłem o tym czytać, pojawiło się pojęcie TDD i tak się zastanawiam, czy w realnym projekcie jest czas na właśnie takie podejście?
To jest podejście, które umiejętnie użyte sprawia, że czas się oszczędza. Umiejętnie użyte, czyli tam, gdzie jest sens pisać testy jednostkowe i przy odpowiednim zdefiniowaniu czym jest jednostka.
Niestety testy w firmach są często robione od tyłka strony, czyli:
- Najpierw projektowane są wszystkie klasy mające rozwiązać dany problem.
- Każda klasa musi mieć interfejs, żeby dało się ją mockować w testach. (W efekcie mamy nadmiar zbędnych interfejsów.)
- Uruchomienie tego kodu, testowanie manualne, wprowadzanie poprawek. (Czas, czas, czas, więcej czasu - oczywiście straconego.)
- Na koniec dopisanie testów jednostkowych do każdej klasy. (W efekcie mamy testy klas, nie jednostek.)
- Potem okazuje się, że zapomnieliśmy o jakimiś przypadku, którego zaimplementowanie wymaga sporej refaktoryzacji, więc trzeba połowę klas wywalić, za to dodać zupełnie inne. Razem ze starymi klasami wyrzucamy ich interfejsy, testy do nich i mocki, czyli tracimy relatywnie dużo czasu i pracy. Za to musimy zrobić nowe klasy, interfejsy, mocki i testy.
To jest strata czasu, dlatego ludzie, którzy popracowali w taki sposób uważają potem, że pisanie testów to strata czasu.
To tak jak z jazdą samochodem. Jeśli nie umiesz go prowadzić ani uruchomić i pchasz z rodzina nad morze, to zajmie Ci to znacznie więcej czasu niż pójście nawet na piechotę. Ale jak umiesz prowadzić, to osiągniesz cel znacznie szybciej.
Przy podejściu TDD:
- Definiujesz sobie jednostkę, czyli na początku klasę z metodą, która przyjmie jakąś strukturę danych wejściowych i zwróci jakąś strukturę danych wyjściowych.
- Potem piszesz przypadki testowe. Wszystkie - dla typowych poprawnych danych, dla przypadków brzegowych (dzielenie przez 0), dla danych błędnych, itd.
- Uruchamiasz testy, muszą być czerwone. Jeśli są zielone, to zepsułeś.
- Implementujesz kod produkcyjny, na początku nawet w tej jednej metodzie. Testy robią się zielone.
- Jak przechodzi testy, to refektoryzujesz, czyli wydzielasz metody prywatne, przenosisz część kodu do oddzielnych klas. (Ogólnie działasz zgodnie z SOLID, wzorcami projektowymi, itd.)
Zysk jest taki:
- Twój kod jest testowalny - z założenia, bo testy napisałeś na początku.
- Nie straciłeś czasu na pisanie zbędnego kodu, bo napisałeś tylko tyle kodu, ile potrzeba do przejścia testów.
- Nie masz śmieciowych interfejsów i mocków, czyli długu technologicznego do utrzymywania.
- Nie tracisz tyle czasu na testowanie manualne z debugerem.
To jest ogromna oszczędność czasu, tylko trzeba od początku myśleć w odpowiedni sposób.
Jeżeli chodzi o same testy, nie wiem czy dobrze to rozumiem, ale wydaje mi się że tych testów to w sumie wyjdzie więcej niż kodu aplikacji.
Więcej niż kodu testowanego. Czy więcej niż kodu aplikacji, to zależy od aplikacji.
Jest wiele rzeczy, których nie ma sensu testować jednostkowo, bo taki test nie wniesie żadnej wartości, bo będzie zbyt oderwany od rzeczywiście uruchomionej aplikacji.
Mam metodę która tworzy usera w DB. Z tego co rozumiem, to test nie powinien się łączyć z bazą, tylko powinna tam być zaślepka tak?
Taki test raczej w ogóle nie ma sensu. Co Ci da, że użyjesz zaślepki? To wcale nie uchroni Cię przed tym, że w bazie nie będzie jakiejś kolumny i dane się nie zapiszą. Albo przed tym, że użytkownik z danym emailem już istnieje.
W takim teście możesz jedynie sprawdzić, czy poprawnie wywołujesz swój mock. Żadnej wartości dla jakości aplikacji.
Czy w TDD powinno sie testować wszystkie metody czy tylko te podatne na bug ? I czy powinno sie tesotowac metody prywatne?
Jeśli metoda nie jest podatna na błędy, to znaczy, że nie jest nigdzie używana, więc należy ją usunąć.
A w testach nie należy testować żadnych metod ani publicznych ani prywatnych, tylko jednostki. To, że w tym celu trzeba wywołać jakąś metodę wynika z tego, że w wielu językach programowania w ogóle kod umieszcza się w metodach, ale to jest efekt języka, a nie cel sam w sobie. Jednostką może być zarówno jedna metoda, jak i wiele metod w wielu klasach.