Z uwagi na koszt napisania i utrzymania testów w systemie, powstało zagadnienie: piramida testów, które standaryzuje to, ile, jakich testów powinniśmy pisać. Testy jednostkowe, integracyjne oraz e2e powinny być dobierane w sposób przemyślany. Sprawdź, jak to robić, aby zoptymalizować koszty, które każdorazowo ponosisz, wprowadzając warstwę testową.
Sens testowania
Testowanie oprogramowania kojarzy się każdemu z nas trochę inaczej. Na pewno wszyscy zgodzimy się, że jest to coś potrzebnego, bo jaki jest sens wypuszczać w świat nieprzetestowane rozwiązania?
Sprawdzanie software’u może odbywać się w sposób manualny, gdzie pewna oddelegowana osoba ręcznie „bada” dostarczone rozwiązanie pod kątem ewentualnych błędów lub niezgodności ze specyfikacją. Testowanie manualne jest drogie, ponieważ wymaga czasu, a także tworzenia scenariuszy testowych, po których tester będzie przechodził. Czasami jednak takie testy są nieuniknione, natomiast w wielu przypadkach możliwa do wprowadzenia jest warstwa testów automatycznych, czyli takich, które będą symulowały zachowania, które wcześniej robił tester manualny. Ten rodzaj sprawdzania również jest stosunkowo drogi i w napisaniu i w utrzymaniu, ponieważ wymaga odpowiedniego środowiska uruchomieniowego, analizy kodu, a także obsługi zmian.
Podrodzajem testów automatycznych są testy niższego poziomu. I to na nich się dzisiaj skupimy. Zazwyczaj piszą je sami programiści, podczas implementacji poszczególnych funkcjonalności. Wyróżnić możemy trzy główne rodzaje, których dotyka piramida testów: testy jednostkowe, integracyjne oraz e2e. Szczegółowo opiszę je wszystkie w osobnym wpisie, gdzie załączę również stosowne przykłady, jednak dzisiaj wystarczy nam wiedza teoretyczna oraz umiejętność ich rozróżnienia.
Koszt napisania i utrzymania testu
Jak wspomniałem, wdrożenie warstwy testowej zawsze wiąże się z kosztami. Są to jednak koszty, które warto ponieść, ponieważ mogą one być niczym w porównaniu z kosztami konsekwencji, które poniesiemy wypuszczając niestabilne, nieprzetestowane oprogramowanie. Skupmy się na koszcie trzech rodzajów testów niższego poziomu, bo ich dotyka omawiane dzisiaj zagadnienie piramidy testów.
- Test jednostkowy (unit test), jest to test, który na niskim poziomie sprawdza poprawność realizowanej logiki przez pewną jednostkę. Ową jednostką może być pojedyncza funkcja, klasa czy nawet kilka klas, które stanowią spójny unit. Warunkiem jest, aby taki test nie wychodził poza środowisko, czyli np. nie łączył się z bazą lub nie wykonywał zapytań http. Jak widać, w tym przypadku, jedyny koszt o jakim możemy mówić, to koszt napisania takiego testu i jego ewentualne utrzymanie. Programiści powinni testować swoje programy przed ich oddaniem, więc koszt napisania można powiedzieć, że jest kompensowany korzyściami braku konieczności „przeklikiwania”. Jeśli zaś idzie o utrzymanie, to celowo napisałem „ewentualne”, gdyż logika niskiego poziomu zmienia się raczej rzadziej, niż częściej. Dodatkowo, uruchomienie tych testów w procesach CI również nie generuje olbrzymich kosztów, ponieważ nie wychodzimy poza środowisko, przez co nie ma konieczności dodawania innych kontenerów, np. bazy danych. I ostatnia kwestia, unit-y są szybkie!
- Test integracyjny, to rodzaj testu, w którym sprawdzamy integrację pomiędzy dwoma elementami. Mogą być to elementy systemu, np. dwa mikroserwisy w jednym klastrze, a także element systemu z zewnętrznym zasobem, np. REST API systemu do wysyłania SMS-ów. Chodzi tutaj o sprawdzenie czy sama integracja działa, często z pominięciem niskopoziomych aspektów. Utrzymanie tych testów jest droższe, między innymi z uwagi na fakt, że zmiana może nastąpić również po drugiej stronie. Ich napisanie jest też bardziej czasochłonne. Z drugiej strony, uruchamianie ich podczas pipeline’ów generuje także dodatkowy koszt, bo musimy postawić kontener z bytem, z którym chcemy sprawdzić integrację. Czas testowania jest również już nieporównywalnie większy.
- Test e2e (end to end), czyli sprawdzenie całego flow, od początku do końca. Testujemy tutaj czy system zachował się zgodnie z oczekiwaniami, we wszystkich możliwych aspektach. Taki test może np. symulować wciśnięcie przycisku „utwórz” i sprawdzenie czy w bazie danych dodał się rekord, czy został wysłany email powitalny, a także czy wyświetlone zostało odpowiednie powiadomienie. Jak się pewnie domyślasz koszt takich testów jest już nieporównywalnie większy od poprzednich. Idąc przez ich napisanie, utrzymanie, czas testowania, czy kończąc na uruchamianiu ich w CI.
Piramida testów, co to?
Przejdźmy teraz do właściwego zagadnienia dzisiejszego wpisu – piramidy testów. Jak już wspomniałem wcześniej, określa ona ilość testów poszczególnego typu (unit, integracyjny, e2e), jaką powinniśmy mieć w systemie. Jest ona zbudowana na podstawie kosztów poszczególnych rodzajów, które przed chwilą sobie omówiliśmy. Piramida testów zakłada, że powinniśmy pisać jak najwięcej testów jednostkowych, ponieważ są po prostu najtańsze, trochę mniej integracyjnych, a najmniej testów typu end to end, gdyż koszty ich utrzymania są spore. Graficzna reprezentacja zagadnienia wygląda następująco:

Największym obszarem jest podstawa piramidy, gdzie znajdują się unity. Im wyżej, tym ta przestrzeń się zmniejsza, co ma odzwierciedlenie w tym, że na kolejnej belce mamy testy integracyjne, zaś na tej najwyższej e2e.
Uzupełnienie tego zagadnienia możesz znaleźć tutaj.
Rodzaje piramidy testów
Okazuje się, że piramida testów nie jest twardą regułą, a raczej zaleceniem w stylu: posiadaj x testów danego typu, bo tak jest ekonomicznie. Niestety, jak to w życiu bywa, nic nie jest stałe i pewne. Przecież mogą istnieć systemy, w których sztuczne pisanie, np. testów jednostkowych nie ma jakiegokolwiek sensu, ponieważ będą wnosić one znikomą wartość. Wyobraź sobie np. makro serwis translacyjny. Pobiera on dane od pewnego dostawcy, mapuje do wspólnej formy, a następnie dodaje je do bazy lub też call-uje inny system. W mojej opinii, pole do pisania unit-ów jest tutaj małe. Zamiast tego powinniśmy skupić się na przetestowaniu integracji lub samego flow, od końca (wysłanie żądania o pobranie danych), do końca (po ich przetłumaczenie i zapisanie).
Piramida testów może się więc różnić, w zależności od tego, co dany system robi i jakie ma potrzeby. Dla aplikacji typu X może okazać się, że ekonomiczniej będzie napisać więcej testów integracyjnych, tak, jak w przykładzie przed chwilą. W takich przypadkach mówimy o podrodzaju piramidy, diament.

Jak się pewnie domyślasz, może również zajść potrzeba posiadania dużej ilości testów end to end w naszym systemie. Wtedy mówimy o odwróconej piramidzie testów.

Podsumowanie
- Wdrażanie warstwy testowej w systemie pozwala ustrzec się przed błędami.
- Piramida testów, to koncepcja mówiąca ile testów, jakiego rodzaju, ze względu na koszt i zyski z tego płynące, powinniśmy posiadać w systemie.
- Wyróżnia się trzy główne typy tego podejścia: standardowy, diament oraz piramida odwrócona, które zależą od specyfikacji systemu.
Dodaj komentarz