Podręcznik
2. Podstawy testowania
2.1. Rola testowania w inżynierii oprogramowania
Testowanie oprogramowania jest dyscypliną inżynierii oprogramowania zajmującą się określaniem metod oraz przeprowadzaniem badania poprawności działania oprogramowania. Na przestrzeni lat rozwinięto wiele metod, które pozwalają zespołom testującym nadążyć za coraz szybciej powstającymi programami, a także opanować coraz bardziej złożone interakcje między systemami informatycznymi.
Podczas powstawania systemu informatycznego konieczne jest stwierdzenie, czy jego działanie nie wykazuje odstępstw od pewnych narzuconych warunków. Warunki te powstają poprzez analizę potrzeb klienta oraz określenie możliwości zespołu tworzącego program. Odstępstwa od tak ustalonej specyfikacji nazywa się błędami oprogramowania i obejmują one takie sytuacje jak:
- oprogramowanie nie wykonuje czegoś, co powinno wykonywać,
- oprogramowanie robi coś, czego nie powinno robić,
- oprogramowanie nie wypełnia warunków dotyczących cech pozafunkcjonalnych,
- oprogramowanie zachowuje się w sposób nie przewidziany specyfikacją.
Istotną część błędów stanowią tzw. pluskwy (ang. bug) – są to usterki umiejscowione w kodzie programu i wynikające ze zmęczenia, przeoczeń bądź nieuwagi programistów. Celem procesu testowania oprogramowania jest możliwie wczesne znajdowanie nieprawidłowości działania aplikacji, a także odpowiednie opisywanie ich w celu ułatwienia programistom dokonania poprawek. W procesie testowania tworzy się dane testowe wykorzystywane podczas badania reakcji systemu na sygnały wejściowe. Zbiór danych testowych powinien być odpowiednio duży oraz zróżnicowany.
Nie istnieje jednoznaczna miara, pozwalająca ocenić skuteczność przyjętych strategii testowania. Jednym z podejść do problemu weryfikacji procesu testowania jest badanie pokrycia testami zbioru przetwarzanych przez system danych oraz kodu wykonywanego podczas uruchamiania testów. Miara pokrycia kodu i danych pozwala ocenić, które fragmenty kodu (linie, instrukcje) zostały „zbadane” oraz jaki zakres danych został wykorzystany przy weryfikacji systemu.
Inną miarą jest szacunkowe określenie liczby błędów w systemie i liczby błędów wykrytych. Można w tym celu stosować metodę posiewową. Technika ta polega na tym, że do kodu programu wprowadza się celowo pewną liczbę sztucznie wytworzonych błędów. Błędy te powinny być podobne do tych prawdziwych, gdyż w przeciwnym razie miara będzie mało dokładna. Następnie zespół testujący, nieświadomy umieszczonych błędów, jest oceniany poprzez odpowiednie oszacowania bazujące na liczbie wykrytych przez niego usterek.
Jeżeli:
B – liczba wykrytych błędów
b – liczba posianych błędów, które zostały wykryte
P – liczba posianych błędów
to szacunkowa liczba błędów przed wykonaniem testów wynosi
a szacunkowa liczba błędów pozostałych po wykonaniu testów wynosi
2.2. Podstawowe metody testowania
Popularne obecnie techniki testowania można podzielić na metody nieinwazyjne (metody czarnej skrzynki), metody związane z analizą powstającego kodu (metoda szklanej skrzynki) oraz metody analizy specyfikacji (testy statyczne).
Najczęściej używanymi metodami testowania są metody kierujące się zasadą testów czarnej skrzynki (ang. black box testing). Polegają one na sprawdzaniu działania programu (ogólnie: systemu informatycznego), bazującym całkowicie na monitorowaniu danych wejściowych i wynikowych oraz porównywaniu ich z oczekiwaniami (założeniami). Podczas takich testów nie wnika się w strukturę kodu programu, co oznacza również działanie nieinwazyjne (np. nie dokonuje się zmian kodu w celu wykonania testów). Testowanie tego typu obejmuje szereg różnych rodzajów testów związanych z funkcjonalnością oprogramowania. Jest to zatem metoda testowania dynamicznego.
By przeprowadzić udany test metodą czarnej skrzynki można wykorzystać pewne techniki weryfikacji oprogramowania bazujące na zasadach nieingerencji i nieznajomości kod programu. Należą do nich metody:
- tworzenia klas równoważności,
- warunków granicznych,
- zmian stanów,
- niedoświadczonego użytkownika.
Stosowanie metody klas równoważności wynika z podstawowej potrzeby prześledzenia wszystkich możliwych stanów w systemach komputerowych. W dowolnym, nawet najprostszym procesie informatycznym, liczba możliwych stanów, danych wejściowych, konfiguracji sprzętu, zachowań użytkownika jest ogromna, a z punktu widzenia ograniczonego czasem i funduszami zespołu programistycznego – wręcz nieskończona. Z drugiej strony, podczas badania jakości oprogramowania niezbędne jest przetestowanie jak najszerszego spektrum możliwych sytuacji, z jakimi będzie miał do czynienia sprawdzany program. Rozwiązaniem powyższego konfliktu jest metoda klas równoważności, która pomaga w wyborze odpowiednich zadań testowych, ograniczając liczbę takich zadań, nie zmniejszając jednocześnie skuteczności testów.
Klasą równoważności nazywa się zbiór zadań testowych, które testują to samo lub ujawniają te same błędy. Głównym zagadnieniem podczas testowania metodą klas równoważności jest identyfikacja klas równoważności, czyli taki podział zagadnień testowych, który powala uzyskać odpowiednie pokrycie testowanego oprogramowania i jednocześnie daje zastosować się w praktyce. Identyfikację klas równoważności można przeprowadzać ze względu na podobne dane wejściowe, podobne stany programu, podobne dane oczekiwane, podobne zachowania użytkownika itp.
Podczas podziału na klasy równoważności często ujawnia się problem warunków granicznych, zasadzający się na dylemacie jednoznacznego zaliczenia danego zadania testowego do jednej z kilku grup, które „zbiegają się” lub „stykają” w danym punkcie. Wtedy metoda klas równoważności zaczyna uzupełniać się z metodą warunków granicznych.
Testowanie warunków granicznych można również nazwać „praktycznym zastosowaniem klas równoważności dla testowania danych”. Ideą tej metody jest określenie przedziałów, jakie mogą przyjmować dane wejściowe oraz sprawdzenie zachowania się programu dla danych z okolicy granic tych przedziałów.
Przeciwwagą dla metod czarnej skrzynki są metody „szklanej (białej) skrzynki” (ang. glass-, white-box testing). Wykorzystują one wiedzę o kodzie programu w celu podniesienia skuteczności testów. Testy bazujące na wglądzie w strukturę programu polegają przede wszystkim na badaniu kodu. Testerzy zadają odpowiednie dane wejściowe, aby zbadać różne ścieżki działania kodu i określić przewidywane wyniki na wyjściu. Tego typu badania mogą być wspomagane poprzez formalne i nieformalne przeglądy kodu oraz poprzez analizę raportów narzędzi – analizatorów kodu.
W testach białej skrzynki wykorzystywane jest kilka technik, zapewniających badanie różnych aspektów działania kodu. Technika pokrycia instrukcji polega na przejściu podczas testów przez wszystkie instrukcje testowanego fragmentu kodu przynajmniej raz. Podobną techniką jest technika pokrycia gałęzi. Polega ona przejściu podczas testów chociaż raz wszystkimi możliwymi gałęziami wychodzącymi z punktów decyzyjnych (np. instrukcji warunkowych).
Kolejną techniką testów białej skrzynki jest technika pokrycia warunków. Polega ona na sprawdzeniu działania kodu dla różnych kombinacji warunków w instrukcjach warunkowych. Bardzo ogólną techniką testowania metodą białej skrzynki jest technika ścieżek bazowych. W ramach tej techniki należy określić tzw. złożoność cykliczną (cyklomatyczną) testowanego fragmentu kodu (procedury). Złożoność tą określamy na podstawie grafu przepływu sterowania. W grafie takim węzły odpowiadają instrukcjom, a krawędzie określają kolejność wykonywania tych instrukcji (łącznie z określeniem alternatywnych przebiegów). Złożoność cykliczną liczymy na podstawie liczby węzłów decyzyjnych, węzłów złączenia oraz regionów (obszarów zamkniętych krawędziami łączącymi węzły). Złożoność cykliczna determinuje liczbę niezależnych ścieżek wykonania kodu. Na tej podstawie możemy określić liczbę testów niezbędnych do pokrycia wszystkich instrukcji w danym fragmencie kodu.
2.3. Poziomy testowania
Najbardziej szczegółowym rodzajem testów są testy jednostkowe. Dotyczą one kodu systemu i są tworzone bezpośrednio przez programistów go piszących. Rolą testów jednostkowych jest sprawdzenie działania elementarnych składników kodu, np. klas oraz ich metod. Sprawdzana jest logika działania procedury lub funkcji poprzez zadanie danych wejściowych (parametrów) i sprawdzenie, czy dane wyjściowe (wyniki) są zgodne z oczekiwaniami. Dobrze przygotowane testy jednostkowe pozwalają również na przeprowadzanie testów regresyjnych. Są to testy powtarzane w identyczny sposób w kolejnych iteracjach projektu, mające na celu sprawdzenie, czy wprowadzone w kodzie zmiany (np. optymalizacja, refaktoryzacja) nie spowodowały powstania nowych błędów.
Testy jednostkowe dążą do sprawdzania działania rozpatrywanych elementów niezależnie od innych z nimi powiązanymi. Jeśli dany element wymaga do działania innych elementów, stosuje się tzw. zaślepki, które imitują działanie tych elementów. Do tworzenia zaślepek na potrzeby testów jednostkowych również istnieje wiele dedykowanych bibliotek, które ułatwiają ten proces.
Testy kodu w których zaczynamy testować większą liczbę współpracujących ze sobą elementów naraz (np. cały komponent) możemy nazwać testami modułowymi, które stanowią wyższy poziom testów jednostkowych. Wykonywane są one również przez programistów, najczęściej z wykorzystaniem tych samych narzędzi. Rozpoczynają jednak już one kolejną kategorię testów, nazywanych testami integracyjnymi, których rolą jest sprawdzanie poprawnej współpracy wielu różnych elementów. W skład tej kategorii wchodzą również testy współdziałania i testy systemowe. Pierwsze z nich sprawdzają współdziałanie par współpracujących ze sobą elementów. Nacisk kładziony jest na sprawdzenie poprawności komunikacji poprzez wcześniej ustalone interfejsy. Drugie skupiają się na testowaniu poprawnej współpracy wszystkich elementów systemu koniecznych do dostarczenia poszczególnych funkcjonalności.
Ostatni rodzaj testów stanowią testy akceptacyjne, które mają dowieść zgodności systemu z wymaganiami, co pozwoli na jego ostateczny odbiór przez klienta. Typowo dokonywane są one w oparciu o scenariusze przypadków użycia uzupełnione o odpowiednie dane testowe. W testach akceptacyjnych biorą często udział przedstawiciele klienta lub przyszli użytkownicy systemu.