Kategoria

Ron patton software testing, strona 7


rozdział 7
08 września 2019, 21:02

Rozdział 7 Testowanie pod rentgenem
W TYM ROZDZIALE Dynamiczne testowanie metodami szklanej skrzynki Dynamiczne testowanie a lokalizowanie i usuwanie błędów Test komponentów Pokrycie danych Pokrycie kodu
Jak dotąd w części II-iej poznaliśmy trzy spośród czterech podstawowych technik testowania: statyczne testowanie metodami czarnej skrzynki (testowanie specyfikacji), dynamiczne testowanie metodami czarnej skrzynki (testowanie oprogramowania) i statyczne testowanie metodami szklanej skrzynki (badanie i analiza kodu źródłowego). W tym rozdziale zapoznamy się z czwartą podstawową techniką – dynamicznym testowaniem metodami szklanej skrzynki. Testując oprogramowanie, będziemy zaglądać do wnętrza „skrzynki” jakby przy pomocy rentgena.
Oprócz rentgenowskich okularów, trzeba też będzie wystąpić pod flagą programisty – o ile ma się do tego kompetencje. Jeśli komuś brak znajomości programowania, nie trzeba się zrażać. Podane w rozdziale przykłady nie są zanadto skomplikowane i przyłożywszy się, każdy jest w stanie je śledzić. Zrozumiawszy choć w części tę grupę technik testowych, można stać się o wiele skuteczniejszym testerm.
Jeśli ma się pewne doświadczenie w programowaniu, ten rozdział otwiera bardzo szerokie możliwości. Większość firm produkujących oprogramowanie zatrudnia testerów właśnie do testowanie na niskim poziomie. Poszukiwane są osoby mające umiejętności zarówno w programowaniu jak i w testowaniu, co jest rzadkim i często poszukiwanym połączeniem.
Najważniejsze punkty tego rozdziału: 157.Czym jest dynamiczne testowanie metodami szklanej skrzynki 158.Różnica między dynamicznym testowaniem metodami szklanej skrzynki a usuwaniem błędów z programu 159.Co to jest testowanie jednostkowe i testowanie integracyjne 160.Jak testować funkcje na niskim poziomie 161.Rodzaje danych które trzeba testować na niskim poziomie
162.Jak zmuszać program do wymaganego sposobu działania 163.Jakimi różnymi metodami można zmierzyć staranność testowania Dynamiczne testowanie metodami szklanej skrzynki
Znamy już na pewno bardzo dobrze pojęcia statyczny, dynamiczny, metody szklanej skrzynki i metody czarnej skrzynki, tak że wiedząc, że ten rozdział zajmie się testowaniem dynamicznym metodami szklanej skrzynki, powinniśmy się łatwo domyśleć jego treści. Skoro jest dynamiczne, to znaczy że program musi być uruchamiany, a skoro jest metodami szklanej skrzynki, to znaczy że zagląda się „do środka pudełka”, analizuje kod i obserwuje jak jest wykonywany. Testuje się jakby z rentgenem na oczach.
Dynamiczne testowanie metodami szklanej skrzynki posiłkuje się informacją zdobytą przez obserwowanie chodzącego kodu. Ta informacja jest wykorzystywana aby zadecydować co testować, czego nie testować, i w jaki sposób podejść do testowania. Inną powszechnie stosowaną nazwą na dynamiczne testowanie metodami szklanej skrzynki jest testowanie strukturalne, ponieważ widzi się przy nim i wykorzystuje strukturę kodu do konstruowania i do wykonywania zadań testowych.
Jakie pożytki mogą wynikać ze znajomości tego, co dzieje się „wewnątrz pudełka”, jak działa oprogramowanie? Spójrzmy na rysunek 7.1. Pokazane są na nim dwa pudełka, realizujące podstawowe operacje arytmetyczne: dodawanie, odejmowanie, mnożenie i dzielenie.
Rysunek 7.1 Wybrałoby się inne zadania testowe wiedząc, że pudełku jest komputer i inne, jeśli w pudełku siedziałby człowiek z papierem i ołówkiem.
Jeśli nie wie się, jak działa pudełko, wybrałoby się dynamiczne techniki czarnej skrzynki, które zostały opisane w rozdziale 5-ym, „Testowanie z klapkami na oczach”. Gdyby jednak zajrzeć do pudełek i zobaczyć, że jedno z nich zawiera komputer, a w drugim ukrywa się człowiek z ołówkiem i papierem, wybrałoby się przypuszczalnie dla nich zupełnie odmienne zestawy testów. Oczywiście, to bardzo uproszczony przykład, ale dobrze ilustruje jak znajomość sposobu działania programu wpływa na sposób testowania.
Dynamiczne testowanie metodami szklanej skrzynki oznacza więcej niz tylko oglądanie pracy kodu, może również oznaczać bezpośrednie testowanie i kontrolę oprogramowania. Dynamiczne testowanie metodami szklanej skrzynki obejmuje cztery obszary: 164.Bezpośrednie testowanie na niskim poziomie funkcji, procedur, rutyn i bibliotek. W Windowsach nazywane one są zwykle interfejsem programistycznym (API). 165.Testowanie oprogramowania na najwyższym poziomie, jako gotowy program, z tym że zadania testowe oparte są rónież na tym, co się wie o sposobach jego działania. 166.Uzyskanie dostępu do do zmiennych w kodzie i do informacji o jego stanie ułatwi sprzwdzenie, czy testy są właściwie skonstruowane. Również daje to możliwośc „zmuszenia” programu do innego działania , niż dałoby się osiągnąć testując tylko technikami czarnej skrzynki. 167.Pomiary, jaka część kodu i specyfikacji została rzeczywiście przetesowana – i uzupełnienie zestawu zadań testowych o nowe, jeśli nie idało się osiągnąć pożądanego pokrycia kodu.
Każdy z tych czterech obszarów będzie przedyskutowany w pozostałej części rozdziału. Warto to wziąć pod uwagę czytając dalej i zastanowić się, na ile te techniki przydałyby się do testowania programów. Dynamiczne testowanie a lokalizowanie i usuwanie będów
Nie należy mieszać dynamicznego testowania metodami szklanej skrzynki z lokalizacją i usuwaniem błędów. Kto zajmował się programowaniem, na pewno spędził wiele godzin lokalizując i usuwając błędy w napisanym przez siebie kodzie. Obie czynności wydaja się podobne, ponieważ obie zajmują się błędami w oprogramowaniu i obie „grzebią się” w kodzie, ale ich cele są zupełnie odmienne (rysunek 7.2).
Rysunek 7.2 Dynamiczne testowanie metodami szklanej skrzynki oraz lokalizowanie i usuwanie błędów mają różne cele, ale zazębiają się.
Celem testowania jest znajdowanie błędów. Celem lokalizacji i usuwania błędów jest ich likwidacja. Te rejony zazębiają się tam, gdzie staramy się błąd wyizolowac i zidentyfikować jego przyczynę. Dowiemy się na ten temat więcej w rozdziale 18-ym „Raportowanie wyników”, ale na razie przyjmijmy uproszczone wytłumaczenie. Tester oprogramowania ma za zadanie zawęzic problem do naprostszego możliwego testu, który wywołuje symptom poszukiwanego błędu. Testując metodami szklanej skrzynki, możemy często nawet zidentyfikować ”podejrzany” fragment kodu. Programista zajmujący się lokalizowaniem i usuwaniem błędu przejmuje pałeczkę w tym właśnie miejscu, ustala dokładnie przyczynę błędu i usiłuje ją usunąć.
Ważne jest umieć rozdzielić w praktyce pracę testera i programisty. Programiści piszą kod, testerzy szukają i znajdują błędy i czasem piszą nieco kodu do automatyzacji testów; programiści poprawiają błędy. Bez wyraźnego podziału, pewne zadania mogą zostać przez obie strony pominięte, albo – przeciwnie – jakaś praca zostanie wykonana dwa razy.
Testując na niskim poziomie w pobliżu kodu, używa się wielu tych samych narzędzi co programiści. Jeśli program jest kompilowany, tester również go kompiluje, ewentualnie z innymi ustawieniami dla umożliwienia łatwiejszego wykrywania błędów. Tester używa też prawdopodobnie tego samego programu śledzącego aby wykonywać program pojedynczymi krokami, obserwowac wartość zmiennych, ustawiać punkty zatrzymania itd. Często też pisze się własne programy, np. aby móc przetestować pojedyncze moduły. Testowanie kawałków
Przypomnijmy sobie z rozdziału 2-iego „Proces wytwarzania oprogramowania” różne modele wytwarzania oprogramowania. Model skokowy był najprostszy, ale też najbardziej chaotyczny. Wszystko łączy się ze sobą na raz, zaciska kciuki i można mieć nadziję, że powstanie z tego gotowy produkt. Łatwo się domyśleć, że testowanie przy tym trybie pracy jest bardzo trudne. Co najwyżej można przystąpić do testowania dynamicznego metodami czarnej skrzynki, biorąc program w jednym dużym kawałku i próbując, co też dałoby się znaleźć.
Nauczyliśmy się już że taka metoda jest bardzo kosztowna, ponieważ błędy znajduje się bardzo późno w procesie wytwórczym. Z punktu widzenia testu, istnieją dwie główne przyczyny tych wysokich kosztów: 168.Trudno – a czasem wręcz niemożliwe – jest zorientować się, co jest przyczyną awarii. Oprogramowanie jest jak zepsuty czarodziejski stolik – choć wypowiedziało się zaklęcie „stoliczku nakryj się”, żadne potrawy się nie pojawiły. Nie sposób zgadnąć, jaki drobiazg szwankuje i powoduje, że całość nie działa. 169.Jedne błędy mogą zasłaniać inne błędy. Zadanie testowe wywołuje awarię, programista spokojnie znajduje  i usuwa błąd, ale kiedy wykonuje się zadanie testowe ponownie, znów następuje awaria. Tak wiele różnych błędów nałożyło się jedne na drugie, że bardzo czasochłonne robi się dotarcie do pierwszej przyczyny.
Test komponentów i test integracyjny
Żeby wybrnąć z tego całego bałaganu, najlepiej do niego po prostu nie dopuścić. Jeśli kod powstaje i jest testowany po kawałku, a potem stopniowo składa się go w większe całości, nię będzie tylu niespodzianek, kiedy w końcu połączy się cały produkt (rysunek 7.3).
Program główny Moduł   Moduł …
Rysunek 7.3 Poszczególne fragmenty kodu buduje się i testuje pojedynczo, potem integruje ze sobą i testuje ponownie.
Testowanie na najniższym poziomie nazywane jest testowaniem jednostkowym&testowaniem komponentów albo testowaniem modułu1. Moduły są testowane, błędy w nich znajdowane i usuwane, po czym następuje integracja i test integracyjny grup modułów. Proces przyrostowego testowania przebiega stopniowo, łącząc ze sobą coraz więcej i więcej modułów, aż wreszcie cały produkt – a przynajmniej jego większa część – zostaje przetestowana w całości w ramach testowania systemu.
Kiedy stosuje się tę strategię testowania, identyfikacja i lokalizacja błędów stają się o wiele łatwiejsze. Problem znaleziony na poziomie modułu z reguły musi znajdować się w tym samym module. Jeśli następuje awaria podczas łączenia przedtem przetestowanych z osobna modułów, to jej źródłem jest przypuszczalnie interakcja między modułami. Zdarzają się wyjątki od tej zasady, ale ogólnie rzecz biorąc, testowanie i lokalizacja błędów przebiega wtedy znacznie sprawniej niż kiedy testuje się wszystko na raz.
Istnieją dwie główne metody integracji przyrostowej: oddolny i odgórny. W testowaniu oddolnym (rysunek 7.4) testerzy piszą własne procedury, zwane sterownikami, które przywołują testowany moduł. Podłącza się moduły dokładnie w ten sam sposób jak w pełnym programie. Sterowniki wysyłają dane testowe do testowanych modułów, odczytują wyniki i weryfikują, czy są poprawne. W ten sposób daje się zwykle przetestwoać oprogramowanie bardzo szczegółowo, przy użyciu bardzo różnorodnych danych testowych, nawet takich które trudno byłoby zastosować podczas testowania kompletnego systemu.
Rzeczywiste oprogramowanie
Temperatura Wyniki Moduł konwersji z oF na oC
Konfiguracja w świecie rzeczywistym
Oprogramowanie sterownika
Dane testowe     Wyniki Moduł konwersji z oF na oC
Konfiguracja sterownika testowego
Rysunek 7.4 Sterownik testowy może zastąpić część oprogramowania i przetestować bardzo dokładnie moduł niższego rzędu.

Integracja odgórna brzmi trochę jak skokowa w mniejszej skali. W końcu jeśli gotowy jest główny program, musi być za późno brać się za testowanie modułów? Nie całkiem jest to prawdą. Spójrzmy na rysunek 7.5. W tym wypadku, moduł interfejsu na niskim poziomie zbiera dane o temperaturze z elektronicznego termometru. Moduł wyświetlacza umieszczony jest tuż ponad interfejsem, skąd czyta dane i wyświetla je na ekranie dla użytkownika. Aby przetestować górny moduł wyświetlacza na gotowym systemie, trzeba by posługiwać się palnikami, woda, lodem i zamrażarką, żeby spowodować zmiany temperatury czujnika, przekazywane do modułu wyświetlającego.
Zamiast jednak testować moduł wyświetlacza usiłując sterować temperaturą termometru, można napisać fragment kodu, zwany namiastką, który działa tak jak interfejs, podając temperatury - zapisane np. na pliku - wprost do modułu wyświetlającego. Moduł wyświetlający odczytuje przekazywane dane tak samo, jakby je czytał wprost z interfejsu prawdziwego termometru. Nie jest w stanie dostrzec żadnej różnicy. Używając namiastek, można – w tym wypadku – szybko przetestować moduł wyświetlacza przy użyciu wielkiej ilości danych testowych i dokonać walidacji tego modułu.
Moduł wyświetlacza temperatury
Moduł interfejsu termometru
Konfiguracja w świecie rzeczywistym
Testowany moduł wyświetlacza temperatury
Namiastka skonstruowana przez testera
Plik z danymi testowymi
Konfiguracja sterownika testowego
Rysunek 7.5 Namiastka testowa przesyła dane do góry, do testowanego modułu.
Przykład testu modułu
Funkcją dostępną w wielu językach programowania jest konwersja ciągu znaków ASCII w wartość całkowitoliczbową.
Ta funkcja przyjmuje ciąg cyfr, znaki ‘-‘ lub ‘+‘ oraz możliwe dodatkowe znaki jak spacja oraz litery, i przekształca je w wartość liczbową. Na przykład, ciąg znaków „12345” zostaje przekształcony w liczbę dwanaście tysięcy trzysta czterdzieści pięć. Tej funkcji używa się często do przetwarzania danych z okna dialogowego – na przykład czyjś wiek albo inne dane.

W języku C ta funkcja nazywa się atoi(), co jest skrótem nazwy „ASCII na liczbę całkowitą”1. Rysunek 7.6 pokazuje specyfikację tej funkcji. Kto nie jest programistą, nich się nie denerwuje. Oprócz pierwszej linijki, określającej jak funkcję się przywołuje, specyfikacja jest napisana w zwykłej polszczyźnie i można jej używać do opisu tej samej funkcji dla dowolnego języka programowania.
Co powinien zrobić tester, któremu przypadło zadanie wykonania dynamicznego testowania – metodami szklanej skrzynki – tego modułu?
int atoi (const char *string) Funkcja „z ASCII na liczbę całkowitą” przekłada ciąg znaków na liczbę całkowitą.
Wartość powrotna Funkcja zwraca wartość całkowitoliczbową, powstałą przez potraktowanie ciągu znaków na wejściu jako zapisu liczby. Wartość powrotna wynosi 0 jeśli dane z wejścia nie dadzą się przkształcić na liczbę całkowitą. Wartość powrotna jest niezdefiniowana w wypadku przepełnienia.
Parametr wejściowy ciąg znaków Ciąg znaków który ma być przekształcony.
Uwagi Ciąg znaków na wejściu jest sekwencją znaków, którą można zinterpretować jako wartość liczbową. Funkcja przestaje czytać ciąg znaków po natrafieniu na pierwszy znak, którego nie daje się zinterpretować jako części składowej liczby. Ten znak może być znakiem zerowym (`\0`) zakańczającym ciąg znaków.
Ciąg znaków dla tej funkcji ma postać: [pusty znak] [znak plus/minus] cyfry
Pusty znak może być spacją i/albo znakiem tabulacji, które są pomijane; znak plus/minus to albo plus (+), albo minus (-); cyfry to to jeden lub więcej cyfr dziesiętnych. Funkcja nie rozpoznaje punktu dziesiętnego, potęg ani żadnych innych, niewymienionych powyżej znaków. Rysunek 7.6 Specyfikacja funkcji atoi() w języku C. Wydaje się od razu, że ten moduł wygląda jak moduł na najniższym poziomie, taki który jest przywoływany przez inne moduły, ale sam niczego nie przywołuje. Można to ewentualnie potwierdzić, czytając kod modułu. Jeśli tak jest, to logiczną metodą będzie napisanie sterownika testowego, aby móc sprawdzać ten moduł niezależnie od reszty programu.
Sterownik testowy wysyłałby skonstruowane przez testera ciągi znaków do funkcji atoi(), odbierał wynik i porównywał go z oczekiwanym rezultatem. Najprawdopodobniej, sterownik testowy zostałby napisany w tym samym języku, co testowana funkcja – w tym wypadku, w języku C – ale daje się też pisać sterowniki w innych językach, byle miały one interfejs do języka, w którym napisany jest testowany moduł.
Sterownik testowy może przybierać różne kształty. Może to być proste okno dialogowe, jak na rysunku 7.7, którego używa się do wprowadzania ciągów znaków i do obserwacji wyników. Mógłby to być osobny program, czytający ciągi znaków i oczekiwane wyniki z pliku. Okno dialogowe, sterowane przez użytkownika, jest interakcyjne i bardzo elastyczne – można je udostępnić rónież testerowi stosującemu techniki czarnej skrzynki. Z drugiej strony, osobny program może działać o wiele szybciej, czytając dane testowe i zapisując wyniki bezpośrednio na pliku.
Rysunek 7.7 Sterownik testowy w postaci okna dialogowego może być zastosowany, aby posyłać zadania testowe testowanemu modułowi.
Następnie należałoby zanalizować specyfikację i zadecydować, jakie zadania testowe typu czarnej skrzynki warto wypróbować, a w końcu zastosować podział na klasy równoważności aby zmniejszyć ich ilość (przypomnijmy sobie rozdział 5-y). Tabela 7.1 pokazuje przykłady zadań testowych wraz z ciągami znaków stosowanych jako dane wejściowe oraz oczekiwanymi wynikami wyjściowymi. Tabela ta nie ma być wyczerpująca.
Tabela 7.1 Przykładowe zadania do testowania przekształcenia ciągu ASCII w liczbę całkowitą.
Ciąg znaków (na wejściu) Wartość na wyjściu „1” 1 „-1” -1 „+1” 1 „0” 0 „-0” 0 „+0” 0 „1.2” 1 „2-3” 2 „abc” 0 „a123” 0 i tak dalej

Wreszcie należałoby również rzucić okiem na kod źródłowy, zobaczyć z jaki sposób zrobiono implementację funkcji i wykorzystac tę „szkalnoskrzynkową” wiedzę na temat modułu, aby dodać lub usunąć zdania testowe.
Ważne, aby zadanie testowe typu „czarnoskrzynkowego”, oparte na specyfikacji, konstruować zanim zacznie się stosowac metody szklanej skrzynki. W ten sposób testuje się w pierwszy rzędzie to, co moduł powinien wykonywać. Jeśli natomiast zacząć od zadań testowych sporządzonych według metod szklanej skrzynki, to jest poprzez analizę kodu, łatwo zatracić bezstronność i konstruować zadania testowe na podstawie tego, jak moduł rzeczywiście działa. Jednak programista mógł przecież nie zrozumieć specyfikacji i wtedy zadania testowe skonstruowane przez testera będą tak samo błędne. Owszem, mogą być dokładne, znakomicie „testujące” moduł, ale nie będą trafne, ponieważ nie będą testować właściwego działania.
Dodawanie i odrzucanie zadań testowych przy użyciu wiedzy na temat kodu jest w istocie szczególnym zastosowaniem metody podziału na klasy równoważności przy zastosowaniu informacji „wewnętrznych”. Pierwotne zadania testowe mogły zakładać, że zadania takie jak „a123” i „z123” są istotnie różne. Analiza kodu mogłaby jednak ujawnić, że zamiast posługiwać się tabelą ASCII, programista ograniczyl się do poszukiwania znaków pustych, znaków ‘+‘ i ‘-‘ oraz cyfr. Mając tę informację, jedno z powyższych zadań testowych można spokojnie odrzucić, ponieważ oba znajdują się w tej samej klasie równoważności.
Inna sytuacja – dokładniejsza analiza kodu mogłaby na przykład ujawnić, że identyfikowanie znaków ‘+‘ i ‘-‘ wygląda trochę podejrzanie – może po prostu trudno jest je zrozumieć. W tej sytuacji, warto dla upewnienia się dodać kilka zadań testowych ze znakami ‘+‘ i ‘-‘ rozrzuconymi w różnych miejscach. Pokrycie danych Powyższy przykład testowania funkcji atoi() metodami szklanej skrzynki był znacznie uproszczony i pomijał wiele szczegółów na temat wpływu analizy kodu na dobór zadań testowych. W rzeczywistości analiza kodu jest czymś więcje niż źródłem dobrych poysłów, co testować, a czego nie warto.
Należy – tak samo jak to się robi w testowaniu metodami czarnej skrzynki – podzielić kod na dane i stany (albo przepływ sterowania). Stosując ten punkt widzenia, o wiele łatwiej odnieść uzyskaną informację na temat kodu do wcześniej skonstruowanych zadań testowych metodami czarnej skrzynki.
Weźmy najpierw pod uwagę dane. Do danych zaliczają się wszystkie zmienne, stałe, wektory, inne struktury danych, dane wejściowe z klawiatury i z myszy, dane wejściowe i wyjściowe z plików i z ekranu, wjeście/wyjście z innych urządzeń jak modemy, sieci i tak dalej.

Przepływ danych
Badanie pokrycia przepływu danych polega na prześledzeniu całej drogi przez fragment programu jednego „kawałka” danych. Na poziomie testowania jednostkowego oznacza to przepływ danych przez pojedynczy moduł. Można także prześledzić drogę danych przez kilka zintegrowanych modułów albo nawet przez całe oprogramowanie – aczkolwoiek byłoby to bardzo czasochłonne.
Testując funkcję na tak niskim poziomie, używa się programu śledzącego i obserwuje wartości zmiennych w czasie wykonywania programu (rysunek 7.8). Stosując testowanie czarnej skrzynki, zna się tylko wartość zmiennej na początku i na końcu. Stosując dynamiczne testowanie szklanej skrzynki, można też sprawdzać wartości pośrednie podczas wykonywania programu. Biorąc pod owagą poczynione obserwacje, można zmodyfikować zadania testowe tak, aby wymusić przyjęcie przez zmienne interesujących czy ryzykownych wartości pośrednich.
Rysunek 7.8 Program śledzący i obserwacja zmiennych umożliwia śledzenie wartości zmiennych w czasie wykonywania programu.
Wewnętrzne wartości graniczne
Wewnętrzne wartości graniczne omawiane już były w rozdziale 5-ym w odniesieniu do wbudowanych tabel ASCII i do wartości potęgi dwójki. To są przypuszczalnie najczęściej spotykane wewnętrzne wartości graniczne powodujące błędy, ale każdy fragment kodu będzie też miał swoje własne, specjalne wewnętrzne wartości graniczne. Oto kilka przykładów: 170.Moduł służący do wyliczania podatków może przy progu podatkowym zmienić sposób naliczania z tabeli wartości na użycie arytmetycznego wyrażenia do obliczeń. 171.System operacyjny może zacząć przenosić dane do pamięci tymczasowej na dysku kiedy zaczyna brakować pamięci RAM. Wartość tej granicy nie musi być nawet ustalona z góry, może się zmieniać zależnie od tego, ile miejsca pozostało na dysku.

172.Dla uzyskania większej dokładności, złożony algorytm numeryczny może zmieniać użwaną formułę obliczeń zależnie od wielkości liczby.
Przy testowaniu metodami szkalnej skrzynki należy uważnie badać kod w poszukiwaniu wewnętrznych wartości granicznych, aby móc dorobić dla nich dodatkowe zadania testowe. Warto spytać programistę, który napisał kod, czy potrafi zidentyfikować takie miejsca, i zwrócić baczną uwagę na wewnętrzne tabele danych, które są szczególnie podatne na problemy wewnętrznych wartości granicznych.
Wzory i równania
Bardzo często wzory i równania ukryte są głęboko w kodzie i ich obecność ani skutki nie są oczywiste, kiedy się patrzy z zewnątrz. Program finansowy do obliczania procentu złożonego na pewno będzie gdzieś zawierał nastąpujący wzór:
A = P (1 + r/n)nt
gdzie P = suma wyjściowa r = roczna stopa procentowa n = ilość razy, kiedy w ciągu roku nalicza się procent t = ilość lat A = suma po upływie czasu t Dobry tester posługujący się metodami czarnej skrzynki wybrałby, należy mieć nadzieję, zadanie testowe gdzie n=0, ale tester stosujący metody szklanej skrzynki, zobaczywszy ten wzór w kodzie, na pewno zastosuje n=0, ponieważ spowoduje się w ten sposób awarię wzoru i błąd dzielenia przez zero. Cóż się jednak stanie, jeśli wartość n jest wynikiem innych obliczeń? Może program ustala wartość n na podstawie danych wprowadzanych przez użytkownika, albo algorytm wypróbowuje różne warotści n w celu znalezienia takiej, dla której wynik będzie najniższy. Należy zadać sobie pytanie, czy istnieje możliwość, że n kiedykoliwek otrzyma wartość zero i odkryć, jakie dane trzeba wprowadzic do programu aby to uzyskać.
Należy przeszukiwać kod w poszukiwaniu wzorów i równań, badać używane w nich zmienne i konstruować zadania testowe oparte na dodatkowych klasach równoważności, jako dodatek do klas równoważości dla danych wejściowych i wyjściowych programu.
Wymuszanie awarii
Ostatnim rodzajem omawianych w tym rozdziale testów opartych na danych jest wymuszanie awarii. Jeśli używa się programu śledzącego, można nie tylko obserwować warości zmiennych, ale również je modyfikować.
W omawianym przykładzie obliczania procentu złożonego, jeśli nie znajduje się sposobu nadania zmiennej n wartości zerowej, można nadać jej warość zero przy pomocy programu śledzącego. Program albo sobie z tym poradzi… albo nie.
Warto pamiętać, że przy pomocy programu śledzącego można stworzyć sytuację, która nigdy nie może zaistnieć przy normalnym użytkowaniu programu. Jeśli program kontroluje że warość n jest większa od zera na początku funkcji, a zmiennej n później do niczego co może zmienić jej wartość już się nie używa, to nadanie jej wartości zerowej i spowodowanie w ten sposób awarii programu jest niedozwolonym zadaniem testowym.
Wymuszanie awarii może być skuteczną metodą, o ile stosować ją ostrożnie i z namysłem, sprawdzając razem z programistami, że wybrane zadania testowe są dozwolone. Można w ten sposób wykonywac zadania testowe, w przeciwnym razie trudne lub niemożliwe do osiągnięcia.
Wymuszanie komunikatów o błędach
Wymuszanie awarii jest świetną techniką, aby wywołać pojawienie się wszelkich możliwych komunikatów o błędach, ukrytych w programie. Wiele programów stosuje wewnętrzne kody dla oznaczenia każdego komunikatu o awarii. Kiedy wewnętrzna flaga sygnalizująca błąd jest ustawiona, procedura obsługi błędu odczytuje zmienną zawierającą ten kod i wyświetla odpowiedni komunikat.
Wiele błędów trudno jest zaaranżować – jak na przykład podłączenie 2049 drukarek. Jeśli jednak chce się tylko skontrolować, że poprawny jest sam komunikat o błędzie, (ortografia, słownictwo, format itd), wymuszanie błędów może być bardzo skutecznym sposobem, żeby je wszystkie zobaczyć. Trzeba tylko pamiętać, że w ten sposób  testuje się kod wyświetlający komunikat o błędzie, a nie kod wykrywający błąd. Pokrycie kodu

Tak jak z testowaniem metodami czarnej skrzynki, testowanie danych to dopiero połowa bitwy. Dla osiągniecia wyczerpującego pokrycia należy również przetestować stany programu i przepływ kontroli między nimi. Trzeba sprawdzić wejścia i wyjścia każdego modułu, wykonać każdą linijke kodu, i prześledzić każdą ścieżkę przez program1. Badanie oprogramowania na tym poziomie nosi nazwę analizy pokrycia kodu.
Analiza pokrycia kodu jest dynamiczną techniką szklanej skrzynki, ponieważ wymaga pełnego dostępu do kodu, aby móc obserwować te fragmenty programu, przez które się przechodzi podczas wykonywania zadań testowych.
Najprostszą formą analizy pokrycia kodu jest zastosowanie programu śledzącego, by obserwować które linie kodu się przechodzi podczas jego wykonywania pojedynczymi krokami. Rysunek 7.9 pokazuje przykład programu śledzącego dla Visual Basic w działaniu.
Rysunek 7.9 Program śledzący umożliwia przyjście programu pojedynczymi krokami aby skontrolować, które linie kodu i które moduły przechodzi się podczas wykonywania zadań testowych.
Dla bardzo małych programów albo pojedynczych modułów ten sposób może być całkiem zadowalający. Jednak aby móc przeprowadzić analizę pokrycia kodu dla większości programów, trzeba się posługiwać specjalnym narzędziem, zwanym jako analizator pokrycia kodu. Rysunek 7.10 pokazuje przykład takiego narzędzia.
Analizator pokrycia kodu podłącza się do testowanego programu i jest egzekwowany w tle, kiedy wykonuje się zadania testowe. Kiedy wykonywanie przechodzi przez funkcję, linijkę kodu albo punkt decyzyjny, analizator zapisuje informację o tym. Po zakończonym wykonywaniu można uzyskać dane statystyczne, które identyfikują fragmenty kodu, przez które się przeszło i te, przez które się nie przeszło podczas testowania. Mając te dane wie się następujące rzeczy:
173.Które części kodu nie zostały pokryte przez zastosowane testy. Jeśli kod znajduje się w odrębnym module, którego się nigdy nie przeszło, wiadamo że trzeba się zabrać za tworzenie dodatkowych zadań testowych, aby przetestować działanie tego modułu.
 
Rysunek 7.10 Analizator pokrycia kodu dostarcza szczegółowych danych o skuteczności zastosowanych zadań testowych (rysunek jest własnością i został wydrukowany za zezwolenienm Bullseye Testing Technology.) 174.Które zadania testowe są zbędne. Jeśli wykonanie serii zadań testowych nie zwiększa stopnia pokrycia kodu, to przypuszczalnie należą wszystkie one do tej samej klasy równoważności. 175.Jakie nowe zadania testowe trzeba skonstruować aby osiągnąć lepsze pokrycie. Należy zanalizować fragmenty kodu gdzie pokrycie jest słabe, zrozumieć jego działanie i sporządzić nowe zadania testowe, które wykorzystają ten kod.
Osiąga się także wyczucie jakości testowanego oprogramowania. Jeśli pokrycie kodu wynosi 90% i nie znajduje się wielu błędów, oprogramowanie jest zapewne w dobrym stanie. Jeśli, przeciwnie, testy osiągnęły ledwo 50% pokrycia kodu i nadal znajduje się błędy, trzeba spodziewać się jeszcze wiele pracy.
Pokrycie linii kodu
Najprostszą formą pokrycia kodu jest tak zwane pokrycie instrukcji albo pokrycie linii kodu. Monitorowanie pokrycia instrukcji w trakcie testowania oznacza zwykle, że celem jest wykonanie choć raz każdej instrukcji w programie. Dla krótkiego programu, jak ten na wydruku 7.1, 100% pokrycia instrukcji oznacza wykonanie linii od 1-ej do 4-ej.
Wydruk 7.1 Bardzo łatwo jest przetestować każdą linię tego prostego programu PRINT „Czołem, Świecie”
PRINT „Dzisiejsza data: „; Date$ PRINT „Jest godzina: „; Time$ END Nietrudno ulec złudzeniu, że 100% pokrycia instrukcji oznacza, że program został przetestowany całkowicie. Celem i sygnałem do zaprzestania testów byłoby osiągniecie 100% pokrycia. Niestety, to tylko złudzenie. Przetestowanie każdej instrukcji programu nie oznacza, że przetestowało się również wszystkie możliwe ścieżki przez program.
Pokrycie rozgałęzień programu
Testowanie w celu pokrycia jak największej ilości możliwych ścieżek przez program nazywane jest testowaniem ścieżek. Najprostszą formą testowania ścieżek jest testowanie rozgałęzień. Spójrzmy na program pokazany na wydruku 7.2.
Wydruk 7.2 Instrukcja IF stwarza rozgałęzienie w kodzie PRINT „Czołem Świecie„ IF Date$ = „2000-01-01” THEN PRINT „Dosiego Roku!” ENDIF PRINT „Dzisiejsza data: „; Date$ PRINT „Jest godzina: „; Time$ END Jeśli ma się na celu osiągnąć 100% pokrycia instrukcji, wystarczy jedno zadanie testowe, gdzie zmienna Date$ ma wartość 1 stycznia 2000. Program wykona wówczas następującą ścieżkę:
Linie 1, 2, 3, 4, 5, 6, 7
Analizator pokrycia kodu określiłby, że przetestowana została każda instrukcja i że osiągnięto 100% pokrycia. No to jesteśmy gotowi z testowaniem, prawda? Nieprawda! Przetestowana została każda instrukcja, ale nie każde rozgałęzienie.
Instynkt podpowiada, że należałoby jeszcze wykonać zadanie testowe z datą inną niż 1-y stycznia 2000. Wówczas program wykona inną ścieżkę:
Linie 1, 2, 5, 6, 7
Większość analizatorów pokrycia podaje osobno wyniki pokrycia instrukcji i pokrycia rozgałęzień, co pozwala sobie wytworzyć o wiele lepsze pojęcie o skuteczności testowania.
Pokrycie warunków logicznych
Kiedy juz myśleliśmy, że wszystko gotowe, pojawia się nowa trudnośc w testowaniu ścieżek. Wydruk 7.3 jest odmianą wydruku 7.2. Dodany został nowy warunek do instrukcji IF w drugiej linii, kontrolujący zarówno czas jak i datę. Testowanie warunków logicznych uwzględnia złożone warunki logiczne w instrukcji warunkowej.
Wydruk 7.3 Złożony warunek w instrukcji IF powoduje, że pojawia się więcej różnych ścieżek przez kod PRINT „Czołem Świecie„ IF Date$ = „2000-01-01” AND Time$ = „00:00:00” THEN PRINT „Dosiego Roku!” ENDIF PRINT „Dzisiejsza data: „; Date$ PRINT „Jest godzina: „; Time$ END Aby uzyskać pełne pokrycie warunków logicznych w tym przykładowym programie, potrzebne są cztery grupy danych testowych, jak pokazano w tabeli 7.2. Te dane gwarantują pokrycie wszelkich możliwości w instrukcji IF.
Tabela 7.2 Zadania testowe potrzebne dla osiągnięcia pełnego pokycia warunku IF ze złożonym warunkiem logicznym
Date$ Time$ Wykonanie linii 0000-01-01 11:11:11 1,2,5,6,7 0000-01-01 00:00:00 1,2,5,6,7 2000-01-01 11:11:11 1,2,5,6,7
2000-01-01 00:00:00 1,2,3,4,5,6,7
Jeśli interesuje nas tylko pokrycie rozgałęzień, trzy pierwsze grupy danych nie będą interesujące i mozna je połączyć w jedną klasę równoważności i w jedno zadanie testowe. Kiedy jednak stosuje się miarę pokrycia warunków logicznych, wszystkie cztery zadania są istotne, ponieważ reprezentują cztery różne warunki logiczne.
Analizatory pokrycia kodu mogą zwykle zostać skonfigurowane tak, by pokazywały również pokrycie warunków przy raportowaniu wyników. Kiedy się osiąga pokrycie warunków logicznych, osiąga się zarazem pokrycie rozgałęzień i pokrycie instrukcji.
Nawet jeśli przetestuje się każdą instrukcję, każde rozgałęzienie i każdy warunek logiczny (co jest możliwe tylko dla najmniejszych programów), nadal jeszcze nie przetestowało się wszystkiego. Zwróćmy np. uwagę, że wszysktkie błędy danych, opisane na początku tego rozdziału, nadal są możliwe. Przepływ kontroli i przepływ danych łącznie stwarzają działające oprogramowanie. Podsumowanie
W tym rozdziale dowiedzieliśmy się, jak dostęp do kodu źródłowego w czasie wykonywania programu otwiera cały nowy rozdział testowania oprogramowania. Dynamiczne testowanie metodami szklanej skrzynki upraszcza pracę testera, dając mu wgląd w ukrytą informację, co warto jest przetestować. Poznawszy szczegóły kodu, można eliminować zbędzne zadania testowe i dodawać nowe zadania w miejscach, których istnienia nawet się z początku nie podejrzewało. I jedno, i drugie poprawi wydajnośc testowania.
W rozdziałach 4 – 7 ponaliśmy podstawowe zasady testu oprogramowania: 176.Statyczne testowanie metodami czarnej skrzynki, polegające na badaniu specyfikacji i wyszukiwaniu problemów zanim jeszcze zostaną wpisane w kod programu. 177.Dynamiczne testowanie metodami czarnej skrzynki, polegające na testowaniu oprogramowania bez znajomości wewnętrznych mechanizmów jego działania. 178.Stayczne testowanie metodami szklanej skrzynki, polegające na badaniu szczegółów kodu źródłowego w postaci formalnych przeglądów i inspekcji. 179.Dynamiczne testowanie metodami szklanej skrzynki, gdzie obserwuje się działanie wewnętrznych mechanizów programu i dopasowuje zadania testowe do otrzymanej tą drogą informacji.
W pewnym sensie, te cztery rozdziały obejmują wszystko, co najważniejsze w testowaniu oprogramowania. Oczywiście, przeczytać w książce a umieć zastosować w praktyce to zupełnie co innego. Trzeba wiele zaangażowania i ciężkiej pracy, żeby zostać dobrym testerem. Żeby umieć zastosować te podstawowe techniki w praktyce, wiedzieć która pasuje najlepiej w jakiej sytuacji, trzeba wiele praktyki i doświadczenia.
W części 3-ej „Zastosowanie umiejętności w praktyce” poznamy różne dziedziny testowania i zastosowanie umiejętności z „czernej skrzynki” i ze „szklanej skrzynki” do rozmaitych rzeczywistych scenariuszy. Pytania
Pytania mają na celu ułatwienie zrozumienia. W aneksie A „Odpowiedzi do pytań” znajdują się prawidłowe rozwiązania – ale nie należy ściągać! 1.W jaki sposób znajomość wewnętrznych mechanizmów działania programu wpływa na to, jak i co należy przetestować? 2.Czy jest różnica między dynamicznym testowaniem metodami szklanej skrzynki, a lokalizowaniem i usuwaniem błędów? 3.Jakie są dwa główne powody, dla których testowanie jest niemal niewykonalne w modelu skokowym wytwarzania oprogramowania? Jak można im zaradzić? 4.Prawda czy fałsz: jeśli projektowi brakuje czasu, można przeskoczyć testowanie modułów (jednostkowe) i przystąpić od razu do tesowania integracyjnego. 5.Jaka jest różnica między namiastką testową a sterownikiem testowym? 6.Prawda czy fałsz: zawsze należy najpierw skonstruować zadania testowe metodami czarnej skrzynki. 7.Która jest najlepsza spośród trzech opisanych w tym rozdziale metod pomiaru pokrycia? Dlaczego? 8.Jaka jest najpoważniejsza trudność testowania metodami szklanej skrzynki, zarówno statycznego jak i dynamicznego?

 

 

rozdział 6
07 września 2019, 22:19

Rozdział 6 Analiza kodu

W TYM ROZDZIALE  Statyczne testowanie metodami szklanej skrzynki: badanie projektu konstrukcji i kodu  Formalne przeglądy  Standardy i reguły programowania  Lista kontrolna do przeglądów kodu

Testowanie oprogramowania nie ogranicza się do tego, by traktować specyfikacje lub program jak „czarną skrzynkę”, jak to opisne zostalo w rozdziale 4-ym „Badanie specyfikacji” oraz w rozdziale 5-ym „Test z klapkami na oczach”. Znając trochę programowanie, nawet niewiele, można testować bezpośrednio projekt konstrukcji systemu oraz kod źródłowy.

W wielu branżach te metoda weryfikacji jest o wiele mniej powszechna niż testowanie metodami czarnej skrzynki. Tam, gdzie wytwarza się oprogramowanie dla zastosowań wojskowych, finansowcyh, kontroli procesów przemysłowych oraz dla zastosowań medycznych – albo jeśli ma się szczęście pracować w organizacji stosującej zdyscyplinowane metody wytwarzania - weryfikacja produktu na poziomie projektu oraz kodu jest powszechna.

Ten rozdział jest wprowadzeniem do podstaw weryfikacji projektu konstrukcji i kodu źródłowego. Nie jest to zwykle zadanie dla początkującego testera, ale przypuszczalnie – mając pewne umiejętności w programowaniu – tester zetknie się z tą dziedziną1.

Najważniejsze punkty w tym rozdziale to: 86.Zalety statycznego testu metodami szklanej skrzynki 87.Różne rodzaje statycznych przeglądów metodą szklanej skrzynki 88.Standardy i reguły kodowania 89.Ogólne metody inspekcji kodu w poszukiwaniu błędów Statyczne testowanie metodami szklanej skrzynki: badanie projektu konstrukcji i kodu

Autor 1 Autor wychodzi z założenia, że typowy tester nie jest wykształconym programistą. Oczywiście, nie musi tak być (przyp. tłumacza).

Przypomnijmy sobie definicje testowania statycznego i testowania metodami szklanej skrzynki z rozdziału 4-ego. Testowanie statyczne odnosie się do testowania czegoś co nie jest działającym kodem – do badania i inspekcji. Testowanie metodami szklanej skrzynki (albo „białej skrzynki”) oznacza, że ma się dostęp do wnętrza konstrukcji, do kodu programu.

Tak więc statyczne testowanie metodami szklanej skrzynki jest procesem starannego i metodycznego przeglądu projektu konstrukcji, architektury lub kodu źródłowego w celu znalezienia błędów bez uruchamiania programu. Czasami używa się też nazwy analiza strukturalna.

Motywy stosowania statycznego testu metodami szklanej skrzynki są dwa: znajdować błędy jak najwcześniej oraz znajdować błędy które byłoby trudno zidentyfikować i zlokalizować przy pomocy dynamicznego testowania metodami czarnej skrzynki. Im bardziej osoby dokonujące przeglądów są niezależne od zespołu wytwarzającego kod, tym lepiej, zwłaszcza jeśli znalizy dokonuje się na niskim poziomie i wcześnie w cyklu wytwarzania.

Dodatkowym zyskiem z przeprowadzania statycznego testu metodami szklanej skrzynki jest to, że w jego trakcie zespół testerów często znajduje wiele pomysłów, które daje się potem zastosować do testowania dynamicznego. Nawet nie wnikając w zawiłe szczegóły kodu, uczestnictwo w przeglądach pozwala zidentyfikować obszary programu które są zawiłe i gdzie często pojawiają się błędy.

Zespoły projektowe różnie dzielą się odpowiedzialnością za statyczne testowanie metodami szklanej skrzynki. W niektórych zespołach odpowiedzialność za zorganizowanie przeglądów spoczywa na programistach, którzy zapraszają na nie testerów w roli niezależnych obserwatorów. W innych zespołach odpowiedzialność spoczywa na testerach, którzy zapraszają programistów na przeglądy. Każde z tych podejść może działać dobrze. Zespół projektowy ma prawo wybrać to, co mu najlepiej odpowiada.

Statyczny test metodami szklanej skrzynki ma jedną wadę – nie zawsze się go przeprowadza. Wiele zespołów uległo przesądom, że ta technika jest zbyt czasochłonna, zbyt kosztowna i niezbyt produktywna. Wszystko to jest nieprawdą – w porównaniu z kosztami testowania, znajdowania niektórych i pomijania wielu błędów na samym końcu projektów. Kłopot w tym, że główne zadanie programisty wiele osób rozumie jako pisanie linijek kodu i każde zadanie, które zmniejsza tak rozumianą efektywność wydaje się spowalniać cały proces powstawania oprogramowania.

Na szczęście, czasy się zmieniają. Coraz więcej przedsiębiorstw zdaje sobie sprawę z zysków płynących z wczesnego testowania i zatrudnia oraz szkoli swoich programistów i testerów, aby wykonywali testowanie metodami szklanej skrzynki. Nie jest to ścisła matematyka (o ile ktoś nie testuje implementacji algorytmów matematycznych), ale żeby zacząć ją stosować przydaje się znajomość kilku podstawowych technik. Jeśli kogoś interesuje pójście jeszcze dalej, możliwości są olbrzymie. Formalne przeglądy

Przegląd formalny to proces, który steruje statycznym testowaniem metodami szklanej skrzynki. Przegląd formalny może mieć rozmaite formy – począwszy od prostego spotkania między dwoma programistami aż po szczegółową, rygorystyczną inspekcję kodu1.

Cztery elementy wyróżniają przeglądy formalne: 90.Identyfikacja problemów. Celem przeglądów jest znalezienie błędów – nie tylko tego, co jest zrealizowane niepoprawnie, ale również tego, czego brakuje. Krytykę kieruje się na kod (przedmiot przeglądu), a nie na osobę autora. Uczestnicy nie powinni traktować krytyki osobiście. Poczucie wielkości, emocje i wrażliwe uczucia trzeba zostawić za drzwiami. 91.Postępowanie wedle zasad. Należy postępować wedle przyjętego zestawu zasad. Te zasady mogą np. określać ilośc kodu który podlega przeglądowi (zwykle kilkaset linii), ile czasu należy poświęcić (kilka godzin), na co należy zwrócić uwagę itd. Ważne jest, aby uczestnicy znali swoje role i czego się od nich oczekuje. Dzięki temu przeglądy przebiegają sprawniej. 92.Przygotowanie. Oczekuje się, że każdy uczestnik będzie przygotowany do przeglądu. Zależnie od rodzaju przeglądu, uczestnicy mogą mieć różne role i muszą je znać, aby móc je w czasie przeglądu realizować. Większość problemów znalezionych w procesie przeglądu

1 Czytelniku, wybacz, ale oto znowu nomenklatura autora różni się od powszechnie przyjętej. Otóż zwykle inspekcje i przeglądy formalne to synomimy, określające przeglądy dokonywane szczegółowo, rygorystycznie, według opisanego procesu, natomiast cała reszta – to po prostu przeglądy, albo przeglądy nieformalne. Nie ma to większego znaczenia, ale dobrze orientować się w tym nomenklaturowym chaosie, żeby rozumieć o czym jest mowa.

Warto też pamiętać, że nie istnieje powszechnie przyjęta klasyfikacja różnych typów przeglądów i podane przez autora definicje przeglądu koleżeńskiego, ręcznego sprawdzianu i inspekcji różnią się w

Dziwne coś:znajduje się w czasie przygotowań, a nie podczas samego przeglądu2. 93.Pisanie raportu. Grupa dokonująca przeglądu musi stworzyć pisemny raport podsumowujący wyniki przeglądu. Raport musi być dostępny dla całego zespołu projektowego. Jest istotne aby wszyscy poznali wyniki przyglądu: ile znaleziono błędów, gdzie itp.

Najważniejsze dla formalnych przeglądów jest to, że odbywają się według ustalonego procesu. Przypadkowe „zebrać się razem i przelecieć się przez trochę kodu” nie wystarcza, a nawet może okazać się przeciwksuteczne. Jeśli proces odbywa się bałaganiarsko, wiele błędów pozostanie nieznalezionych, a uczestnicy będą mieli poczucie zmarnowango czasu.

Właściwie realizowane, przeglądy mogą być wyśmienitą metodą wczesnego znajdowania błędów. Wyobraźmy je sobie jako pierwsze siatki na motyle (rysuek 6.1), które chwytają największe „pluskwy” na samym początku procesu wytwarzania. Oczywiściem mniejsze „pluskwy” przedostaną się, ale zostaną schwytane w kolejnych fazach testowania, stosujących siatki o coraz mniejszych oczkach.

Rysunek 6.1 Formalne przeglądy są jak pierwsze w serii siatek do łapania błędów – „pluskiew”.

Oprócz znajdowania problemów i błędów, formalne przeglądy przynoszą szereg pośrednich korzyści.

szczegółach od definicji innych autorów, czy też od nazewnictwa stosowanego w przedsiębiorstwach (przyp. tłumacza).

2 Wywód byłby jaśniejszy, gdyby słowo przegląd stosować tylko do całego procesu, zaś zebranie na którym spisuje się znalezione błędy nazywać zebraniem przeglądowym, a nie – myląco – również przeglądem (przyp. tłumacza)..

94.Komunikacja. Przekazuje się sobie informacje, których nie ma w oficjalnych raportach. Na przykład, testerzy zajmujący się testowaniem metodami czarnej skrzynki mogą uzyskać cenny wgląd w obszary możliwych kłopotów. Początkujący programiści mogą nauczyć się nowych technik od doświadczonych programistów. Kierownictwo może uzyskać lepsze wyczucie, jak projekt posuwa się do przodu. 95.Jakość. Kod napisany przez programistę kontroluje się szczegółowo, linijka po linijce, funkcja za funkcją – co często powoduje, że programiści stają się staranniejsi. To nie znaczy że w przeciwnym razie byliby niechlujni – po prostu wiedząc, że kod będzie szczegółowo skontrolowany przez kolegów, odruchowo podejmuje się dodatkowy wyciłek żeby unikać błędów. 96.Powstanie ducha zespołu. Dobrze zorganizowane i poprowadzone, przeglądy mogą stać się forum, gdzie testerzy i programiści uczą się lepiej nawzajem rozumieć swoją pracę i potrzeby, i szanować nawzajem swoje umiejętności. 97.Rozwiązania. Może udać się znaleźć rozwiązania dla trudnych problemów, choć nie wszystkie typy przeglądów pozwalają na ich dyskutowanie w czasie zebrania przeglądowego. Może być lepiej, żeby taka dyskusja odbywała się poza zebraniem.

Te pośrednie korzyści zdarzają się, ale nie można na nie liczyć z pewnością. Zdarza się, że – z różnych powodów – członkowie zespołu pracują w izolacji od siebie. Formalne przeglądy to świetny sposób, żeby zebrać ich razem w jednym pokoju, dyskutujących wspólny problem.

Kontrole koleżeńskie

Najłatwiej zebrać razem członów zespołu i zlecić im wykonanie pierwszego wspólnego przeglądu w postaci przeglądu koleżeńskiego, który jest najprostszą formą przeglądu. Te metoda jest w istocie rozmową w stylu „pokaż mi jak to zrobiłeś, to ja ci pokażę jak ja to zrobiłem.”

Przeglądy koleżeńskie często odbywają się tylko z udziałem programisty – autora kodu oraz jednego czy dwóch testerów albo programistów w roli inspektorów. Ta niewielka grupa po prostu wspólnie przegląda kod i poszukuje problemów i opuszczeń. Aby mieć pewność że przegląd będzie wydajny (i nie zamieni się w przerwę na kawę), wszystkie cztery elementy formalnego przeglądu powinny być zastosowane: szukanie problemów, postępowanie według zasad, przygotowanie do przeglądu i napisanie raportu. Ponieważ przeglądy koleżeńskie są nieformalne, często omija się niektóre z tych elementów. Jednak samo zebranie się razem aby przedyskutowac kod niejednokrotnie wystarcza, żeby znaleźć błędy1.

Ręczny sprawdzian

Ręczne sprawdziany są krokiem wzwyż pod względem poziomu formalizmu w porównaniu z przeglądami koleżeńskimi. Ręczny sprwadzian polega na tym, że programista, który napisał kod, prezentuje kod programu „na piechotę” niewielkiej grupie programistów i testerów. Insepktorzy powinni zawczasu otrzymać kopie kodu, tak żeby mogli go skontrolować i zapisać komentarze i pytania, które chcą zadać w trakcie przeglądu. Ważne, by choć jeden z inspektorów był doświadczonym programistą.

Prezentator czyta kod linijka po linijce lub funkcja po funkcji i wyjaśnia, co i dlaczego kod wykonuje. Inspektorzy słuchają i pytają o wszystko, co budzi wątpliwości. Ponieważ w ręcznym sprawdzianie z reguły uczestniczy więcej osób niż w kontroli koleżeńskiej, jest bardzo ważne by wszyscy byli do zebrania przygotowani i by przestrzegać reguł. Ważne jest też, by po ukończonym sprawdzianie został napisany raport, streszczający jakie błędy zidentyfikowano i jak planuje się je skorygować.

Inspekcje

Inspekcje są najbardziej formalnym rodzajem przeglądów. Mają precyzyjną strukturę i wymagają, żeby wszyscy uczestnicy byli odpowiednio wyszkoleni. Inspekcje tym różnia się od przeglądów koleżeńskich i ręcznych sprawdzianów, że osoba która przedstawia kod, zwana prezentatorem, nie jest autorem kodu. Ta sytuacja wymaga, by ktoś inny oprócz autora rozumiał przedstawiany materiał w szczegółach.

Autor :1 Istnieje modna ostatnio metoda wytwarzania kodu, zwana “programowaniem ekstremalnym” (extreme programming, xP), polegająca na tym, że programiści pracują w parach: jeden pisze kod, drugi dokonuje na bieżąco “koleżeńskiej inspekcji” powstającego kodu; po jakimś czasie zamieniają się rolami (przyp. tłumacza).

Pozostali uczestnicy inspekcji nazywani są inspektorami1. Każdy otrzymuje odrębne zadanie zanalizowania kodu z innego punktu widzenia, np. użytkownika, testera, albo osoby zajmującej się konserwacją oprogramowania. To pozwala uwzglądnić w czasie inspekcji różne aspekty produktu i często pozwala zidentyfikować różne rodzaje błędów. Jeden z inspektorów otrzymuje nawet zadanie inspekcji kodu od tyłu – to znaczy od końca do początku – aby upewnić się czy materiał został zrealizowany jednolicie i w pełni.

Dwaj inspektorzy otrzymuja również rolę przewodniczącego i sekretarza, którzy odpowiadają za przestrzeganie reguł i płynny przebieg całego procesu.

Po zebraniu przeglądowym inspektorzy mogą zebrać się ponownie, aby przedyskutować znalezione problemy i wspólnie z przewodniczącym przygotować pisemny raport idnetyfikujący konieczne zmiany dla naprawienia błędów. Programista następnie przeprowadza te zmiany, a przewodniczący sprawdza, czy zostały zrobione właściwie. Zależnie od zakresu i wielkości zmian oraz od stopnia krytyczności oprogramowania, może zostać zarządzona kolejna inspekcja dla znalezienia pozostałych błędów.

Stwierdzono, że inspekcje są bardzo skuteczne w znajdowaniu błędów, zwłaszcza w dokumentacji projektu konstrukcji i w kodzie. Popularność inspekcji wzrasta, gdy coraz więcej przedsiębiortstw i zespołów projektowych odkrywa ich zalety. Standardy i reguły programowania

W trakcie formalnych przeglądów inspektorzy poszukują problemów i opuszczeń w kodzie programu. Istnieje wiele typowych błędów, przy których program po prostu nie będzie działać poprawnie. Najlepjej znajduje się je poprzez staranną analizę kodu – doświadczeni programiści i testerzy zwykle umieją to robić.

Istnieje też typ problemów, gdzie kod wprawdzie działałby poprawnie, ale nie jest napisany zgodnie ze standardem lub normą. Można to porównać ze zdaniem, które wprawdzie da się zrozumieć i przekazuje informację, ale nie spełnia zasad gramatyki i składni języka polskiego. Standardy to są ustalone, zapisane, wymagane zestawy reguł – co wolno, a czego nie wolno. Reguły to są sugerowane, praktyczne wskazówki, rekomendacje, zlecane sposoby. Stanardy nie dozwalają wyjątków. Reguły nie są równie obowiązujące.

Autor 1 W języku angielskim inspektorzy w procesie przeglądu nazywani są tu reviewers, a inspektorzy uczestniczący w inspekcji – inspectors. Po polsku podobne rozróżnienie byłoby językowo bardzo niezręczne, dlatego zachowano jednakową nazwę dla obu (przyp. tłumacza).

Może to brzmieć dziwnie, że fragment oprogramowania może działać, może być nawet przetestowany i bardzo stabilny, a mimo to niepoprawny, ponieważ nie spełnia innych kryteriów. Istnieją trzy główne powody, dla których przestrzega się standardów i reguł: 98.Niezawodność. Udowodniono, że kod napisany zgodnie ze standardem lub zestawem reguł jest bardziej niezawodny i ma mniej błędów niż kod nie przestrzegający standardów. 99.Czytelność/łatwość konserwacji. Kod spełniający standardy i reguły jest łatwiej przeczytać, zrozumieć i konserwować. 100.Przenośność. Kod musi być niekiedy przeniesiony na inny sprzęt albo skompilowany innym kompilatorem. Jeśli jest zgodny ze standardem, będzie przypuszczalnie łatwiej – może nawet zupełnie bezboleśnie – przenieść kod na inna platformę.

Wymagania projektu mogą niekiedy domagać się bezwzględnego przestrzegania zasad międzynarodowego albo krajowego standardu, albo tylko zalecać przestrzeganie wewnętrznch zasad zespołu projektowego. Co jest ważne, to stosować jakiś zestaw reguł i weryfikować jego przestrzeganie w trakcie formalnych przeglądów.

Przykłady standardów i reguł programowania

Rysunek 6.2 pokazuje przykład standardu programowania, który określa zasady stosowania instrukcji goto, while i if-else w języku C. Niewłaściwe używanie tych instrukcji często prowadzi do błędów w kodzie, i większośc standardów programowania podaje zasady posługiwania się nimi.

TEMAT: 3.05 Ograniczenia zastsowania struktur kontrolnych STANDARD Nie należy używać instrukcji go to (jak rónież etykiet). Pętla while powinna być stosowana zamiast pętli do-while, z wyjątkiem miejsc gdzie logika problemu jednoznacznie wymaga aby wnętrze pętli zostało wykonane chociaż raz niezależnie od wartości warunku kontrolnego pętli. Tam gdzie pojedyncze if-else może zastąpić continue, należy używać ifelse.

UZASADNIENIE Instrukcja go to jest zabroniona, ponieważ stwierdzono empirycznie korelację jej użycia z występowaniem błędów i trudnym do zrozumienia kodem, oraz z powodu abstrakcyjnego, że algorytmy powinny być wyrażone w postaci struktur ułatwiających skontrolowanie zgodności programu ze strukturą realizowanego procesu. Użycia do-while odradza się z tego powodu, że pętla powinna być kodowana tak, żeby mogła byc łatwo ominięta, to znaczy kontrola warunku pętli powinna być dokonywana przed wejściem do wnętrza pętli.

Rysunek 6.2 Przykład standardów kodowania wyjaśniający jak należy używać kilku struktur kontrolnych języka (Dostosowane z Zasady programowania w C++, Thomas Plum i Dan Saks, © 1991, Plum Hall).

Pokazany tutaj standard składa się z czterech głównych części: 101.Tytuł opisuje czego standard dotyczy. 102.Standard (albo reguła) opisuje treść standardu/reguły wyjaśniając szczegółowo co jest, a co nie jest dozwolone. 103.Uzasadnienie wyjaśnia motywację standardu tak aby programista mógł zrozumieć czemu nakazyana praktyka jest korzystna. 104.Przykład pokazuje proste fragmenty kodu stosującego się do standardu. Nie zawsze jest to konieczne.

Rysunek 6.3 jest opisem reguły stosowania funkcji języka C w języku C++. Zwróćmy uwagę na różnice w sformułowaniach. Tutaj zaczyna się od „należy starać się unikać”. Reguły nie są tak surowe jak standardy i pozwalają na pewną elastyczność w razie potrzeby.

TEMAT: 7.02 C_problemy – Problemowe dziedziny z C

REGUŁA Należy starać się unikać aspektów języka C niezgodnych w programowaniem w C++ 1. Nie używać setimp i longimp jeśli istnieją obiekty z destruktorami, które mogłyby powstać między wykonaniem setimp a wykonaniem longimp. 2. Nie stosować makro offsetof z wyjątkiem kiedy stosuje się do członków tej samej struktury prostej. 3. Nie mieszać I/O w stylu C (używającego stdio.h) z I/O w stylu C++ (używającego iostream.h lub stream.h) w tym samym pliku. 4. Unikać stosowania funkcji języka C takich jak memcpy albo memcap dla kopiowania albo porównywania obiektów innych typów niż wektor znaków lub prosta struktura. 5. Unikać makro NULL z języka C; używać w zamian 0.

UZASADNIENIE Każdy z tych aspektów dotyczy obszaru tradycyjnych zastosowań z języka C, które stwarzają pewne problemy w C++. Rysunek 6.3 Przykład reguł kodowania pokazuje jak stosować pewne aspekty C w C++ (Dostosowane z Zasady programowania w C++, Thomas Plum i Dan Saks, © 1991, Plum Hall).

To kwestia stylu

Istnieją standardy, istnieją reguły, a wreszcie jest też styl. Z punktu widzenia jakości i testu oprogramowania, styl jest bez znaczenia.

Każdy programista, tak samo jak każdy pisarz albo malarz, ma swój włany niepowtarzalny styl. Nawet kiedy przestrzegane są reguły, a użycie konstrukcji jest z nimi zgodne, zwykle nadal jest łatwo powiedzieć, kto jest autorem danego oprogramowania.

Czynnikiem wyróżniającym jest styl. W programowaniu może to być na przykład długość komentarzy albo nazewnictwo zmiennych, albo jakiej formy są wcięcia w kodzie pętli. To jest specyficzny wygląd kodu.

Niektóre zespoły, w zapale stwarzania standardów i reguł, zaczynają czasem krytykować styl kodu. Tester oprogramowania powinien trzymać się z dala od takich dyskusji. Kiedy wykonuje się przeglądy fragmentów oprogramowania, należy ograniczyć uwagi do tego co jest błędne, czego brakuje albo co nie jest zgodne z pisanymi standardami czy regułami. Należy sobie samemu zadać pytanie - czy ma się naprawdę do czynienia z błędem czy też z różnicą opinii, z kwestią stylu. W drugim wypadku nie może być mowy o błędzie.

Skąd wziąć standardy?

Jeśli projekt musi stosować się do istniejących standardów, albo jeśli tylko chce się porównać kod ze standardami, do dyspozycji ma się kilka źródeł.

Narodowe i międzynarodowe standardy w dziedzinie języków programowania i technologii informatycznych można znaleźć: 105.American National Standard Institute (ANSI), www.ansi.org 106.International Engineering Consortium (IEC), www.iec.org 107.International Organisation for Standardisation (ISO), www.iso.ch 108.National Committee for Information Technology Standards (NCITS), www.ncits.org

Można też znaleźć dokumentację reguł programowania i opisy najlepszych praktyk w następujących organizacjach: 109.Association for Computing Machinery (ACM), www.acm.org 110.Institute of Electrical and Electronics Engineers, Inc (IEEE), www.ieee.org

Często można też uzyskać informację od producenta oprogramowania. Wielu producentów publikuje standardy do własnego oprogramowania i można je kupować za niewielką opłatą. Lista kontrolna do przeglądów kodu

Pozostała część tego rozdziału zajmuje się problemami, których należy szukać dokonując formalnego przeglądu kodu. Te listy kontrolne1 stosuje się oprócz porównywania kodu ze standardami oraz oprócz kontroli czy kod spełnia wymagania konstrukcyjne.

Aby rzeczywiście rozumieć i stosować opisane poniżej punkty kontrolne, należy mieć pewne doświadczenie z programowaniem. Jeśli nie ma się dosyć doświadczenia, warto przeczytać książkę dla początkujących, na przykład „Jak nauczyć się samemu programowania w 24 godziny” z wydawnictwa Sams Publishing2 zanim podejmie się próby szczegółowej analizy kodu programu.

Autor:1 Obawiam się, że taka wiedza może okazać się dramatycznie niewystarczająca (przyp. tłumacza).

Błędy w referencjach zmiennych

Błędy w referowaniu zmiennych często spowodowane są użyciem zmiennej, stałej, wektoru, ciągu znaków albo rekordu które nie zostały zainicjalizowane w sposób pasujący do trybu czy sposobu użycia i referowania. 111.Czy istnieje referencja do niezainicjalizowanej zmiennej? Szukanie opuszczeń jest tu równie ważne jak szukanie błędów. 112.Czy indeks wskazyjący pozycję w wektorze znaków albo w ciągu znaków zawsze jest w zakresie zgodnym z rozmiarami wektora? 113.Czy istnieją jakieś możliwe błędy „o jeden” w indeksowych referencjach do wektorów? Przypomnijmy sobie listing 5.1 z rozdziału 5ego. 114.Czy używa się zmiennych tam gdzie stała byłaby odpowiedniejsza, na przykład do kontroli granic wektora? 115.Czy zmiennej jest kiedykolwiek przydzielona wartość innego typu niż zmienna? Na przykład, czy kod przydziela wartość zmiennoprzecinkową zmiennej całkowitoliczbowej? 116.Czy pamięć, do której referują wskażniki, została przydzielona? 117.Kiedy wspólna struktura danych jest używana w kilku funkcjach, czy definicja struktury jest wszędzie taka sama?

Błędy w deklarowaniu zmiennych

Błędy w deklaracjach są spowodowane przez niewłaściwe deklarowanie zmiennych lub stałych. 118.Czy wszystkie zmienne mają zadeklarowaną właściwą długość, typ i rodzaj pamięci? Na przykład, czy zmienna nie powinna być raczej zadeklaraowana jako ciąg znaków niż jako wektor znaków? 119.Czy zmienne inicjalizowane w momencie deklaracji są inicjalizowane poprawnie i wartościa zgodną ze swoim typem? 120.Czy istnieją zmienne z podobnymi nazwami? Nie jest to koniecznie błędem, ale być może symptomem, że pomieszały się zmienne o podobnych nazwach z różnych miejsc w programie.

Wersja 9 października 2001, Bogdan Bereza

Ron Patton „Test oprogramowania” Część II strona 59 (86) 121.Czy istnieją zadeklarowane zmienne, których się potem nie używa albo użyte są tylko jeden raz? 122.Czy wszystkie zmienne zadeklarowane są w swoich modułach? Jeśli nie, czy jest zrozumiałe że zmienna jest wspólna z najbliższym modułem wyższego rzędu?

Błędy obliczeniowe

Błędy obliczeniowe to po prostu błędna matematyka. Wynik obliczenia jest fałszywy. 123.Czy gdzieś w obliczeniach używa się zmiennych różnych typów, jak np. dodawanie liczby całkowitej do zmiennoprzecinkowej? 124.Czy gdzieś w obliczeniach używa się zmiennych tego samego typu ale różnej długości – na przykład dodanie bajtu do słowa? 125.Czy zasady konwersji kompilatora wobec zmiennych różnych typów lub długości są wzięte pod uwagę w kodzie realizującym obliczenia? 126.Czy zmienna której przydzielony zostanie wynik obliczenia jest mniejsza niż wynik wyrażenia po prawej stronie? 127.Czy możliwy jest niedomiar lub przepełnienie w trakcie obliczeń? 128.Czy może gdzieś wystąpić dzielenie przez zero? 129.Czy w wypadku działań całkowitoliczbowych kod uwzględnia możliwość utraty dokładności, np. przy dzieleniu? 130.Czy wartość zmiennej może przekroczyć wartość „rozsądną”? Na przykład, czy wynik obliczania prawdopodobieństwa może być mniejszy niż zero lub wiekszy niż 100%? 131.W wyrażeniach zawierających wiele operatorów, czy zasady kolejności obliczania oraz zasady priorytetu operatorów są uwzględnione? Czy potrzebne są nawiasy dla wyjaśnienia?

Błędy w porównaniach

Mniejsze niż, większe niż, równe, nierówne, fałsz, prawda. Porównania i decyzje są zwykle bardzo narażone na błędy warunków brzegowych.

132.Czy porównanie jest poprawne? To się wydaje trywialne, ale prawie zawsze jest jakas wątpliwość czy porównanie ma być np. „mniejszy niż” czy też „mniejszy niż lub równy”. 133.Czy dokonuje się porównań między wartościami zmiennoprzecinkowymi? Jeśli tak, czy mogą wystąpić jakieś kłopoty z dokładnością? Czy na przykład 1.00000001 jest dostatecznie blisko 1.00000002, żeby porównanie wykazało równość? 134.Czy wyrażenie boole‘owskie wyraża właściwie to co powinno? Czy obliczenia na zmiennych boole‘owskich są poprawnie zbudowane? Czy kolejność oszacowania jest wzięta pod uwagę? 135.Czy argumenty wyrażenia boole‘owskiego są boole‘owskie? Czy na przykład zmienna całkowitoliczbowa zawierająca liczbę całkowitą nie jest używana w boole‘owskich obliczeniach?

Błędy przepływu sterowania

Błędy przepływu sterowania są skutkiem nieprzewidzianego – innego niż zamierzone – działania pętli i innych konstrukcji języka. Spowodowane są zwykle – bezpośrednio lub pośrednio – przez błędy w obliczeniach albo w porównaniach. 136.Jeśli język zawiera takie konstrukcje jak begin…end oraz do…while, czy zakończnia są jawne i czy właściwie zamykają konstrukcje? 137.Czy program, moduł, subrutyna albo pętla kiedykolwiek zakończą działania? Jeśli nie, czy to jest w porządku? 138.Czy istniej ryzyko przedwczesnego wyjścia z pętli? 139.Czy istnieje możliwość, że kod we wnętrzu pętli nie zostanie wykonany ani razu i czy jest to do przyjęcia? 140.Jeśli program zawiera wielokrotne rozgałęzienie, takie jak instrukcja switch… case, czy wartość zmiennej indeksowej może przekroczyć wartości indeksów możliwych rozgałęzień? Jeśli tak, czy istnieje odpowiednia porcedura postępowania w tym wypadku? 141.Czy istnieją błędy „o jeden”, które mogą spowodować niezaplanowane wykonanie wnętrza pętli?

Błędy w parametrach procedur

Błędy w parametrach procedur biorą się z niewłaściwego przekazywania danych do podprocedur. 142.Czy typ i wielkość parametrów otrzymanych przez procedurę zgodne są z wysłanymi przez kod przywołujący porcedurę? Czy ich kolejność jest poprawna? 143.Czy jeśli procedura ma wiele punktów wejścia (brr…), to istnieją odniesienia do parametrów nie zdefiniowanych dla danego punktu wejścia? 144.Czy jeśli zmienne posyła się do procedury jako parametry, czy ich wartość może zostać przypadkowo zmieniona przez procedurę? 145.Czy procedura zmienia wartość parametru, który ma być tylko wartością wejściową? 146.Czy skala dla poszczególnych parametrów jest taka sama w rutynie wywołującej i wywołanej – np. europejskie i amerykańskie miary? 147.Jeśli występują zmienne globalne, czy mają one takie same definicje we wszystkich procedurach, które z nich korzystają?

Błędy danych wejściowych i wyjściowych

Ta klasa błędów odnosi się do wszelkich działań związanych z czytaniem z pliku, przyjmowaniem danych z klawiatury albo z myszy, pisanie na urządzeniu wyjścia, np. na drukarce albo na ekranie. Podane poniżej punkty są bardzo ogólnikowe i uproszczone. Należy je uzupełnić, żeby zastosować do różnych typów oprogramowania i urządzeń, które się testuje. 148.Czy oprogramowanie dokładnie spełnia wymagania formatu danych pisanych albo czytanych przez urządzenie zewnętrzne? 149.Jeśli plik albo urządzenie peryferyjne nie jest gotowe, czy program radzi sobie właściwie z tą sytuacją? 150.Czy oprogramowanie radzi sobie gdy zewnętrzne urządzenie jest odłączone, nie odpowiada, albo jest przepełnione w trakcie czytania lub pisania? 151.Czy wszelkie możliwe błędy są obsługiwane przez oprogramowanie w przewidywalny sposób?

152.Czy wszystkie komunikaty błędów zostały sprawdzone i są trafne, mają odpowiednią treść, są poprawne gramatycznie i ortograficznie?

Inne kontrole

Poniższa lista zawiera pozycje, które nie pasowały do pozostałych kategorii. Nie jest ona w żadnym razie kompletna, ale powinna być dobrym początkiem dla sporządzenia listy pełniejszej, stosownej dla konkretnego projektu. 153.Czy oprogramowanie będzie działać poprawnie z wszystkimi wymaganymi językami? Czy jest w stanie posługiwać się rozszerzonym kodem ASCII? Czy używa Unicode zamiast ASCII? 154.Jeśli oprogramowanie ma być przenośne na inne kompilatory i mikroprocesory, czy zostalo to wzięte pod uwagę? Przenośność, jeśli wymagana, staje się często dużym problemem jeśli się jej nie planowało i nie testowało pod jej kątem. 155.Czy uwzględnione zostały wymagania kompatybilności tak żeby oprogramowanie działało poprawnie z różną ilością dostępnej pamięci, różnymi rodzajami sprzętu takiego jak karty dźwiękowe i graficzne, i z różnymi urządzeniami peryferyjnymi, jak drukarki i modemy? 156.Czy kompilacja programów powoduje różne komunikaty „ostrzegawcze” albo „informacyjne”? Zwykle oznacza to że w kodzie kryje się coś wzbudzającego wątpliwości. Puryści twierdziliby, że żadne komunikaty ostrzegawcze nie są dopuszczalne. Podsumowanie

Badanie kodu – statyczne testowanie metodami szklanej skrzynki – okazało się wielokrotnie skutecznym sposobem wczesnego znajdowania błędów. Ta metoda wymaga sporo przygotowań, aby mogła być produktywna, ale wiele badań pokazuje, że spędzony tym czas dobrze się opłaca. Żeby zaś uczynić ten rachunek jeszcze atrakcyjniejszym, dostępne są dzis programy pozwalające zautomatyzować dużą część pracy. Istnieją programy sprawdzające zgodność kodu źródłowego z wieloma istniejącymi standardami i z własnymi regułami, które można dostosowywać do potrzeb klienta. Również kompilatory są dzisiaj na tyle udoskonalone, że uruchomiwszy pełny arsenał ich możliwośi kontrolnych, można automatycznie zidentyfikowac większość problemów znajdujących się na omówionej na poprzednich stronach liście. Te narzędzia nie likwidują potrzeby przeglądów i inspekcji kodu, tylko ułatwiają je znacznie i dają testerom czas, aby móc szukać błędów jeszcze głębiej.

Jeśli w zespole projektowym nie testuje się obecnie na tym poziomie, a testerzy mają nieco doświadczenia w programowaniu, można zaproponować rozważenie takiej możliwości. Programiści i kierownictwo mogą się początkowo obawiać tego przedsięwzięcia, nie bardzo wierząc, by zyski rzeczywiście były aż tak duże – w końcu trudno jest udowodnić, że na przykład znalezienie błędu podczas inspekcji zaoszczędziło projektowi pięć dni, które trzeba by było spędzic wiele miesięcy później podczas testowania metodami czarnej skrzynki. Niemniej, statyczne testowanie metodami szklanej skrzynki nabiera coraz więcej impetu, i w niektórych branżach nikt nie odważy się dziś dostarczać niezawodnego oprogramowania, nie poddawszy go statycznemu testowaniu. Pytania

Pytania mają na celu ułatwienie zrozumienia. W aneksie A „Odpowiedzi do pytań” znajdują się prawidłowe rozwiązania – ale nie należy ściągać! 1.Wymień kilka korzyści wynikających ze stosowania statycznego testowania metodami szklanej skrzynki. 2.Prawda czy fałsz: statyczne testowanie metodami szklanej skrzynki pozwala znaleźć zarówno brakujące elementy jak i problemy. 3.Jakie są kluczowe elementy formalnych przeglądów? 4.Oprócz poziomu formalizmu, jaka jest zasadnicza różnica między inspekcjami a innymi rodzajami przeglądów? 5.Jeśli programista został poinformowany, że wolno mu używać nazw zmiennych nie dłuższych niż osiem znaków i wszystkie muszą zaczynać się dużą literą, czy mamy do czynienia ze standardem czy z regułą? 6.Czy listę kontrolną do przeglądów kodu opisaną w tym rozdziale powinno się przyjąć jako standard waszego zespołu do weryfikacji kodu?