Piramida testów: kiedy, ile i jakie testy pisać

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:

Wizualna koncepcja: piramida testów.

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.

Podrodzaj piramidy testów: 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.

Odwrócona piramida 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.
Autor wpisu

blog@orbisbit.com

Komentarze

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Sprawdź również
  • CQRS – Command Query Responsibility Segregation

    Command Query Responsibility Segregation czyli CQRS. Jest to wzorzec projektowy, który rozdziela zadania odczytu i zapisu do osobnych modeli. Sprawdź ten wpis, aby dowiedzieć się kiedy i jak z niego skorzystać.

    Zobacz wpis

  • GRASP – kolejny zbiór zasad Clean Code do zapamiętania

    Pewnie większość z Was słyszała o zasadach SOLID. Są one bardzo rozpowszechnione i dosyć często stosowane, ale czy słyszeliście o GRASP? General Responsibility Assignment Software Patterns, to kolejna dawka zasad czystego kodu do zapamiętania.

    Zobacz wpis

  • Wzorzec strategia (strategy pattern)

    Jeżeli masz dość if-ologii w swoim kodzie, to konieczne sprawdź czym jest czynnościowy wzorzec projektowy strategia. Pozwala on mądrze obsługiwać różne scenariusze w procesie i jednocześnie być fancy pod względem zasad SOLID.

    Zobacz wpis