Blog
Możemy wyróżnić dwa sposoby tworzenia oprogramowania z użyciem testów automatycznych: ten w którym testy są pisane po utworzeniu kodu, oraz ten w którym testy są pisane są najpierw (TDD).
Co je jednak różni? Porównajmy te dwa procesy w aspekcie jakości kodu wynikowego, czasochłonności oraz trafności przeprowadzanych testów.
Prześledźmy historię mającą miejsce przed wiekami, w dalekiej krainie. Historię wielkiej inwazji orków. Ohydne orki, wspomagane przez jeszcze paskudniejsze trolle, zapragnęły zająć cały świat. Naprzeciw nich stanęły dwa dzielne ludzkie królestwa. Królestwo w którym testy pisane są „po” i królestwo Test Driven Development. W obu królestwach inżynierowie opracowali takie same sposoby walki ze złem. Zaostrzone palisady chroniące przed orkami i trebusze miotające wielkie głazy przeciwko trollom. W obu królestwach ochoczo przystąpiono do realizacji. W obu jednak w zgoła inny sposób.
Jakość kodu
Celem testów tworzonych po napisaniu kodu jest zapobieganie regresji w przyszłości. Testy bazują na już istniejącym kodzie, a tym samym nie mają wpływu na jakość tego kodu. Głównym zadaniem tych testów jest wykrywanie błędów mogących pojawić się w przyszłości.
Podobnie sądzili inżynierowie z pierwszego królestwa. Najpierw zbudowali trebusze i palisadę. Wówczas aby zapobiec temu że kolejne zmiany zepsują prawidłowe działanie systemów obronnych, utworzyli realistyczne makiety orków i trolli, aby po każdych kolejnych zmianach móc testować umocnienia – by się upewnić czy wszystko nadal działa jak należy.
Zupełnie inne podejście prezentuje metodologia Test Driven. Głównym celem jest zapewnienie jakości kodu. Dzięki temu że najpierw powstają testy a dopiero później kod – proces ten automatycznie wymusza tworzenie kodu czystszego i lepszej jakości. Dzieję się tak z dwóch powodów. Po pierwsze, utworzenie kiepskiej jakości kodu jest po prostu trudne, więc samoczynnie nasuwa się łatwiejsze wyjście. Po drugie, integralną częścią procesu TDD jest ciągła refaktoryzacja, mająca również znaczący wpływ na jakość kodu. I dopiero najmniej ważnym celem procesu, niemalże pobocznym w świetle poprzednich, jest testowanie regresji w przyszłości.
Tak też sądzili w drugim z królestw. Tutaj zanim przystąpili do budowy umocnień, najpierw krok po kroku przygotowywali bazę testów. Najpierw zasymulowali ciężar orka by określić czy pale na palisadę są wystarczająco wytrzymałe. Dzięki temu wybrali ich odpowiednią grubość. Wówczas wzięli skórę orka by testować czy ostrza palisady są zdolne przebić ten pomiot zła. Po tym kroku zaczęli ostrzyć pale do czasu aż zaczęły przebijać skórę. I dopiero gdy surowce na palisadę spełniały normę – wówczas wykonali pełną makietę orka by sprawdzać pod jakim kątem i jak wysoko należy wbić palisadę. Dzięki temu mieli pewność że w finalnie ukończonej palisadzie każdy element będzie prawidłowy.
Czasochłonność
Budowniczy pierwszego królestwa uważali że budowa połączona z tworzeniem testów w pierwszej kolejności okaże się powolniejszym procesem. Jak wielkie było ich zdziwienie gdy okazało się, że umocnienia na pozycjach TDD dawno zostały ukończone, a tymczasem oni nadal nie skończyli. Dlaczego tak się stało? Otóż budowniczowie nie przewidzieli, że manualne testowanie czy umocnienia są prawidłowe, zabierało cenny czas. Podczas gdy w krainie TDD półautomatycznie, mechanicznie nabijali uprzednio przygotowany kawałek skóry na pal by sprawdzić jego ostrość – tutaj taka weryfikacja była dokonywana manualnie przez ludzi. Dodatkowo, gdy okazało się że palisada nie jest wystarczająco wytrzymała – musieli ją rozebrać i zmontować ponownie – podczas gdy to samo w królestwie TDD wychwycono już na pierwszym etapie – długo przed tym zanim zaczęto wbijać pale w ziemię. Na domiar złego, utworzenie makiet orków i trolli po zakończeniu prac, okazało się koszmarnym i trudnym zajęciem – podczas gdy w krainie TDD, te makiety nabierały kształtu w prostszy sposób, ewolucyjnie, z zadania na zadanie, z dnia na dzień.
Tworzenie testów po utworzeniu kodu trwa relatywnie długo. Utworzenie przypadków testowych jest zazwyczaj mocno skomplikowane z racji na zależności i złożoność kodu. Na domiar złego, nieco wcześniej przetestowaliśmy to manualnie, więc dublujemy swoją pracę.
Proces TDD przebiega znacznie szybciej niż proces tworzenia testów po. Nawet więcej – w prawidłowo przeprowadzonym procesie TDD tworzenie kodu może być szybsze nawet bardziej niż utworzenie kodu który w ogóle nie ma testów – dzięki temu że unikamy powolnych testów manualnych – a zamiast tego zastępujemy je szybkimi testami automatycznymi.
Trafność testów
Czy w obu królestwach jakość testów była identyczna? Jak pokazała wojna z orkami – niestety nie.
W pierwszym królestwie zweryfikowano tylko działania względem standardowych typów orków. Powstały podstawowe typy makiet. Z różnych powodów: część była zbyt trudna do wykonania, części zaniechano celowo a części po prostu zapomniano. Nie sprawdzono działania gdy ork był wyższy, cięższy lub bardziej gruboskórny. Część przeciwników przebiła się przez umocnienia i walki z nimi trwały po blady świt.
Tymczasem w krainie TDD do testów ostrości pali, użyli różnych rodzajów skóry orczej, zarówno tych mniej jak i bardziej gruboskórnych. Do testów wytrzymałości użyto różnych ciężarków odpowiadających różnym typom orków. Wreszcie do testów wysokości użyto kilku prostych makiet odpowiadających wysokościom różnym orkom. Dzięki czemu sprawdzono czy palisada spełni swe zadanie dla każdego typu orka.
W przypadku testów pisanych po utworzeniu kodu jest trudno pokryć 100% ścieżek kodu. Trudne jest samo uzyskanie pokrycia 100% linii kodu a co dopiero pokrycie 100% logiki biznesowej. Na domiar złego, często elementy które nie są przetestowane, są to elementy w których napisanie testów jest najtrudniejsze – a właśnie te elementy powinny być przetestowane najbardziej. Testowanie mniej złożonego (i tym samym łatwiej testowalnego) kodu ma mniejszy sens niż testowanie kodu bardziej złożonego. Niestety w praktyce jest zazwyczaj dokładnie odwrotnie. Trudne rzeczy omijamy, a testujemy te błahe.
W przeciwieństwie do powyższego – w przypadku TDD nie ma miejsca na wyjątki. Z założenia pokrycie testami powinno osiągać 100% – i to zarówno proste pokrycie linii kodu, jaki i, co ważniejsze, pokrycie logiki biznesowej.
Podsumowanie
Podsumowując – w każdym z powyższych aspektów proces TDD przewyższa pisanie testów po utworzeniu kodu. Wprawdzie oba królestwa dzielnie odparły inwazję orków. A obaj królowie na stałe zapisali się w annałach historii. Jednak w królestwie w którym pisali testy „po” budowa umocnień trwała dłużej i nie były one tak dobrej jakości jak w krainie TDD, przez co wielu dzielnych wojów nie wróciło nigdy do swych domów.
A Ty jakie widzisz inne zalety (bądź wady) procesu TDD w porównaniu do pisania testów po? Jak powstawała Twoja zbroja? Czy jesteś pewien że jest w pełni sprawna? Czy masz pewność że żaden z elementów nie zawiedzie, gdy przyjdzie czas?