Testy to święty gral początkujących programistów. Będąc na stanowisku CTO prowadziłem sporo rekrutacji na stanowiska juniorskie. Najczęściej powtarzaną przez rekrutów pożądaną cechą zespołu było praktykowanie pisania testów.

Przy okazji testów wszyscy wspominali o podejściu TDD (Test Driven Design), a nawet byli w stanie zdefiniować głównego założenie tego podejścia. Niestety ani jeden z nich nie pracował w takim podejściu, a zdecydowana większość z nich nigdy nawet nie pisała testów. Wszyscy narzekali, że w projektach, z którymi obecnie pracują testów nie ma i podawali to jako główną przyczynę problemów z aplikacją.

The grass is always greener on the other side of the fence

Wszyscy testujemy

Pracując w jednej z firm miałem sporą styczność z prowadzoną przez tę firmę fundacją. Fundacja ta była założycielem akcji, która ma na celu pomoc dzieciom w gorszej sytuacji materialnej wejść w życie dorosłe poprzez wyrównywanie ich szans na rynku pracy IT. Prowadzone było sporo szkoleń dla dzieci w różnym wieku w zakresie programowania.

Pracując z początkującymi programistami zauważyłem, że każdy, nawet najbardziej początkujący programista testuje swój kod. Przecież praktycznie wszyscy zaczynają od tego słynnego Hello World!. Jest to nic innego jak sprawdzenie, czy nasza aplikacja poprawnie się uruchamia, oraz czy mechanizm renderowania tekstu działa. Klawisz F5 pozwala weryfikować poprawność wprowadzanych zmian do kodu i “uruchamianie naszego testu” od nowa.

W większych aplikacjach również można zauważyć, że programiści bardzo często posługują się w trakcie pisania jednym zestawem poleceń. Są to wszelakiego rodzaju var_dump, var_export, dd, dump, die, exit czyli funkcje, które informują w jakim stanie znajduje się w danym czasie aplikacja.

Po skończeniu pisania fragmentu kodu, aplikacji użycie tych funkcji zostaje usunięte i aplikacja jest już “gotowa”.

A co gdyby te wszystkie procesy zautomatyzować… ?

Co warto testować?

To zależy. Od czego? Głównie od tego co piszemy. Czy piszemy stronę firmową? Czy piszemy system bankowy? Czy piszemy może oprogramowanie do marsjańskiego łazika? W każdym z tych przypadków nacisk na testowanie oprogramowania będzie inny.

Testy można podzielić na 3 główne rodzaje. Testy end to end, integracyjne oraz jednostkowe. Zależności między nimi idealnie ilustruje poniższa grafika.

Piramida testy

Testy End to end są najbardziej czasochłonne sprawdzają największy obszar mechanizmów jednoczenie co powoduje że są bardzo kosztowne w utrzymaniu. Jedna zmiana może wymagać szeregu aktualizacji w kodzie testującym.

Testy integracyjne dotykają jedynie wybranych elementów systemu.

Najszybciej pisze się jednak testy jednostkowe. Koszt ich utrzymania jest również najniższy ponieważ jeden test może testować jedynie jedno zadanie.

Czym jest test?

Definicje testu można pewnie bardzo łatwo wywnioskować po początku tego tekstu.

Test to kod, który testuje inny kod.

Może sprawdzać oczekiwany wynik. Może sprawdzać wystąpienie wyjątków.

Czym jest jednostka

Jednostka w rozumieniu testów jednostkowych to najmniejsza testowalna część kodu aplikacji.

Po co w ogóle testować?

Testy automatyczne są kodem. Kod można dowolną ilość razy uruchamiać. Jest jest to angażujące dla osoby obsługującej aplikację. Przy zastosowaniu CI jest praktycznie bezobsługowe. Lepiej oddać mozolną powtarzalną pracę komputerowi. Przenosząc tą odpowiedzialność na komputer nie tylko jesteśmy w stanie zaoszczędzony czas przeznaczyć na inne rzeczy, ale też komputer jest w stanie naszą pracę wykonać dużo, dużo szybciej. Projekt zawierający tysiące testów jednostkowych komputer jest w stanie przetestować nawet w minutę.

Co więcej, jeżeli nasze testy są poprawnie napisane jesteśmy możemy być pewni, że testy wykonywane są za każdym razem w identyczny sposób. Przy testowaniu tego ręcznie człowiek może się wiele razy pomylić.

Jeżeli automat jest taki szybki to po co ograniczać się jedynie do testowania kodu nad, którym obecnie pracujemy. Automat może testować wszystkie funkcjonalności aplikacji. Przez co możemy spać spokojnie wiedząc, że nowo napisany kod nie zepsuje niczego po drugiej stronie aplikacji. Skutkuje to uniknięciem wielu niepotrzebnych poprawek, debugowaniu całej aplikacji i korzystaniu z opcji git blame aby znaleźć winowajcę.

Myślę, że zdecydowana większość programistów nie lubi pisać dokumentacji do swojego kodu. Zwłaszcza, jeżeli jest ona potrzebna dla innych programistów. Posiadając testy danej funkcjonalności w pewnym sensie stają się one jej dokumentacją. W testach zawarte są wszystkie przypadki użycia interfejsów danej klasy. To jak można z niej korzystać, a jak nie. Testy zawsze są aktualnie ponieważ odzwierciedlają sam kod aplikacji. Nigdy wiec nie rozjadą się względem faktycznego stanu.