Zakładając np. taką sytuację, że napisałeś jakąś tam część z tego projektu, po czym to dokładnie sprawdziłeś i stwierdzasz że funkcjonuje poprawnie, postanawiasz jeszcze napisać do tego testy automatyczne, gdzie na ich podstawie wyjdzie, że wszystko działa absolutnie prawidłowo. Co przez to zyskasz?
-
Jezeli rozwijasz jakis system, to zmiany w jednym miejscu moga miec wplyw na dzialanie systemu w innym miejscu. Dzieki testom masz wieksze szanse sie o tym dowiedziec zanim system trafi na produkcje.
-
Latwiej sie refaktoryzuje kod majac pewne pokrycie testami bo w momencie refaktoryzacji istnieje niemale ryzyko, ze cos zrobisz nie tak. Majac testy dowiesz sie, ze jakiegos przypadku nie uwzgledniles podczas dopieszczania kodu programu.
-
Testy pomagaja w zachowaniu wstecznej kompatybilnosci (o ile jest konieczna).
Nalezy zwrocic tez uwage na fakt, ze niektore rzeczy dosc ciezko przetestowac recznie. Przyklad: dana jest aplikacja, ktorej celem jest monitorowanie temperatury pewnego urzadzenia. Jezeli temperatura przekroczy wartosc X to oprogramowanie powinno wyslac powiadomienie z informacja o takim stanie rzeczy. Jak chcesz to przetestowac recznie skoro urzadzenie akurat nie przekracza temperatury X i moze nawet nigdy nie przeroczyc? W testach automatycznych jest to mozliwe poniewaz mozna zmienic sztucznie wartosc zwracana przez metode dokonujaca pomiar temperatury.
Co do tych testów i użytych tam metod typu AssertEquals, AssertTrue, AssertFalse czy inne tego typu, jaką wartość będzie miał taki test jak nie uwzględnisz tam jakiegoś ekstremalnego przypadku?
Jak sie o tym ekstremalnym przypadku dowiesz (zakladam, ze ten ekstremalny przypadek to blad a nie poprawnie dzialajacy kod z tym, ze nie przetestowany pod katem tego przypadku) to zrobisz sobie dodatkowy test, pokrywajacy ten przypadek. Nastepnie poprawisz kod systemu, nad ktorym pracujesz tak aby byl dostosowany do tego ekstremalnego przypadku. To rozwiazanie ma te zalete, ze prawdopodobienstwo, ze blad pojawi sie ponownie jest duzo nizsze.
W tym kontekście nie widzę znaczenia czy test był napisany na początku czy dopiero później.
Moim zdaniem pisanie testow w pierwszej kolejnosci moze pozytywnie wplynac na jakos kodu zrodlowego. W mojej opinii po prostu latwiej sie pisze testy do kodu o wyzszej jakosci, a to troche zniecheca do pojscia na skroty.
Mnie ciekawi czy TDD nie powoduje patologii w architekturze oprogramowania.
Nie wiem jakie zdanie maja inni programisci, ale mnie sie latwiej testuje cos co ma dobra architekture. Jezeli nie jestem ze swoja opinia odosobniony to powiedzialbym, ze TDD (wlasciwie to niekoniecznie TDD ale ogolnie mowiac testy) bardziej te patologie eliminuja niz ja tworza, poniewaz zamiast podejscia "stworze sobie troche gorsza architekture i bede mial szybciej funkcjonalnosc X" byc moze bedziesz mial podejscie "jak sobie teraz pojde na skroty to pozniej bede tracil niepotrzebnie czas na testy". No ale to opinia dosc subiektywna.
Co do tych warstw, gdzie wszystko musi ze soba dzialac: w testach jednostkowych chodzi o to, aby przetestowac fragment kodu w izolacji od reszty kodu (klas i metod zaleznych). Te wszystkie warstwy sobie zaslepiasz (np. stosujac mocki) i zmieniasz ich zachowanie.
Zalozmy, ze:
- Pracujesz nad systemem, panelem klienta gdzie kazdy uzytkownik wplaca pieniadze w ramach abonamentu za dana usluge. Jezeli suma wplat z calego okresu dzialalnosci przekroczy 10 000 000 PLN to masz wyslac wiadomosc e-mail do szefa, ze biznes sie kreci i dobrze byloby pomyslec o podwyzkach dla pracownikow :)
- Masz klase Mailer zawierajaca metode sendMail.
- Masz klase Payments, ktora posiada metode sumAll
- Masz klase Scheduler, ktora posiada cyklicznie wykonywana metode checkSumOfAllPayments.
Przykladowy scenariusz testowy metody checkSumOfAllPayments moglby wygladac tak:
- Tworzysz mocka klasy Mailer tak aby metoda sendMail nic nie robila.
- Tworzysz mocka klasy Payments i zmieniasz zachowanie metody sumAll tak aby bez zadnego odwolywania sie do bazy danych po prostu zwrocila wartosc 10 000 001 (dzialasz na obiekcie, ktory udaje, ze jest implementacja klasy Payments, chociaz w rzeczywistosci nie jest).
- W klasie Scheduler pozbywasz sie zaleznosci. Jezeli masz pola typu Mailer i Payments to w ich miejsca wstawiasz mocki, tak aby klasa Scheduler nie odwolywala sie do rzeczywistych implementacji powyzszych klas.
- Sprawdzasz czy metoda sendMail zostala wywolana z odpowiednimi parametrami (odpowiedni adres e-mail, temat i tresc).
Innymi slowy: zaleznosci, w tych roznych warstwach Cie kompletnie nie interesuja. No chyba, ze mowa o testach integracyjnych to co innego.
Dodam, ze do mockowania i weryfikacji wywolan sa odpowiednie frameworki.