Chciałbym popełnić kilka artykułów na temat Agile, ale zanim to zrobię, chcę wyjaśnić na czym polega Test Driven Development (TDD), które jest ściśle powiązane z tą metodologią.

Test Driven Development – ogólny przegląd

Programowanie oparte na testach (bo tak to można przetłumaczyć) różni się od tradycyjnego, że testy pisane są przed kodem. Ciężko to zrozumieć zanim się tego nie zrobi, a jeszcze ciężej zrozumieć, że ta metoda na prawdę działa.
Otóż zanim weźmiemy się za programowanie jakieś funkcjonalności najpierw piszemy do niej test. Gdy go uruchomimy – wiadomo – oznaczony zostanie kolorem czerwonym jako błędny.
Drugim krokiem jest napisanie „tak mało kodu jak to tylko możliwe, jednak wystarczająco, aby test przeszedł. Trzecim ostatnim krokiem, jest poprawienie kodu (zarówno programu jak i testu) aby zwiększyć jego czytelność i funkcjonalność, nie psując przy tym testu. Trzeci krok nie jest obowiązkowy. Metodologia ta mówi także, że ten proces nie powinien zająć mniej niż 10 minut.

Proces taki często nazywany jest Red-Green-Refactor i może być zobrazowany np. w ten sposób:
Red_Green_Refactor

Podczas refactoringu musimy uruchamiać testy od czasu do czasu, aby sprawdzać czy nadal są one zielone.

Jeżeli dobrze będziemy używać TDD to przyniesie nam to bardzo dużo dobrego. Przede wszystkim, nasz program będzie dobrze pokryty testami, powinniśmy się starać tak pisać testy, aby pokryć każdą linijkę kodu. Drugą zaletą jest zasada YAGNI (You Ain’t Gonna Need It), pisząc kod nie powinniśmy dodawać funkcjonalności, które nie są nam w tej chwili potrzebne. Osobiście ciężko mi było przestawić się ten rodzaj programowania. Zawsze miałem „wizję” całego programu przed oczami i gdy pisałem coś nowego miałem na uwadze funkcjonalności z końca projektu. Czasami głowa bolała od tego wszystkiego, gdy zacząłem się gubić co i gdzie ma być. Zgodnie z YAGNI nie myślę co będzie „potem” skupiam się na „teraz”, bo pracując wg metodologi Agile, może się okazać, że „potem” będzie zupełnie inne niż zdaje się być.
Opisując zasadę YAGNI można jeszcze zwrócić uwagę na kilka ważnych rzeczy. Kod pisany nad wyrost może znacznie podnieść ryzyko niepowodzenia projektu. Dopisując coś, co tak na prawdę w tej chwili jest zbędne musimy to brać pod uwagę przy pisaniu ważnych rzeczy. Możemy nieświadomie wprowadzić ograniczenia, które staną na przeszkodzie terminowemu ukończeniu projektu. Najczęstszym błędem jest myśl „Tylko dopiszę trzy linijki”. Potem 3 kolejne do tych trzech i tak lawinowo napiszemy kawał kodu, który musimy dodatkowo udokumentować i otestować.

W TDD testy stają się integralną częścią systemu a nie istnieją jedynie jako dodatek do niego.

Jak powinno wyglądać TDD

Mamy na prawdę dużo narzędzi, aby testy były wykonywane automatycznie. Szkoda czasu na ręczne uruchamianie ich. Wiele też środowisk programistycznych posiada narzędzia wspomagające testowanie.

  1. Pierwszym krokiem jest przygotowanie listy funkcjonalności, które zamierzamy napisać. Lista taka jest przeważnie utworzona w jakiś zewnętrznym narzędziu do prowadzenia projektów.
  2. Piszemy test – zgodnie z powyższymi wytycznymi. Test sprawdzający jedną funkcjonalność.
  3. Piszemy kod – który spowoduje pomyślnie ukończenie testu – piszemy tylko tyle ile trzeba.
  4. Gdy jest taka potrzeba poprawiamy kod programu i kod testów. Testy nadal muszą być zielone.

Cały czas pamiętajmy o 10 minutowym cyklu.

Jakieś rady?

Testy możemy pisać w dowolnej kolejności, nie są uzależnione jeden od drugiego. Pamiętajmy tylko aby był on nieduży. Gdy test nam trochę „urośnie” to podzielmy go na jeden lub dwa. Dobrą praktyką jest nie nadużywanie dysku twardego lub bazy danych. Tam gdzie to jest możliwe, w ogóle z tego nie korzystajmy. Twórzmy obiekty w pamięci, chyba że specyfika testu akurat będzie wymagać dostępu do innego medium.

Problemem jednak mogą być sytuacje gdy jedna klasa jest powiązana bezpośrednio z drugą. Nie powinniśmy testować dwu klas na raz NIGDY. Aby tego uniknąć stworzono dwa sztuczne, puste obiekty testowe. Pierwszym z nich jest stub. Stuby są obiektami, których stanem sterujemy ręcznie. Nie posiadają one żadnej logiki obliczeniowej. Gdy Stub ma zwracać jakąś wartość – to musimy sami ustalić jaką. Użycie stubów przeważnie jest wykorzystywane tylko po to aby aplikacja się kompilowała.
Drugim sztucznym obiektem jest mock. Obiekty typu mock z kolei, służą do testowania funkcjonalności. Sprawdzamy za ich pomocą działanie wywoływanych metod. Opisywanie mocków to jest temat na długi artykuł więc pozostanę na takim opisie. Dodam tylko jeszcze to:

  • Stub jest obiektem zawierającym informacje o stanie faktycznym, pożądanym lub końcowym. Służy zatem do testowania stanu.
  • Mock jest obiektem zawierającym informacje o zachowaniu i sposobie dotarcia do stanu końcowego. Służy zatem do testowania zachowania.

Nie wszystko testujemy

Pisząc pierwsze w swoim życiu testy, zastanawiamy się co testować, a czego nie? Dla początkującego może to być poważny problem, aby nie tracić czasu na zbędne testy.
Główną zasadą jest to, że testujemy każde miejsce zagrożone błędem – wszystko to co jest podejrzane o zagrożenie w działaniu. Przypuszczam, że po pewnym czasie intuicja dokładnie nam podpowie co testować a co nie.

Czego nie testować?

Nie testujemy przede wszystkim obcego kodu. Jeżeli w projekcie wykorzystujemy obcą bibliotekę, to jej nie testujemy. Testy piszemy tylko i wyłącznie do własnego kodu. Nie testujemy także getterów i setterów oraz metody toString() (w javie), chyba że metody te mają jakąś dodatkową funkcjonalność. Nie testujemy także głównej biblioteki języka. Istnieje także szkoła, która mówi, że nie testujemy metod prywatnych ponieważ są one testowane przy okazji metod publicznych. Tak i nie. Ale powinniśmy sami dojść do tego czy powinniśmy takie metody testować czy nie. Może to zależeć w szczególności od języka i od specyfiki projektu. Uważam, że w niektórych przypadkach metody prywatne także powinny zostać przetestowane.

Problemy z TDD

Przy niektórych aplikacjach możemy napotkać problemy z pisaniem testów. Testowanie niektórych funkcjonalności może być bardzo trudne, np.:

  • testowanie na danych w bazie danych,
  • testowanie aplikacji wielowątkowych,
  • testowanie Interfejsu Użytkownika,
  • testowanie aplikacji uruchamianych w oddzielnych kontenerach (JSF, Portlets),

W takich sytuacjach powinniśmy sami zdecydować co jesteśmy w stanie poświęcić:

  • bezpieczeństwo – nie poświęcając zbyt dużej ilości czasu na te skomplikowane testy,
  • czas – pisząc te testy.

Reasume

Mam nadzieję, że ten pokrótce przedstawiony temat zachęci do testowania swojej aplikacji w większym stopniu. Albo chociaż zachęci do zapoznania się bardziej szczegółowo z tematem TDD.
.