Testowanie oprogramowania Przedstawienie metod, które można wykorzystać przy testowaniu programów w celu wykrycia usterek
Cele Poznać kilka metod testowania używanych w celu wykrywania usterek programów. Poznać porady dotyczące testowania interfejsów komponentów. Poznać specyficzne podejścia do testowania komponentów i testowania integracji systemów obiektowych. Poznać zasady działania narzędzi CASE wspomagających testowanie.
Zawartość Testowanie defektów Testowanie integracyjne Testowanie obiektowe Warsztaty do testowania
Fazy testowania Testowanie komponentów Testowanie integracyjne Twórca oprogramowania Zespół testujący integrację
Organizacja testowania W trakcie procesu planowania menedżerowie muszą podejmować decyzje o tym, kto jest odpowiedzialny za poszczególne fazy testowania. W większości wypadków programiści odpowiadają za testowanie własnego kodu. Po zakończeniu tych czynności do pracy przystępuje zespół integrujący, który łączy moduły różnych programistów, buduje oprogramowanie i testuje system jako całość. Testowanie integracyjne musi być oparte na pisemnej specyfikacji systemu.
Testowanie defektów i zatwierdzające Celem testowania defektów jest ujawnienie utajonych defektów w systemie oprogramowania przed dostarczeniem. Pozytywny test defektu to test powodujący, że system działa niepoprawnie i w ten sposób ujawniający defekt. Z tego wynika podstawowa właściwość testowania. Testowanie wykazuje obecność, a nie brak defektów programu. Testowania zatwierdzające ma wykazać, że system spełnia swoją specyfikację. Testowanie zatwierdzające służy do sprawdzenia za pomocą testów akceptacyjnych, czy system działa poprawnie.
Proces testowania defektów Przypadki testowe Dane testowe Raport z testowania Wyniki testów Porównaj wyniki z przypadkami testowymi Opracuj przypadki testowe Przygotuj dane testowe Uruchom program na danych testowych
Strategie testowania Należy przetestować wszystkie funkcje systemu dostępne z menu. Należy przetestować kombinacje funkcji (np. formatowanie tekstu) dostępnych z tego samego menu. Należy przetestować wszystkie funkcje, w których użytkownik wprowadza dane, zarówno na poprawnych, jak i niepoprawnych danych wejściowych.
Testowanie czarnej skrzynki Testowanie funkcjonalne lub inaczej testowanie czarnej skrzynki to podejście do testowania, przy którym testy wprowadza się ze specyfikacji programu lub komponentu. System jest „czarną skrzynką”, której zachowanie można ustalić jedynie na podstawie jego danych wejściowych związanych z nimi danych wyjściowych. Inną nazwą tego podejścia jest testowanie funkcjonalne, ponieważ osoba testująca zajmuje się jedynie funkcjonalnością, a nie implementacją oprogramowania.
Testowanie czarnej skrzynki Dane wejściowe powodujące anormalne zachowanie Testowe dane wejściowe Wejb System Dane wyjściowe, które umożliwiają wykrycie defektów Wyjb Wyniki wyjściowe testów a l h e p r e s e n c e o t f d e f e c t s
Dzielenie danych na klasy równoważności Dane wejściowe programu można zakwalifikować do kilku różnych klas. Te klasy określają wspólne właściwości, np. liczby dodatnie, liczby ujemne, napisy bez odstępów itd. Programy działają zwykle w porównywalny sposób dla wszystkich elementów jednej klasy. Ze względu na te równoważne zachowania, klasy te są czasem nazywane klasami równoważności lub dziedzinami. Jedno z systematycznych podejść do testowania defektów polega na zidentyfikowaniu wszystkich klas równoważności, które muszą być obsłużone przez program. Przypadki testowe projektuje się tak, aby dane wejściowe i wyjściowe leżały wewnątrz tych klas.
Klasy równoważności System Niedopuszczalne dane wejściowe Dopuszczalne Dane wyjściowe
Przykłady klas równoważności 3 11 4 7 10 Mniej niż 4 Między 4 i 10 Więcej niż 10 Liczba wartości wejściowych 9999 100000 10000 50000 99999 Mniej niż 10 000 Między 10 000 i 99 999 Więcej niż 99 999 Wartości wejściowe
Specyfikacja podprogramu wyszukującego zadany klucz procedure Szukaj (Klucz : ELEM ; T: SEQ of ELEM ; Znaleziono : in out BOOLEAN; L: in out POZ.ELEM) ; Pre-condition -- ciąg ma co najmniej jeden element T’FIRST <= T’LAST Post-condition -- znaleziono element i wskazuje go L ( Znaleziono and T (L) = Klucz) or -- element nie występuje w ciągu ( not Znaleziono and not (exists i, T’FIRST >= i <= T’LAST, T (i) = Klucz ))
Zbiór przypadków testowych dla podprogramu wyszukującego Tablica Element Jedna wartość Jest w ciągu Jedna wartość Nie ma jej w ciągu Więcej niż 1 wartość Jest pierwszym elementem w ciągu Więcej niż 1 wartość Jest ostatnim elementem w ciągu Więcej niż 1 wartość Jest środkowym elementem w ciągu Więcej niż 1 wartość Nie ma jej w ciągu Ciąg wejściowy (T) Klucz (Klucz) Wynik (Znaleziono, L) 17 17 true, 1 17 0 false, ?? 17,29,21,23 17 true, 1 41,18,9,31,30,16,45 45 true, 7 17,18,21,23,29,41,38 23 true, 4 21,23,29,33,38 25 false, ??
Testowanie strukturalne Testowanie strukturalne to podejście do testowania, przy którym testy opracowuje się na podstawie wiedzy o strukturze i implementacji oprogramowania. To podejście jest czasem nazywane testowaniem “białej skrzynki”, “szklanej skrzynki” lub “przezroczystej skrzynki” w odróżnieniu od testowania czarnej skrzynki. Testowanie strukturalne stosuje się zwykle do stosunkowo niewielkich jednostek programów, takich jak podprogramy i operacje związane z obiektem. Jak wskazuje nazwa, osoba testująca może analizować kod i korzystać z wiedzy o strukturze komponentu przy opracowywaniu danych testowych.
Testowanie strukturalne Dane testowe Testuje Jest podstawą opracowania Testowe dane wyjściowe Kod komponentu
Implementacja podprogramu wyszukującego binarnie w Javie Class WyszukiwanieBinarne { // Obudowywanie funkcji wyszukiwania binarnego, która pobiera uporządkowaną // tablicę obiektów oraz klucz i przekazuje obiekt z dwoma atrybutami, a dokładniej: // pozycja - numer pozycji w tablicy // znaleziono - wartość logiczna wskazująca, czy klucz znajduje się w tablicy, czy nie // Funkcja przekazuje obiekt, ponieważ w Javie nie ma możliwości przekazywania // do funkcji typów podstawowych przez odniesienie, a więc i przekazania // dwóch wartości // Przekazana pozycja ma wartość -1, jeśli elementu nie znaleziono public static void szukaj (int klucz, int [] tablica,Wynik w ) { int początek = 0 ; int koniec = tablica.length - 1 ; int środek ;
Implementacja podprogramu wyszukującego binarnie w Javie- c.d. w.znaleziono = false ; w.pozycja = -1 ; while ( początek <=koniec ) { środek = (koniec + początek ) / 2 ; if (tablica [środek] = = klucz) w.pozycja = środek ; w.znaleziono = true ; return ; } // gałąź if else if (tablica [środek] < klucz) początek = środek - 1; } } // pętla while } // szukaj } // Wyszukiwanie Binarne
Klasy równoważności wyszukiwania binarnego Granice klas równoważności Elementy < Środek Elementy > Środek Punkt środkowy
Przypadki testowe dla podprogramu wyszukującego binarnie Ciąg wejściowy Klucz (Klucz) Wynik (Znaleziono, L) 17 17 true, 1 17 0 false, ?? 17,21,23,29 17 true, 1 9,16,18,30,31,41,45 45 true, 7 17,18,21,23,29,38,41 23 true,4 17,18,21,23,29,33,38 21 true, 3 12,18,21,23,32 23 true, 4 21,23,29,33,38 25 false, ??
Testowanie ścieżek Testowanie ścieżek to strategia testowania strukturalnego, której celem jest zbadanie każdej niezależnej ścieżki wykonania komponentu lub programu. Jeśli wykonano każdą niezależną ścieżkę, to wszystkie instrukcje komponentu musiały być wykonane co najmniej raz. Co więcej, każdą instrukcję strukturalną przetestowano wtedy zarówno dla przypadku prawdy, jak i fałszu. W procesie tworzenia obiektowego testowanie ścieżek może służyć do testowania metod związanych z obiektami. Liczba ścieżek w programie jest zwykle proporcjonalna do jego rozmiaru. Testowanie ścieżek jest więc najczęściej stosowane w fazach testowania jednostkowego i testowania modułów.
Graf strumieni Przed przystąpieniem do testowania ścieżek należy opracować graf strumieni programu, który jest szkieletowym modelem wszystkich ścieżek w programie. Graf strumieni składa się z węzłów reprezentujących decyzje i krawędzi odpowiadających przepływom sterowania. Graf strumieni powstaje przez zastąpienie instrukcji sterujących programu równoważnymi diagramami. Jeśli w programie nie ma instrukcji skoku, to utworzenie jego grafu strumieni jest łatwym procesem. Budując graf strumieni, możemy pominąć instrukcje sekwencyjne (przypisania, wywołania procedur i instrukcje wejścia-wyjścia). Każda gałąź instrukcji warunkowej (if-then-else lub case) jest przedstawiona w postaci odrębnej ścieżki. Pętle obrazuje się za pomocą strzałki wstecznie wskazującej węzeł warunku pętli.
Graf strumieni podprogramu wyszukującego binarnie 1 while początek <= koniec początek > koniec 2 if (tablica [środek] = = klucz 3 8 4 if (tablica [środek] < klucz 6 5 9 7
Testowanie integracyjne Po odrębnym przetestowaniu komponentów programy należy je zintegrować w gotowy lub częściowo ukończony system. Ten proces integracji polega na zbudowaniu systemu i przetestowaniu otrzymanego systemu w poszukiwaniu problemów związanych z interakcjami komponentów. Testy integracyjne należy opracować na podstawie specyfikacji systemu i należy je rozpocząć natychmiast po powstaniu zdatnych do użycia wersji komponentów systemu. Największą trudnością testowania integracyjnego jest lokalizowanie błędów wykrytych w trakcie tego procesu. Aby ułatwić ową lokalizację błędu, zawsze należy stosować przyrostowe podejście do interakcji i testowania systemu.
Przyrostowe testowanie integracyjne 3 2 1 4 5 A B C D T 1 A T 2 B T 3 Zestaw testów nr 1 Zestaw testów nr 2 Zestaw testów nr 3
Testowanie zstępujące i wstępujące Strategie testowania zstępująca i wstępująca odzwierciedlają różne podejścia do integracji systemu. Przy integracji zstępującej integruje i testuje się komponenty systemu wysokiego poziomu przed ukończeniem ich projektu i implementacji. Przy integracji wstępującej integruje i testuje się komponenty niskiego poziomu przed ukończeniem budowy komponentów wyższego poziomu.
Testowanie zstępujące Jest integralną częścią zstępującego procesu budowania, który rozpoczyna się od komponentów wysokiego poziomu i polega na przejściu w dół hierarchii komponentów. Program jest reprezentowany przez pojedynczy abstrakcyjny komponent z komponentami podrzędnymi w postaci namiastek. Namiastka ma taki sam interfejs jak komponent, ale jej funkcjonalność jest bardo ograniczona. Po zaprogramowaniu i przetestowaniu komponentu najwyższego poziomu w ten sam sposób implementuje i testuje się jego komponenty podrzędne. Ten proces jest kontynuowany do chwili zaimplementowania komponentów najniższego poziomu. Wówczas cały system jest już w pełni przetestowany.
Testowanie wstępujące Polega natomiast na integrowaniu i testowaniu modułów niższego poziomu hierarchii, następnie przejściu w górę hierarchii modułów i przetestowaniu ostatecznego modułu. Przy tym podejściu do rozpoczęcia prac nie jest konieczne ukończenie projektu architektonicznego systemu. Do procesu testowania można więc przystąpić już we wczesnej fazie tworzenia. To podejście może być zastosowane tam, gdzie w systemie ponownie wykorzystuje się i modyfikuje komponenty innych systemów.
Zstępujące testowanie integracyjne ... Poziom 1 Kolejność testowania Poziom 1 Poziom 2 Poziom 2 Poziom 2 Poziom 2 Namiastki poziomu 2 Namiastki poziomu 3
Zstępujące testowanie integracyjne Sterowniki testowania Poziom N Poziom N Poziom N Poziom N Poziom N Kolejność testowania Sterowniki testowania Poziom N-1 Poziom N-1 Poziom N-1
Testowanie interfejsu Testowanie interfejsu jest wykonywane po zintegrowaniu modułów lub podsystemów w większe systemy. Każdy moduł i podsystem ma zdefiniowany interfejs, który jest wywoływany przez inne komponenty programu. Celem testowania interfejsu jest wykrycie usterek, które pojawiły się w systemie z powodu błędów w interfejsach lub nieprawdziwych założeniach o interfejsach.
Schemat testowanie interfejsu Przypadki testowe A B C
Rodzaje interfejsów Interfejsy parametryczne. Są to interfejsy, w których dane lub niekiedy odwołania do funkcji są przekazywane od jednego komponentu do drugiego. Interfejs w pamięci dzielonej. Są to interfejsy, w których blok pamięci jest dzielony między podsystemami. Dane są umieszczane w tym bloku przez jeden podsystem i pobierane stamtąd przez inny. Interfejsy proceduralne. Są to interfejsy, w których jeden podsystem obudowuje zbiór procedur wywoływanych przez inne podsystemy. Interfejsy tego rodzaju mają obiekty i abstrakcyjne typy danych. Interfejsy z przekazywaniem komunikatów. Są to interfejsy, w których jeden podsystem żąda usługi innego przez przesłanie mu komunikatu. Komunikat zwrotny zawiera wyniki wykonania usługi. Niektóre systemy obiektowe i systemy klient-serwer mają interfejsy tego rodzaju.
Testowanie obciążeniowe Po całkowitej integracji systemu można badać pojawiające się właściwości systemu, takie jak efektywność i niezawodność. Testy efektywności projektuje się tak, aby upewnić się, że system może działać przy założonym obciążeniu. Zwykle polega to na zaplanowaniu szeregu testów, w których stopniowo zwiększa się obciążenie do chwili osiągnięcia akceptowalnej efektywności systemu.
Testowanie obiektowe Różnice miedzy systemami obiektowymi a systemami zbudowanymi za pomocą modelu funkjonalnego: obiekty jako oddzielne komponenty są zwykle większe niż poszczególne funkcje obiekty integrowane w podsystemy są zwykle luźno powiązane i nie ma jasnego ‘wierzchołka’ systemu jeśli obiekty użyto wielokrotnie, to osoby testujące mogą nie mieć dostępu do kodu źródłowego komponentu, a zatem nie mogą przeprowadzić jego analizy.
Poziomy testowania obiektowego Testowanie poszczególnych opcji związanych z obiektami. Są to funkcje i procedury. Można wykorzystać podejścia zarówno czarnej, jak i białej skrzynki. Testowanie poszczególnych klas obiektów. Zasada testowania czarnej skrzynki pozostaje w mocy, ale pojęcie klas równoważności należy rozszerzyć tak, aby obejmowało ciągi powiązanych operacji. Testowanie gron obiektów. Ściśle zstępująca ani wstępująca integracja nie jest właściwa w wypadku grup powiązanych obiektów. Należy wykorzystać inne podejścia, takie jak testowanie scenariuszy. Testowanie systemu obiektowego. Przeprowadza się weryfikację i zatwierdzanie względem specyfikacji wymagań systemu w dokładnie ten sam sposób, jak w wypadku innych rodzajów systemów.
Testowanie klas obiektów Podejście do pokrywania testami polega na upewnieniu się, że wszystkie instrukcje i wszystkie ścieżki programu wykonano co najmniej jeden raz. Przy testowaniu obiektów pełne pokrycie testami powinno obejmować: oddzielne testowanie wszystkich operacji związanych z obiektem ustalenie i odczytywanie wszystkich atrybutów związanych z obiektem badanie obiektu we wszystkich możliwych stanach, co oznacza, że należy zasymulować wszystkie zdarzenia powodujące zmianę stanu obiektu.
Przykład stacji meteorologicznej Zawiera on tylko jeden atrybut, który jest identyfikatorem stacji. Jest to stała ustalana w czasie instalowania stacji. Trzeba więc jedynie sprawdzić, czy go nadano. Należy opracować przypadki testowe dla metod raportPogodowy, dostrój, testuj, uruchom i wyłącz. Najlepiej testować te metody w izolacji, ale w niektórych wypadkach konieczne są pewne ciągi testów. Sprawdzanie metody wewnątrz wymaga na przykład wcześniejszego wykonania metody uruchom.
Interfejs stacji meteorologicznej StacjaMeteorologiczna identyfikator raportPogodowy () dostrój (przyrządy) testuj() uruchom (przyrządy) wyłącz (przyrządy)
Testowanie gron obiektów Przy budowaniu systemów obiektowych poziomy integracji są słabiej wyodrębnione. Oczywiście operacje i dane są integrowane w postaci obiektów i klas obiektów. Testowanie tych klas obiektów odpowiada testowaniu jednostek. W systemach obiektowych nie ma bezpośredniego odpowiednika testowania modułów. Murphy i inni (1994) sugerują jednak, że grupy klas, które łącznie oferują pewien zbiór usług, powinny być testowane razem. Nazwali to testowaniem gron.
Podejścia do testowania integracyjnego obiektów Testowanie przypadków użycia lub scenariuszy. Przypadki użycia lub scenariusze są opisami jednego trybu użycia systemu. Podstawą testowania mogą być te opisy scenariuszy i grona obiektów utworzone w celu realizacji przypadków użycia związanych z tym trybem użycia. Testowanie wątków. Polega na testowaniu reakcji systemu na konkretne zdarzenie wejściowe lub zbiór zdarzeń wejściowych. Systemy obiektowe często są sterowane zdarzeniami, a zatem jest to szczególnie dobry sposób testowania. Testowanie interakcji obiektów. Podejście do testowania grup oddziałujących na siebie obiektów.
Testowanie scenariuszy Często jest najskuteczniejszą z tych metod, ponieważ można je przeprowadzić tak, aby zacząć od najczęściej realizowanych scenariuszy, a niecodzienne i wyjątkowe scenariusze wziąć pod uwagę w późniejszej fazie procesu testowania. To czyni zadość podstawowej zasadzie testowania, która nakazuje poświęcenie największego wysiłku najczęściej używanym częściom systemu.
Przebieg gromadzenia danych meteorologicznych : Sterownik Komunikacji : Stacja Meteorologiczna : Dane Meteorologiczne żądanie (raport) potwierdzenie () raportuj () podsumuj () wyślij (raport) odpowiedź (raport) potwierdzenie ()
Warsztaty do testowania Testowanie to kosztowna i pracochłonna do testowania tworzenia oprogramowania. Narzędzia do testowania były więc pierwszymi narzędziami programowymi, które należało opracować. Te narzędzia zawierzają obecnie wiele udogodnień. Ich użycie znacząco obniża koszt procesu testowania. Różne narzędzia do testowania można zintegrować w ramach warsztatów do testowania.
Narzędzia warsztatów do testowania Menedżer testów Generator danych testowych Wyrocznia, czyli generator spodziewanych wyników Narzędzia do porównywania plików Generator raportów Analizator dynamiczny Symulator
Warsztaty do testowania Generator danych testowych Specyfikacja Kod źródłowy Menedżer testów Wyrocznia Dane testowe Analizator dynamiczny Spodziewane wyniki Testowany program Wyniki testów Narzędzia do porównywania plików Raport z wykonania Symulator Generator raportów Raport z wynikami testów
Główne tezy Przetestowanie często używanych części systemu jest ważniejsze niż testowanie tych części, które są rzadko stosowane. Dzielenie na klasy równoważności jest sposobem opracowywania przypadków testowych. Polega na znajdowaniu podziału zestawów danych wejściowych i danych wyjściowych i zadawaniu programowi wartości z fragmentów tego podziału. Często wartością, która z największym prawdopodobieństwem da pozytywny wynik testu, jest wartość graniczna fragmentu podziału. Testowanie czarnej skrzynki nie wymaga dostępu do kodu źródłowego. Przypadki testowe opracowuje się na podstawie specyfikacji programu. Testowanie strukturalne polega na analizowaniu programu w celu odnalezienia jego ścieżek i wykorzystaniu wyników tej analizy przy wyborze wypadków przypadków testowych.
Główne tezy Testowanie integracyjne powinno być poświęcone testowaniu interakcji między komponentami systemu i interfejsami komponentów. Defekty interfejsów mogą powstawać w wyniku błędów przy odczytywaniu specyfikacji, w wyniku niewłaściwego zrozumienia specyfikacji lub nieprawdziwych założeń o synchronizacji. Celem testowania interfejsów jest wykrycie defektów w interfejsach obiektów lub modułów. Testując klasy obiektów, należy opracować testy, które obejmują wykonanie wszystkich metod klasy, przypisanie i odczyt wartości wszystkich atrybutów i umożliwiają sprawdzenie obiektowe we wszystkich możliwych stanach. Systemy obiektowe należy integrować wokół naturalnych gron obiektów, takich jak te związane z konkretnym przypadkiem użycia, zbiorem przypadków użycia lub wątkami obiektów.