Testowanie swojego kodu to bardzo istotna część pracy każdego programisty, jednak, nie każdy z nas lubi to robić. Sposobem na zapewnienie odpowiedniego pokrycia swojego kodu może być TDD.
Czym jest TDD?
To nic innego jak pisanie oprogramowania stymulowane przez testy. Cykl implementacji nowej funkcjonalności wygląda następująco:
1. Dodaj test
Dodanie każdej nowej funkcjonalności rozpoczyna się od napisania testu. Aby być w stanie napisać test musisz dobrze rozumieć implementowaną funkcjonalność — jej wymagania i wynik działania — rozbijając to na pojedyńcze cechy (tak jak w testach jednostkowych). Możesz napisać od razu wszystkie testy, lub tylko jeden, ważne by w tym etapie w ogóle nie myśleć o kodzie, który będzie im odpowiadał — masz po prostu zmapować oczekiwane zachowanie funkcjonalności.
2. Uruchom test(y)
Powinny one nie przechodzić. Każdy nowododany test nie może zakończyć się sukcesem — gdyby tak było, byłby on z bardzo dużym prawdopodobieństwem napisany w zły sposób. Pamiętaj, że taki test powinien nie przejść z znanych Ci powodów — w kolejnym kroku będziesz musiała bowiem dodać kod, który pozwoli mu zakończyć się sukcesem.
3. Napisz kod
Dodaj kod, dzięki któremu test przejdzie pozytywnie. Kod ten nie musi być perfekcyjny (na refactor będzie czas później), natomiast nie powinien on wyprzedzać napisanych testów. Dodatkowe funkcjonalności powinny być implementowane dopiero kiedy pojawią się nowe testy.
4. Uruchom testy
W rezultacie wszystkie dotychczas niedziałające testy powinny przechodzić pozytywnie (jeśli nie — wracasz do programowania). Jeśli wszystkie napisane testy kończą się sukcesem, oznacza to, że Twój kod odpowiada wymaganiom biznesowym.
5. Refactor kodu
To bardzo ważny element takiego podejścia do implementacji — jako, że TDD pozwala na chwilowe rozwiązania (o tym za chwile w przykładzie), czyszczenie kodu jest ważnym i potrzebnym etapem. Wszystkie powtórzenia powinny być wyeliminowane, a kod odpowiednio uporządkowany — jak?- o tym przeczytasz w naszym wpisie o refaktorze.
Powtórz
Aż do dostarczenia pełnej funkcjonalności. Pamiętaj, że z TDD możesz korzystać również gdy modyfikujesz już napisaną aplikację bądź pracujesz z legacy code.
Przykład
Wyobraźmy sobie, że mamy do napisania aplikację, która oblicza silnię. Poniższy przykład będzie w pseudo kodzie:
W pierwszej iteracji napisałabym test, który sprawdza, czy program poprawnie obliczył 0!
//Test
int liczba = 0;
AssertEquals(0,obliczSilnie(liczba));
Odpowiedzią na taki kod, była by metoda przyjmująca int:
obliczSilnie (int liczba){
return 1;
}
Jak widzicie, na ten moment nie robię tego w prawidłowy sposób, po prostu odpowiadam na test, tak by przechodził.
Pisząc test dla 1! okazałoby się, że nie muszę nic modyfikować w kodzie.
Dla 2! mogłabym np. napisać coś takiego:
obliczSilnie (int liczba){
if (liczba == 0)
return 0;
else
return liczba;
}
Teraz wszystkie 3 testy mi przechodzą, ale nadal nie korzystam z metody obliczania silni — powinnam to zmienić, z resztą test case dla 3! to na mnie wymusza.
Modyfikuje więc mój kod:
obliczSilnie (int liczba){
if (liczba == 1)
return 1;
else
return liczba * obliczSilnie(liczba - 1);
}
Mam więc prawie gotowy kod (brakuje tu weryfikacji czy nasz int jest liczbą naturalną), a doszliśmy do niego pisząc 4 przypadki testowe.
Kiedy stosować?
Po pierwsze, kiedy jesteś na początku swojej nauki programowania i nie do końca opanowałeś testy — wtedy takie podejście zmotywuje Cię do szybszego ich opanowania — to naprawdę super sposób na wyćwiczenie się w pisaniu testów!
Po drugie, gdy nie do końca wiesz jak zaimplementować daną funkcjonalność — TDD skupia się na dokładnym zrozumieniu wymagań i potrzeb klienta, a także zmusza Cię do rozbicia funkcjonalności na małe kroki — dzięki takiemu podejściu łatwiej będzie Ci znaleźć rozwiązanie.
Po trzecie, kiedy modyfikujesz już działające funkcjonalności takie podejście pozwoli Ci łatwiej zweryfikować koniecznie modyfikacje.
Uwaga!
TDD jako metoda programowania ma tyle samo zwolenników co przeciwników — czy po prostu osób, które nie lubią w taki sposób pracować. Nie należy z test driven development robić religii — jedynego słusznego podejścia. Warto na pewno poznać taki sposób pracy — jeśli będzie Ci on odpowiadał możesz stosować go na codzień, a jeśli nie — wiedzieć, że możesz go wykorzystać w trudnych przypadkach by lepiej zrozumieć wymagania funkcjonalności.
Dodatkowa literatura:
- Test Driven Development: By Example Kent Beck
- Dyskusja na temat TDD na Reddit(EN)
- Ciekawe porównanie podejść do pisania aplikacji na przykładzie sudoku solvera (EN)