Inzynieria programowania.Szmal.doc

(481 KB) Pobierz
INFORMATYKA z. 22Nr kol

Rozdział 1: Testowanie programów              13

rozdział 1

Testowanie programów

1.               Testowanie programów

1.1. Cel ćwiczenia

Celem ćwiczenia jest zapoznanie się z wybranymi metodami testo­wania oprogra­mo­wania i typowymi środkami pomocnymi w testowa­niu oraz wykorzystanie ich w praktyce, w wybranym systemie progra­mowania.

1.2. Podstawy teoretyczne

Każdy program powinien być przede wszystkim niezawodny. Pojęcie nieza­wodności można rozumieć, w pewnym uproszczeniu, jako brak po­datności na awarie oprogramowania. Przyjmijmy jako defini­cję awarii określenie sfor­muł­owane przez J. Goode­nougha:

Awaria to każdy efekt lub zacho­wanie, którego wystąpienie jest niedo­pu­szczal­ne w normalnych warunkach działania, gdzie to, co „niedo­pu­szczalne” i „nor­malne”, musi być określone dla każdego syste­mu.

Wówczas przykładami awarii mogą być błędne wyniki lub brak wyników, zmniejszenie efektywności programu (zaskakująco długie przetwarzanie dla pewnych danych) lub niszczenie danych użytkownika. Działania takie mogą być spowodowane błędami w strukturze programu lub niepoprawnym wypeł­nianiem specyfikacji.

Niezawodność łączy się ściśle z poprawnością programu. Najogólniej można po­wie­dzieć, że program jest poprawny, jeżeli zawsze działa zgodnie z sensownymi oczekiwaniami użytkownika i intencją programisty. Definicja taka jest bardzo ogólna i elastyczna, bo bazuje na pojęciach wysoce subiektywnych. Program prawidłowo zaprojektowany i napisany powinien być zgodny ze swoją specyfikacją. Należy więc upewnić się, czy badany program istotnie tę specyfikację spełnia. Z drugiej strony, czasami sama specyfikacja może nie obejmować pewnych sytuacji lub być niedoskonała albo niespójna w inny sposób, i wówczas w pełni z nią zgodny program także może ulec awarii. Należy więc sprawdzić i specyfikację.

1.3. Definicja testowania

Testowanie jest to zbiór czynności wykonywanych z intencją wykry­cia w programie  jak największej liczby błędów. Jednym z jego głównych składników jest obserwacja zgodności produkowanych przez program wyników z wcześniej przygotowanymi poprawnymi wyni­ka­mi odniesienia. Proces ten może prowa­dzić do uznania, że dany program działa poprawnie we wszystkich przypadkach, w których ma działać, lub do wykrycia błędu.

Celem testowania programu jest upewnienie się, że program rozwią­zuje to zadanie, do którego został zaprojektowany, i że w każdych warunkach daje poprawne wyniki. Jest to proces różny od procesu uruchamiania programu. Program uruchomiony nie zawiera błędów sygnalizowanych przez translator i jawnie niepoprawnych fragmentów kodu oraz daje jakieś wyniki. Czy wyniki te są poprawne, zgodne z oczekiwaniami ustalonymi w specyfikacji, pozwala ocenić tes­towanie.

Nie jest możliwe dokładne określenie, jak powinno przebiegać testowa­nie konkretnego programu. Można jednak sformułować kilka wskazó­wek dotyczących organizacji procesu testowania, które mogą pomóc w prawi­dłowym przetes­towa­niu programu.

Należy dążyć do:

· sprowokowania błędu, tzn. stworzenia sytuacji, w której błąd może się ujawn­ić (odpo­wie­dnie środowisko, dobrane dane testowe, przygotowane poprawne wyniki od­niesie­nia);

· sporządzenia dokładnego opisu okolicz­ności, które doprowadzi­ły do błędu, co umożliwi przejście do fazy uruchamiania w celu poszuki­wania przyczyny niewłaściwej pracy programu.

Określając granice procesu testowania trzeba wspomnieć o licz­bie wykonywanych testów. Chcąc starannie przetestować cały program, należałoby wykonać testowanie gruntowne. Polega ono na wykonaniu programu dla wszystkich możliwych kombinacji danych wejściowych. Jest to prawie zawsze niemożliwe ze względu na niezbędny w tym celu czas i wysiłek, choć byłby to jedyny sposób udowodnienia całkowitej poprawności programu. W praktyce, ze względu na efektywność testowa­nia, należy stosować możliwie małą, lecz dostateczną liczbę zestawów danych tes­towych, przy czym powinny być one dobie­rane z odrobiną wyobraźni i podej­rzli­wości w stosunku do działania programu. Mimo istnienia wielu formalnych metod, w praktyce programista powinien bazować w znacznym stopniu na swoim sprycie i intuicji.

Testując dany program nal­eży przeprowadzić dwa rodzaje testów. Po pier­wsze, trzeba przekonać się, czy program rea­lizuje właśnie to zada­nie, które zosta­ło sformułowane. Po wtóre, nale­ży uzyskać pewność, że działa poprawnie.

Możliwe są dwa skrajnie odmienne podejścia do testowania progra­mu: względem struktury wewnętrznej (kodu) i względem specyfikacji zewnętrznej.

Można jako podstawę w cza­sie testowania potraktować kod programu. Wtedy zabezpie­czymy się przed błędami wykonania (ang. run-time errors) i wszystkie instrukcje będą się wykonywały poprawnie, jednak nie wykryjemy faktu, że program jest niezgodny ze spe­cy­fi­kacją, tzn. robi nie to, co należało.

Jeśli zaś nie będziemy inte­resować się kodem, możemy testowaniem objąć jedynie spe­cyfika­cję. Wówczas mamy sytuację odwrotną – jeżeli umiejętnie dobierzemy dane testowe, możemy wy­kryć wiele błędów logicznych, jednak nie możemy gwarantować, że program nie zakończy się błę­dem wykonania. Wynika stąd, że dla właści­wego przetestowania programu konieczna jest znajo­mość i sprawdzenie zarówno specyfikacji, jak i kodu programu.

1.3.1. Testowanie struktury wewnętrznej

Przy testowaniu struktury wewnętrznej pojedynczego modułu (testowaniu względem kodu) konieczna jest znajomość tekstu źródłowego programu. Generalnie przyjmuje się, że warun­kami koniecznymi zakończenia testowania względem kodu są:

  1. Doprowadzenie do wykonania każdej instrukcji co najmniej raz, lub – równoważnie – do przejścia każdej z gałęzi przepływu sterowania w module. Chodzi o to, aby każdy wa­runek (punkt w programie, gdzie sterowanie rozgałęzia się, czyli zaczyna się nowa ga­łąź) podczas wykonywania testów przyjął wartość zarówno prawda, jak i fałsz. Trudno powiedzieć, ile razy należy powtórzyć wykonanie programu, aby powyższe warunki spełnić – zależy to od za­dania, które program wykonuje.
  2. Wykonanie wnętrza każdej pętli minimalną liczbę razy, małą liczbę razy oraz bardzo dużą (lub maksymalną, o ile można taką określić) liczbę razy. Osiąga się to przez dobór od­powiednich danych testowych.

Przygotowując dane testowe dla testowania względem kodu korzystamy z diagramów prze­pływu sterowania (DPS) i tworzonych na ich podstawie macierzy pokrycia gałęzi. Tech­nika posłu­giwania się nimi została przedstawiona w Dodatku A. Szerzej o zasadach doboru danych testowych traktuje punkt 1.6.

1.3.2. Testowanie specyfikacji zewnętrznej

W przypadku testowania specyfikacji zewnętrznej program traktujemy jako „czarną skrzynkę”. Znamy jedynie liczbę i rodzaj danych podawanych na wejściu, liczbę i rodzaj da­nych wyjściowych oraz zdefiniowaną w specyfikacji zależność pomiędzy danymi wejścio­wymi a wyjściowymi.

W tym przypadku dobór danych testowych, a szczególnie moment, w którym uznamy te­stowa­nie za zadowalające i wiarygodne, w jeszcze większym stopniu zależy od doświadcze­nia i intuicji testującego. Wskazówki na temat doboru danych testowych znajdują się w punkcie 1.6.

W czasie przygotowywania zestawów danych testowych należy brać pod uwagę:

· specyfikację zewnętrzną programu,

· funkcje realizowane przez program,

· strukturę programu,

· przepływ sterowania.

1.4. Testowanie programu

Testowanie programu trzeba rozpoczynać bardzo wcześnie, właściwie już na etapie projektowania. Należy przede wszystkim sprawdzić, czy poprawnie zaprojektowano rozwiąza­nie sformułowanego zadania, to znaczy, czy program będzie rzeczy­wiście rozwiązywał takie zadanie, jakie należy, i czy wzięto pod uwagę wszystkie przypadki bez niejednoznaczności. Można to zrobić jeszcze przed rozpo­częciem kodo­wania za pomocą „ręcznej symulacji” przygotowa­nego algorytmu. Pomocny tu jest udział klienta zlecającego wykonanie programu. Nazywamy to testowaniem specyfi­ka­cji lub projektu.

Testowanie projektu pozwoli na uniknięcie poważ­niejszych błędów wynika­jących z niejasności lub niekonsekwencji w sfor­mułowa­niu zadania i specyfi­ka­cji zewnętrznej programu.

W czasie pisania programu należy pamiętać, że będzie on testo­wa­ny. Struktura programu powinna być taka, by jego testowanie (także testo­wa­nie poszczególnych fragmentów) było łatwe. Jeśli dla wygody testowa­nia do pro­gramu źródłowego wstawia się dodatkowe instrukcje, to należy je odpo­wiednio skomen­to­wać. Wygodnie jest je umieszczać wewnątrz dyrektyw kompilacji warunkowej[1]. Po sprawdzeniu poprawności programu będzie je można bez kłopotu usu­nąć lub dezaktywować.

Testowanie modułów (procedur, fragmentów programu) przeprowadza się poje­dyn­czo, w środowisku nie korzystającym z innych modułów, także przed powsta­niem tych innych modułów. Jeżeli testowany moduł odwołuje się do modułów, które jeszcze nie istnieją, stosuje się symulację tych brakujących części systemu lub programu. Można tu wykorzystać moduły – makiety posiadające jedynie punkt wejścia i punkt po­wrotu oraz jakąś instrukcję sygnalizującą fakt wywołania modułu. Innym roz­wiąza­niem jest utworzenie modułu zastępcze­go realizujące­go w sposób uprosz­czony niezbędne do testowania funkcje.

Jeżeli testowaniu podlega oprogramowanie numeryczne, należy pamię­tać o sprawdzeniu zachowania się modułu w przypadkach:

· błędnych danych wejściowych (dla każdego modułu);

· ...

Zgłoś jeśli naruszono regulamin