Typy i poziom testów. Co warto wiedzieć chcąc zostać testerem
Tworzenie oprogramowania, to nie tylko pisanie kodu lub zbieranie wymagań od klienta, a proces znacznie szerszy i bardziej czasochłonny. Jednym z elementów tego łańcucha zależności są testy. Każda stworzona linijka kodu powinna być sprawdzona pod różnym kątem, aby uniknąć błędów, które mogą spowodować niewielkie niedogodności po stronie klienta lub olbrzymi straty w firmie. Stąd też temat testowania jest niezmienienie ważny.
Każdy deweloper spotka się z testami i zapewne, czy tego chce czy nie, będzie musiał takie testy pisać, a i potem również je utrzymywać. Szczegóły odnośnie typów testów i ich poziomów będą przydatne dla osób, wchodzących w świat testów jako tester czy deweloper.
Każdy rodzaj testów może zawierać testy z innej grupy (np. testy jednostkowe mogą posiadać testy funkcjonalnie i niefunkcjonalne). Podział powstał, aby łatwiej rozdzielić zarówno zasoby ludzkie, jak i weryfikować i analizować wyniki poszczególnych testów.
Typy testów
Na początku przedstawię jakie typy testów mogą występować podczas weryfikacji kodu. Głównym kryterium jest cel testów i to co chcemy testować.
Podstawowymi testami są testy funkcjonalne. Określają one testy, których celem jest sprawdzenie funkcjonalności, jakie oferuje kod. Weryfikacja nie analizuje jak działa aplikacja, a sprawdza wyniki, jakie zostaną uzyskane, poprzez wprowadzenie konkretnych wartości na wejściu. Często mowa jest o testach czarnej skrzynki (black box), gdyż bazą jest dokumentacja, a nie sposób (kod) w jaki napisane zostało oprogramowanie.
Kolejnym typem testów są testy strukturalne. W tej grupie testuje się kod pod kątem jego działa i wszystkich możliwych ścieżek wykonania. Testy strukturalne to inaczej testy białej skrzynki (white box). W odróżnieniu od testów funkcjonalnych w testach strukturalnych interesuje nas kod i to jak działa. W tym miejscu ważne jest pokrycie kodu testami, czyli napisanie takiej ilości testów, która przetestuje wszystkie linie w kodzie. Idealnie jest, gdy pokrycie wynosi 100% lub jest bliskie tej liczbie.
Warto dodać, że testami strukturalnymi są również testy statyczne. Obejmują one chociażby weryfikację kodu (code review), czyli sprawdzanie kodu bez jego uruchomienia („na sucho”).
Testy niefunkcjonalne to grupa testów, która nie weryfikuje tego co zwraca kod lub w jaki sposób robione są obliczenia. W tym przypadku sprawdzane są komponenty i to jak system działa. Można zatem badać wydajność systemu pod kątem obciążenia czy niezawodności, a także testować zabezpieczania systemu. Możliwości jest dużo, ale bardzo często wymagane jest to, aby do każdego z testów stworzone zostało oddzielne środowisko, które umożliwi rzetelne przeprowadzenie weryfikacji. Bardzo często testy przeprowadzane są przez dedykowane osoby/zespoły, które zajmują się tylko jedną dziedziną testów (np. testowanie bezpieczeństwa).
Na koniec typów testów warto wspomnieć o testach, które tworzone są ze względu na zmiany w systemie. Projekt cały czas żyje i nawet jeśli jest tylko w stadium utrzymania, to nowy kod naprawiający błąd, może wpłynąć na istniejące funkcje. Z tego też powodu wykonuje się retesty, czyli ponowne testy po usunięciu błędu. Dodatkowo można również wykonywać testy regresyjne, weryfikując, czy w działających modułach nie znalazły się jakieś błędy. W tym miejscu warto nadmienić o smoke testach (testy dymne). Są to testy, które sprawdzają podstawowe i najbardziej newralgiczne części systemu. Najczęściej jest tu mowa o szybkich testach, które weryfikują czy główny rdzeń aplikacji działa poprawnie.
Poziomy testów
Innym podziałem testów jest rozdzielenie ich pod kątem tego kiedy i dla kogo zostały stworzone. Można tu mówić o poszczególnych etapach projektu i wydzieleniu testów w każdym z takich faz.
Zaczniemy od testów jednostkowych. Zbiór ten obejmuje badanie poszczególnych modułów, często bardzo niewielkich, mowa jest tu nawet o pojedynczych klasach czy metodach. Pomimo tego, iż testuje się niewielki wycinek systemu, to najczęściej jest on zależny od innych, zewnętrznych elementów. W tym celu bardzo często w testach jednostkowych stosuje się zaślepki (tzw. mock lub stub), które umożliwią testowaniu modułu bez przejmowania się zewnętrznym kodem. W testach jednostkowych robimy niejako audyt wszystkich dostępnych ścieżek w kodzie.
Przykładem może być tu test modułu do wystawienia ofert na aukcji internetowej. Nasz kod zostanie przetestowany pod kątem wystawiania ofert przez API, ale zamiast prawdziwego API, do testów użyta zostanie zaślepka. W tym zagadnieniu mogą występować testy funkcjonalne (weryfikacja konkretnego kodu), jak i niefunkcjonalne (wydajność modułu).
Poziom wyżej znajdują się testy integracyjne, to tutaj testujemy kilka modułów i to w jaki sposób działają razem. Bardzo często weryfikuje się w tym miejscu wiele mikro-serwisów czy kilka modułów, które musza współpracować z sobą np. moduł strony web i bazy danych. W tych testach celem jest wykrycie błędów, które mogą pojawić się we współpracy kilku większych elementów.
Testy systemowe to testy użytkownika końcowego. Jest to weryfikacja jak system działa jako całość pod kątem finalnego użycia. Często weryfikacja odbywa się na środowisku zbliżonym do produkcyjnego, co pozwala na sprawdzenie założeń od strony biznesu, jak i architektury. W wielu przypadkach testy takie są czasochłonne i skomplikowane, gdyż dotyczą wielu poziomów aplikacji i nie są łatwe do zautomatyzowania. Najczęściej wykonywane są przez testerów manualnych i zlecane są niezależnym zespołom.
Finalnym poziomem testów są testy akceptacyjne. W tym przypadku nie wyszukuje się już błędów, a jedynie sprawdza się czy końcowy produkt jest tym, czego chciał klient. Z tego też powodu weryfikacja następuje przy udziale klienta, użytkownika końcowego, a system nie może już zawierać żadnych błędów. W tym przypadku celem jest potwierdzenie gotowości produktu do wdrożenia produkcyjnego.
Testy manualne czy automatyczne?
Testy mogą być przeprowadzane przez testera „ręcznie” lub poprzez napisane skrypty (automatycznie).
Testy manualne często wiążą się ze znacznie dłuższym czasem potrzebnym na sprawdzenie danego elementu. Są one także bardziej kosztowne niż testy automatyczne. Najczęściej sprowadzają się do ręcznego wyklinania odpowiednich ścieżek w testowanych modułach lub użycia narzędzi, które pozwolą na weryfikację sprawdzanego elementu (np. Postman może pomóc w testowaniu tworzonego API).
Automatyczne testy są skryptami przygotowanymi wcześniej przez testerów lub deweloperów. Charakteryzują się powtarzalnością i brakiem błędów wynikających z czynnika ludzkiego przy testach manualnych. Z drugiej strony wiele nieoczywistych błędów nie jest możliwych do wykrycia w testach automatycznych. Ich jakość uzależniona jest od tego jak dobrze taki skrypt został napisany.
Testowanie równie ważne jak programowanie
Weryfikacja kodu jest niezmiernie ważnym elementem tworzenia systemu. Jest to proces, który pozwoli na stworzenie produktu, który będzie mógł być spokojnie rozwijany i utrzymywany. Dobra jakość testów umożliwi bezproblemową pracę niewielkim klientom małej aplikacji ze sklepu Google, a także zaoszczędzenie miliardów dolarów w aplikacjach sterujących skomplikowanym sprzętem (a o konsekwencjach niech świadczy chociażby błąd w sofcie do rakiety Ariane-5, który spowodował jej autodestrukcję lub ostatnie kłopoty Boeing 737 MAX).
Powyższe podziały testów to krok w celu uporządkowania tego kiedy i jakie testy mają zostać wykonane. To również zarządzanie tym kto ma testy wykonywać, tak aby nie powielać tych samych weryfikacyjnych procedur i ścieżek. Testowanie jest nieodzownym elementem tworzenia nowego kodu i jego utrzymania. Co ciekawe, możliwe że w przyszłości testerzy zostaną ostatnim bastionem człowieczeństwa w całym procesie wytwarzania oprogramowania.