Test jednostkowy

Test jednostkowy () – metoda testowania tworzonego oprogramowania poprzez wykonywanie testów weryfikujących poprawność działania pojedynczych elementów (jednostek) programu – np. metod lub obiektów w programowaniu obiektowym lub procedur w programowaniu proceduralnym. Testowany fragment programu poddawany jest testowi, który wykonuje go i porównuje wynik (np. zwrócone wartości, stan obiektu, zgłoszone wyjątki) z oczekiwanymi wynikami – tak pozytywnymi, jak i negatywnymi (niepowodzenie działania kodu w określonych sytuacjach również może podlegać testowaniu).

Zaletą testów jednostkowych jest możliwość wykonywania na bieżąco w pełni zautomatyzowanych testów na modyfikowanych elementach programu, co umożliwia często wychwycenie błędu natychmiast po jego pojawieniu się i szybką jego lokalizację zanim dojdzie do wprowadzenia błędnego fragmentu do programu. Testy jednostkowe są również formą specyfikacji. Z powyższych powodów są szczególnie popularne w programowaniu ekstremalnym.

Podział testów jednostkowych 
Testy można podzielić na następujące warianty:
 analiza ścieżek
 użycie klas równoważności
 testowanie wartości brzegowych
 testowanie składniowe

Analiza ścieżek 
Podejście, w którym określamy punkt początkowy oraz punkt końcowy dla przeprowadzenia testów i badamy przebieg możliwych ścieżek pomiędzy nimi. Rozpatrywane możliwe ścieżki od punktu początkowego do punktu końcowego dzielimy na dwa podejścia:
 każda możliwa ścieżka w każdej funkcji została przetestowana
 ścieżki są niemożliwe do sprawdzenia z powodu istnienia pętli

Aby zapobiec niemożności sprawdzenia kodu z powodu pętli stosujemy dwie grupy testów:
 Boundary test – działania w pętli nie są wykonywane lub działania w każdej pętli są wykonywane raz dodatkowo wszystkie ścieżki wewnątrz pętli są raz wykonane.
 Interior test – działania we wnętrzu pętli uważa się za przetestowane, jeśli zostały wykonane wszystkie ścieżki, które są możliwe przy dwukrotnym powtórzeniu pętli.

Boundary Test: zwracane wartości to zero lub jedno przejście
Interior Test: zwracana wartość to dwa przejścia

Użycie klas równoważności 
Klasa równoważności jest to zbiór danych o podobnym sposobie przetwarzania, używanych do przeprowadzenia testu. Wykonanie testu z użyciem kilku elementów zbioru, powoduje uznanie całej klasy za poprawną i zwalnia nas od testowania wszystkich elementów w np. 1000-elementowym zbiorze.

Klasy równoważności dzielą się na:
 klasy poprawności – są to przypadki, dla których przewidujemy poprawne wykonanie programu,
 klasy niepoprawności – są to przypadki, dla których przewidujemy błędne wykonanie programu.

Przykłady:
 rejestracja osoby w wieku od 0 do 120 lat: przypadki testowe = {15, 18, 30, 60, 5}
 długość wiadomości od 1 do 50 znaków: przypadki testowe = {1, 2, 5, 8, 30, 45}
 napięcie od 0 do 100 V: przypadki testowe = {0, 1, 5, 24, 40, 80}

Testowanie wartości brzegowych 
Rozwinięciem testów z użyciem klas równoważności jest testowanie wartości brzegowych. Wartość brzegowa to wartość znajdująca się wewnątrz, pomiędzy lub tuż przy granicy danej klasy równoważności.

Przykłady:
 rejestracja osoby w przedziale wiekowym 0 – 120,
 testowane wartości brzegowe: {-1, 0, 1, 119, 120, 121}
 długość wiadomości od 1 do 50 znaków:
 testowane wartości brzegowe: {0, 1, 2, 49, 50, 51}
 napięcie od 0 do 100 V:
 testowane wartości brzegowe: {-1, 0, 1, 99, 100, 101}

Testowanie składniowe 
Podstawowym zadaniem testowania składniowego jest sprawdzenie poprawności wprowadzanych danych do systemu.

Błędy zależne od systemu i środowiska:
 wymuszone wartości pól (bazy danych)
 autokorekty (MS Office)

W testowaniu składniowym należy pamiętać o zasadzie "garbage in – garbage out", która zadziała gdy:
 brak mechanizmu walidacji danych na wejściu
 brak testów na tolerancje systemu na błędne dane

Przykładowe testy jednostkowe 
W przykładzie zostało przytoczone użycie środowiska JUnit wraz z dostarczonym mechanizmem testów jednostkowych.

Testy w przykładzie dotyczą niedeterministycznego automatu skończenie stanowego. W każdym teście,
oznaczonym adnotacją @Test tworzymy obiekt klasy DAUTAutomaton, następnie dodajemy stany 
automatu. Potem dodajemy przejścia wraz z etykietami, po których możemy udać się do określonego stanu.

Testy zaczynają się w momencie, gdy wywołujemy asercje dla żądanego napisu, aby sprawdzić czy napis jest akceptowany przez automat. Każda litera staje się etykietą przejścia od jednego stanu do drugiego i po przejrzeniu całego napisu musimy znaleźć się w tzw. stanie akceptującym.

       assertTrue(A.run("ab"));        // sprawdza czy dla napisu "ab" automat zwróci prawdę
       assertTrue(A.run("abbbbb"));    // sprawdza czy dla napisu "abbbbb" automat zwróci prawdę
       assertTrue(A.run("c"));         // sprawdza czy dla napisu "c" automat zwróci prawdę
       assertFalse(A.run("a"));        // sprawdza czy dla napisu "a" automat zwróci fałsz, czyli nie zaakceptuje tego napisu
       assertFalse(A.run("xxxxxxx"));  // sprawdza czy dla napisu "xxxxxxx" automat zwróci fałsz, czyli nie zaakceptuje tego napisu
       assertFalse(A.run("ba"));       // sprawdza czy dla napisu "ba" automat zwróci fałsz, czyli nie zaakceptuje tego napisu

Zobacz też 
 test-driven development
 programowanie ekstremalne
 regresja (informatyka)
 testowanie mutacyjne

Testowanie oprogramowania