Nazwy nadane różnym typom testów zwykle mają wspólne motywy w różnych bazach kodu, ale nie mają szczególnie rygorystycznych definicji. W tym kursie znajdziesz wytyczne dotyczące poszczególnych typów testów, ale w pozostałych materiałach mogą być podane inne definicje.
Na poprzednich stronach pojawiły się przykłady zarówno testów jednostkowych, jak i testów komponentów (w tym przykładzie dotyczyły komponentu React). Możemy postawić na oba te elementy w piramidzie testowania (lub w inny kształt), ponieważ są one nieskomplikowane i szybkie w użyciu, ale mogą być mniej przydatne niż bardziej skomplikowany test integracji.
Typowe rodzaje testów
Testy jednostkowe
Najmniejszym zakresem są testy jednostkowe. Zwykle są używane do testowania małych części kodu, czyli czystego bezstanowego kodu, niemal w sposób matematyczny. Jeśli przekażę kod z wejściami X, Y i Z, jego dane wyjściowe powinny wyglądać tak: A, B i C.
Kod z testami jednostkową zwykle nie wymaga zewnętrznych zależności, np. pobierania z sieci ani pośrednio przy użyciu innych funkcji czy bibliotek. Jest to węzeł drzewa w kodzie, który możesz „wyciąć” i samodzielnie przetestować.
Testy jednostkowe zwykle szybko się piszą i przeprowadzają, ale zawsze może się zdarzyć, że testowanie małych jednostek kodu nie da przydatnych informacji. Brak interakcji jednostki kodu z innym kodem oznacza często, że testowanie na wyższym poziomie pozwala zmniejszyć ryzyko.
Testy komponentów
W przypadku programistów internetowych nazwa „komponent” jest przeciążona, co często oznacza komponent widoczny dla użytkownika, np. komponent React lub komponent internetowy. Bardziej ogólna definicja to możliwy do przetestowania fragment pracy, na przykład klasa z zależnościami zewnętrznymi. Aby skutecznie przetestować ten komponent, należy wyeliminować jego zależności lub pominąć te zależności.
Nowoczesne metody tworzenia stron internetowych opierają się na koncepcji komponentu, dlatego testy składowe stanowią praktyczny sposób testowania: możesz na przykład uznać, że każdy element wymaga testów. Testy składowe są też łatwe do kontynuowania w kontekstach, w których jeden programista lub niewielki zespół twierdzi, że jest właścicielem danego komponentu. Trudno jest jednak wypytać złożone zależności.
Testy integracji
Zwykle testują one małe grupy komponentów, modułów, podsystemów lub innych istotnych części kodu razem, aby sprawdzić, czy działają prawidłowo. To bardzo niejasna definicja. Jeśli jesteś programistą stron internetowych, wyobraź sobie, że testowany kod nie jest prawdziwą, produkcyjną kompilacją witryny (a nawet jej bliską), ale nadal łączy różne powiązane elementy systemu.
Może to nawet obejmować „rzeczywiste” zależności, takie jak zewnętrzna baza danych w trybie testowym zamiast czystoszowej bazy danych. Na przykład zamiast twierdzić, że query()
zawsze będzie zwracać te same 2 wpisy, w teście integracji można sprawdzić, czy testowa baza danych zawiera coś. Same dane są mniej ważne, ale testujesz teraz,
czy można połączyć z bazą danych i przesyłać do niej zapytania.
Można pisać stosunkowo proste testy integracji z szerokim zakresem implikacji, które można sprawdzać za pomocą asercji, ponieważ pojedyncze działanie powiązane z różnymi komponentami może powodować szereg wymiernych efektów.
Dzięki temu testy integracji mogą skutecznie wykazać, że Twój złożony system będzie działać zgodnie z oczekiwaniami. Mogą być jednak trudne do pisania i utrzymywania
i wprowadzać niepotrzebną złożoność. Na przykład wpisanie polecenia FakeUserService
na potrzeby testu integracji dodaje wymaganie, aby zarówno ona, jak i element RealUserService
muszą zaimplementować UserService
.
Testy dymu
Są to testy, które powinny zakończyć się bardzo szybko i określić, czy Twoja baza kodu działa prawidłowo. W praktyce oznacza to w dużej mierze wykonywanie prostych testów na kodzie, które mają szeroki wpływ na działanie usługi.
Na przykład w przypadku dużej aplikacji internetowej z logowaniem może to być zapewnienie, że system logowania i uwierzytelniania będzie działać, ponieważ bez niego aplikacja będzie bezużyteczna, a dalsze testowanie nie będą miały znaczenia.
Dobrym rozwiązaniem może być przeprowadzenie testów dymnych w skrypcie test
w pliku package.json w dużej bazie kodu. Jako rodzaj testu dymnego można również przeprowadzić testy ręczne.
Testy regresji
Testowanie regresji to rodzaj testów dymnych, który zapewnia, że dotychczasowe funkcje będą nadal działać, oraz że stare błędy nie zostaną wprowadzone ponownie po opublikowaniu nowej wersji lub popracowaniu nad innymi funkcjami.
Jest to związane z koncepcją programowania opartego na testach (TDD). Przypadki testowe napisane, aby wyraźnie aktywować błąd, a później używane do jego naprawienia, są liczone jako przypadki testowe regresji, ponieważ ich istnienie powinno zapobiegać ponownemu pojawieniu się tego samego błędu.
Testy regresji mogą jednak być problemem bez odpowiedniego rozwiązania. Jest to termin często przywoływany w potrzebach biznesowych: ważne jest, aby stare funkcje się nie zepsuły. Dobrze przetestowana baza kodu powinna to umożliwiać, ale rzeczywiste bazy kodu nie zawsze działają tak dobrze. Szczegółowo omówimy to w kolejnych sekcjach.
Testy wizualne
Testy wizualne obejmują robienie zrzutów ekranu lub filmów przedstawiających stan witryny w celu porównania jej stanu z bieżącym testem (np. poprzedniego zrzutu ekranu). Ze względu na swój charakter wymaga uruchomienia prawdziwej przeglądarki, która renderuje kod HTML, CSS i inne części witryny.
Zamiast wizualnego testowania pełnych testów, które obejmują całą bazę kodu, warto utworzyć „uprzęże” HTML, które renderują tylko określone komponenty, zwłaszcza na ekranach o różnych rozmiarach, aby uruchamiać elastyczne interfejsy. Jest to bardziej złożone niż korzystanie z samego JSDOM lub podobnych platform.
Niepowodzenie testów wizualnych może być dobrym sygnałem do innych rodzajów nieprawidłowości. Złożone interfejsy mogą jednak nie przejść testów wizualnych z powodów niezwiązanych z funkcjami, które próbujesz przetestować. Mogą to być na przykład inne nowe funkcje zmieniające wygląd UI lub nawet nowa wersja systemu operacyjnego renderująca emotikony inaczej niż we wcześniejszych wersjach.
Kompleksowe testy
Kompleksowe testy często znajdują się na szczycie piramidy testowej. Opisują one interakcję z aplikacją lub witryną w całości (związane z konkretną funkcją) i zwykle działają w przeglądarce kontrolowanej przez agenta takiego jak WebdriverIO, Selenium lub Puppeteer. Pozwala to na uruchamianie bazy kodu w ograniczonym lub większym stopniu niż w środowisku produkcyjnym (chociaż często są udostępniane na serwerze localhost).
W zależności od witryny może to wymagać zalogowania się jako użytkownik testowy, wykonania znaczących czynności oraz sprawdzenia, czy witryna lub system działa prawidłowo. W dalszej części przedstawimy więcej przykładów takich testów, ponieważ są one bardzo skuteczne, ale czasem trudne w utrzymaniu.
Niektóre metody ich upraszczania mogą obejmować np. ograniczenie zakresu lub wyśmiewanie konkretnych elementów, jeśli ma to zastosowanie. Jeśli na przykład użytkownicy muszą logować się w witrynie, ale to nie jest testowana funkcja, możesz ustawić flagę środowisk testowych, dzięki którym kontroler testów będzie mógł działać jako użytkownik bez logowania się i tworzenia powiązanych plików cookie.
Mimo że kompleksowe testy mogą być bardzo skutecznym sposobem testowania wielu różnych sekcji kodu naraz, takie testy na dużą skalę mogą być niestabilne lub niemiarodajne z powodu zależności od systemów zewnętrznych. Mogą też pozostawiać w bazie danych dużą ilość danych testowych, np. gdy każdy test tworzy lub modyfikuje wpis. Takie nadmiarowe dane mogą utrudniać ustalenie, dlaczego test się nie powiódł.
Testy interfejsów API
Testy interfejsów API mogą odnosić się do potwierdzania działania interfejsów API udostępnianych przez Twoje oprogramowanie lub uzyskiwania dostępu do rzeczywistych (prawdopodobnie działających) interfejsów API w celu potwierdzenia ich działania. W obu przypadkach testujemy abstrakcje między systemami, czyli sposób, w jaki będą one komunikować się ze sobą, bez konieczności ich łączenia ze sobą, jak w przypadku testu integracji.
Testy te mogą stanowić podstawowy wstęp do testowania integracji bez konieczności uruchamiania systemów, które testujesz połączenia między nimi. Jednak testy rzeczywistych systemów mogą być niepewne.
Inne typy
Są różne inne sposoby testowania, które mogą okazać się przydatne, w zależności od źródła. Interesujące przykłady to:
- Testowanie ręczne.
- Testy akceptacyjne, czyli rozpowszechnione przez firmę Agile testy ręczne, potwierdzają, że produkt „spełnia potrzeby użytkownika”.
- Testowanie chaosu polega na wprowadzaniu losowych danych w celu sprawdzenia, co się dzieje, dzięki czemu strona nie ulegnie awarii po wprowadzeniu nieprawidłowych danych.
- Niepowodzenie testów celowo symuluje awarie w złożonych systemach, np. awarie sieci, dzięki czemu testowany kod odpowiada w kontrolowany sposób.
- Testy kompilacji potwierdzają, że można wygenerować artefakty kompilacji bazy kodu przez sprawdzenie, czy istnieją i jaka jest ich zawartość. Ten typ testu może być przydatny do sprawdzania wyników złożonych systemów CMS.
Pokrycie kodu
Możesz zmierzyć, jaki odsetek Twojego kodu jest testowany w ramach testów automatycznych i raportować to jako statystyki w miarę upływu czasu. Nie zalecamy dążenia do osiągnięcia 100% pokrycia kodu, ponieważ może to prowadzić do niepotrzebnego nakładu pracy oraz tworzenia prostych lub słabo zaprojektowanych testów, które nie obejmują szczegółowo głównych przypadków użycia.
Samo pokrycie może być też przydatnym narzędziem podczas pisania testów lub pracy nad nimi, zwłaszcza w przypadku testów integracji. Wyświetlając wartości procentowe lub poszczególne wiersze kodu testowanego w pojedynczym teście, możesz dowiedzieć się, czego brakuje lub co można przetestować w następnej kolejności.
Zasoby
Sprawdź swoją wiedzę
Które z tych rodzajów testów są znanymi rodzajami testów?