W tym artykule odpowiemy sobie na pytanie jakie rodzaje testów powinniśmy wykonywać i w jakich proporcjach. Pomoże nam w tym piramida testów, czyli prosta graficzna reprezentacja ilości testów, kosztu ich utrzymania i szybkości wykonywania. Opiszemy również podstawowe cechy testów każdego poziomu i ich ograniczenia. Wbrew pozorom nie jest to tylko wiedza dla testerów, ale również dla developerów. Szczególnie jeżeli korzystają z Test Driven Development.
Czym jest piramida testów?
Piramida testów obrazuje nam jakie są rodzaje testów sprawdzających funkcjonalności aplikacji i jakie powinny być między nimi proporcje.
Piramidę testów można umieścić na układzie współrzędnych, gdzie osią poziomą jest ilość przypadków testowych (im dłuższy odcinek tym więcej), natomiast na osi pionowej czas wykonania i koszt utrzymania (im wyżej tym wolniej i drożej).
Rodzaje testów na piramidzie:
- Testy jednostkowe.
- Testy integracyjne.
- Testy systemowe (End to end).
Piramida mówi więc, iż w projekcie powinniśmy robić najwięcej testów jednostkowych, bo są najtańsze w utrzymaniu i wykonują się najszybciej. Testy systemowe są powolne i drogie, dlatego powinniśmy robić ich mało, a większość rzeczy sprawdzać na niższych poziomach.
Testy jednostkowe
Mają za zadanie sprawdzić mały fragment kodu. Może być pojedyncza klasa, metoda, czy funkcja. Może być też kilka małych klas/funkcji odpowiedzialnych wspólnie za jedno zadanie. Wykorzystują testy blackboxowe, aby sprawdzić, czy wymagania stawiane testowanej jednostce są spełnione, a także testy whiteboxowe, aby przetestować wszystkie ścieżki w kodzie. Skupiają się na wyłapaniu błędów logicznych w implementacji.
Testy jednostkowe są wykonywane w izolacji. Nie kompilujemy całego systemu, a jedynie potrzebny fragment kodu produkcyjnego. Niechciane zależności od pozostałych elementów zastępujemy mockami.
Wykonują się szybko. Pojedynczy test trwa kilka milisekund, a cały zestaw do kilku sekund. Dzięki temu dają szybki feedback i mogą być uruchamiane w trakcie developmentu. Dlatego najczęściej pisze je programista. Unit testy są najtańsze w utrzymaniu.
Testy integracyjne
Testuje interfejsy i interakcje. Mogą one zachodzić między wewnętrznymi modułami tworzonego systemu, albo ze światem zewnętrznym. Przykładami elementów sprawdzanych dzięki testów integracyjnych jest obsługa:
- sieci,
- baz danych,
- interakcji z OSem,
- zewnętrznych aplikacji i urządzeń.
Wyłapują błędy w zrozumieniu działania współpracujących elementów. Mogą również wyłapywać błędy logiczne, ale najlepiej jeżeli dzięki unit testom już wcześniej je ograniczyliśmy na tyle, iż możemy się skupić na rzeczach, których unit testy nie wykryją.
Sprawdza jednocześnie większą część aplikacji. Może być choćby cały system. Jednak zwykle wygodniej jest ograniczyć się do kilku elementów realizujących wspólne zadanie. Resztę mockujemy. Testy integracyjne mogą wymagać specjalnego środowiska testowego i konfiguracji. Wymagają zrozumienia działania większej części systemu. Przez to są cięższe w utrzymaniu od unit testów. Testy integracyjne również często są pisane przez developerów, ale mogą je pisać również testerzy.
Wykonują się na tyle długo, iż nie dostajemy od razu feedbacku. Pojedynczy test trwa zwykle kilka sekund, ale cały zestaw może się wykonywać już kilkadziesiąt minut.
Testy systemowe
Odbywają się na docelowym systemie w środowisku produkcyjnym. Cała aplikacja jest sprawdzana na raz. Testy systemowe mają za zadanie sprawdzić przejścia przez wszystkie warstwy aplikacji. Odpowiadają na pytanie, czy spełniono wszystkie wymagania wysokopoziomowe(testy blackbox) i czy działają wszystkie scenariusze wykorzystania przez użytkownika końcowego.
Mogą wyłapać nietrywialne błędy współpracy między modułami, niezrozumienie wymagań, czy problemy specyficzne dla środowiska produkcyjnego. Wykonują się powoli – kilka godzin, czy choćby kilka dni. Ciężko je zautomatyzować i mogą wymagać częstych modyfikacji. Przez to są ciężkie w utrzymaniu. Jednak są niezbędne, bo badają zachowanie kompletnej aplikacji. Zwykle piszą je testerzy.
Testy wszystkich poziomów są tak samo ważne
Każdy rodzaj testów skupia się na wykryciu innych problemów. Z pozostałymi błędami radzi sobie słabiej albo ich znalezienie nie jest w ogóle możliwe. Dlatego nie powinniśmy się ograniczać do jednego rodzaju testów. Niby da się zastąpić testy z dołu piramidy testami z góry, ale się nie opłaca ze względu na poziom skomplikowania, czas wykonywania i wrażliwość na zmiany. Dlatego jak się uprzemy, możemy robić tylko testy na całym systemie. Takie jest choćby klasyczne podejście do testowania – faza testów rozpoczyna się po ukończeniu developmentu i wszystkie testy są wykonywane na końcowym produkcie. Jednak jest to skrajnie nieefektywne. Chcemy sprawdzać jak najwięcej przypadków na niskich szczeblach, aby minimalizować koszty i przyspieszać feedback. Dlatego ilość testów poszczególnych warstw układa się w kształt piramidy.
Istnieją inne podejścia do ilości testów różnych poziomów – test ice cream, test hourglass (więcej tutaj). Często wynika to z niewiedzy, uprzedzeń, braku umiejętności i jest nieefektywne. Są jednak specyficzne systemy, gdzie zaburzenie kształtu piramidy jest uzasadnione. Np. UI dla zewnętrznego serwisu – nie ma logiki biznesowej, dlatego nie ma co unit testować, albo biblioteka – brak docelowego systemu, nie ma testów systemowych. Więcej na ten temat w tym artykule.
Inne rodzaje testów
Istnieją też inne rodzaje testów niezależne od piramidy. Skupiają się one na innych aspektach systemu i również powinny zostać przeprowadzone, o ile zależy nam na jakości, bezpieczeństwie i komforcie użytkowania. Przykłady innych testów to:
- testy eksploracyjne – doświadczony tester próbuje „wywalić” gotowy system.
- testy penetracyjne – sprawdzają podatność systemu na celowe próby przejęcia kontroli, wykradzenia danych, destabilizacji, czy wykorzystanie innych luk bezpieczeństwa.
- testy wydajnościowe (performance) – sprawdzają szybkość wykonywania, duże obciążenie itp.
- testy wytrzymałościowe (endurance) – sprawdzają zdolność do działania przez długi czas.
- testy kompatybilności – sprawdzają działanie z różnymi OSami, przeglądarkami, sprzętem itp.
Podsumowanie
Piramida testów podpowiada nam jakie powinny być proporcje między poszczególnymi rodzajami testów i dlaczego. Testy jednostkowe są tańsze w utrzymaniu i szybciej się wykonują. Chcemy więc za ich pomocą sprawdzać wszystko co możemy. Jednak nie wyłapią one błędów powstałych na skutek użycia wielu modułów, ani problemów specyficznych dla środowiska produkcyjnego. Wtedy musimy użyć testów integracyjnych i systemowych, które są droższe i wolniejsze. Najlepiej jeżeli przystępujemy do testów wyższych poziomów dopiero po wyeliminowaniu błędów logicznych przez unit testy. Oszczędzamy sobie w ten sposób wiele czasu. Tym bardziej, iż skomplikowane testy zwykle angażują więcej ludzi i sprzętu.