Testowanie i jakość oprogramowania

Testowanie oprogramowania, choć kluczowe dla powodzenia projektów IT, wciąż jest niedocenianą dziedziną inżynierii oprog

1,973 76 19MB

Polish Pages [1545] Year 2015

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Testowanie i jakość oprogramowania

Table of contents :
Spis ilustracji
Spis tabel
Spis listingów
Znaki handlowe
Wstęp
CZĘŚĆ I. PODSTAWY TESTOWANIA
1. Wprowadzenie do testowania
1.1. Dlaczego testowanie jest niezbędne
1.2. Definicja testowania
1.3. Słynne przykłady awarii oprogramowania
1.4. Rys historyczny
1.5. Ogólne zasady testowania
1.6. Jak dużo testować i kiedy skończyć?
1.7. Psychologia testowania
1.8. Kodeks etyczny testera
1.9. Role związane z testowaniem
2. Podstawowe definicje3. Proces testowy
3.1. Podstawowy proces testowy
3.1.1. Planowanie
3.1.2. Monitorowanie i nadzór
3.1.3. Analiza testów
3.1.4. Projektowanie testów
3.1.5. Implementacja testów
3.1.6. Wykonanie testów
3.1.7. Ocena spełnienia kryteriów wyjścia oraz raportowanie
3.1.8. Czynności zamykające testowanie
3.2. Proces testowy wg ISO/IEC/IEEE 29119
4. Testowanie w cyklu życia oprogramowania
4.1. Modele wytwarzania oprogramowania
4.1.1. Model kaskadowy
4.1.2. Model V
4.1.3. Model W
4.1.4. Rational Unified Process (RUP)
4.1.5. Rapid Application Development (RAD)
4.1.6. Model spiralny Boehma
4.1.7. Metodyki zwinne
4.1.8. Metodologia Cleanroom
4.2. Weryfikacja i walidacja
4.3. Poziomy testów
4.3.1. Testy jednostkowe
4.3.2. Testy integracyjne
4.3.3. Testy systemowe
4.3.4. Testy akceptacyjne
4.3.5. Pozostałe poziomy testów
4.4. Typy testów
4.4.1. Testy funkcjonalne
4.4.2. Testy niefunkcjonalne
4.4.3. Testy strukturalne
4.4.4. Testy związane ze zmianami
4.5. Poziomy a typy testówCZĘŚĆ II. TECHNIKI PROJEKTOWANIA TESTÓW
5. Testowanie oparte na modelu
5.1. Cechy dobrego modelu
5.2. Taksonomia modeli
5.3. Przykład wykorzystania modelu
5.4. Modele działania oprogramowania
5.4.1. Graf przepływu sterowania
5.4.2. Ograniczenia w stosowaniu grafu przepływu sterowania
5.4.3. Graf przepływu danych
5.4.4. Ścieżki, ścieżki testowe i ścieżki nieosiągalne
6. Techniki testowania statycznego
6.1. Przeglądy
6.1.1. Proces dla testowania statycznego
6.1.2. Metody sprawdzania oraz możliwe wyniki przeglądu
6.1.3. Role
6.1.4. Aspekt psychologiczny przeglądów
6.1.5. Typy przeglądów
6.1.6. Biznesowa wartość przeglądów
6.1.7. Wdrażanie przeglądów
6.1.8. Kryteria sukcesu przeglądów
6.2. Analiza statyczna
6.2.1. Analiza przepływu sterowania
6.2.2. Poprawność sekwencji operacji
6.2.3. Analiza przepływu danych
6.2.4. Narzędzia do parsowania kodu
6.2.5. Testowanie zgodności ze standardami oprogramowania
6.2.6. Metryki złożoności kodu
6.2.7. Formalne dowodzenie poprawności
6.2.8. Symboliczne wykonywanie kodu
6.2.9. Analiza statyczna strony internetowej
6.2.10. Grafy wywołań
7. Analiza dynamiczna
7.1. Wykrywanie wycieków pamięci7.2. Wykrywanie dzikich i wiszących wskaźników
7.3. Błędy API
7.4. Analiza wydajności (profiling)
8. Techniki oparte na specyfikacji (czarnoskrzynkowe)
8.1. Podział na klasy równoważności
8.1.1. Opis metody
8.1.2. Formalna definicja podziału
8.1.3. Poprawne i niepoprawne klasy równoważności
8.1.4. Procedura tworzenia przypadków testowych
8.1.5. Przykład
8.1.6. Przykład śledzenia artefaktów procesu testowego
8.2. Analiza wartości brzegowych
8.2.1. Opis metody
8.2.2. Metody dwóch oraz trzech wartości granicznych
8.2.3. Które wartości rozważać jako brzegowe?
8.2.4. Przypadek zmiennych ciągłych
8.2.5. Przykład
8.3. Tablice decyzyjne
8.3.1. Opis metody
8.3.2. Wartości nieistotne i minimalizacja tablicy decyzyjnej
8.3.3. Przykład
8.4. Grafy przyczynowo-skutkowe
8.4.1. Opis metody
8.4.2. Przekształcanie między grafami P-S i tablicami decyzyjnymi
8.4.3. Metoda śledzenia wstecznego – redukcja liczby testów
8.4.4. Przykład
8.5. Testowanie przejść między stanami
8.5.1. Opis metody
8.5.2. Tabelaryczne reprezentacje przejść
8.5.3. Kryteria pokrycia dla maszyny stanowej
8.5.4. Diagram maszyny stanowej w UML
8.5.5. Przykład
8.6. Kategoria-podział (Category-Partition)
8.6.1. Opis metody
8.6.2. Przykład8.7. Drzewa klasyfikacji
8.7.1. Opis metody
8.7.2. Budowa drzewa klasyfikacji
8.7.3. Asortyment produktów programowych i model cech
8.7.4. Przykład
8.8. Metody kombinacyjne
8.8.1. Opis metody
8.8.2. Each Choice
8.8.3. Base Choice
8.8.4. Multiple Base Choice
8.8.5. Pair-wise testing
8.8.6. Pokrycie n-tupletów (n-wise})
8.8.7. Pełne pokrycie kombinatoryczne
8.8.8. Subsumpcja kryteriów kombinacyjnych
8.9. Testowanie dziedziny
8.9.1. Opis metody
8.9.2. Hiperpłaszczyzny i podprzestrzenie
8.9.3. Wyznaczanie hiperpłaszczyzny przez punkty
8.9.4. Punkty IN, OUT, ON i OFF
8.9.5. Strategia IN-OUT dla testowania dziedziny
8.9.6. Strategia N-ON × M-OFF dla testowania wartości brzegowych
8.9.7. Ograniczenia nieliniowe
8.9.8. Przykład
8.10. Testowanie oparte na przypadkach użycia
8.10.1. Opis metody
8.10.2. Przykład
8.11. Testowanie oparte na scenariuszach
8.11.1. Opis metody
8.11.2. Przykład
8.12. Testowanie oparte na historyjkach użytkownika
8.12.1. Opis metody
8.12.2. Przykład
8.13. Testowanie losowe
8.13.1. Opis metody
8.13.2. Wady i zalety testowania losowego8.13.3. Automatyzacja i problem wyroczni
8.13.4. Adaptive Random Testing (ART)
8.13.5. Losowanie danych wejściowych
8.13.6. Przykład
8.14. Testowanie oparte na składni
8.14.1. Opis metody
8.14.2. Notacja Backusa–Naura (BNF)
8.14.3. Tworzenie przypadków testowych
8.14.4. Przykład
8.15. Testowanie CRUD
8.15.1. Opis metody
8.15.2. Rozszerzenie metody
8.15.3. Przykład
8.16. Wybór i łączenie technik ze sobą
9. Techniki oparte na strukturze (białoskrzynkowe)
9.1. Testowanie instrukcji
9.1.1. Opis metody
9.1.2. Przykład
9.2. Testowanie gałęzi
9.2.1. Opis metody
9.2.2. Przykład
9.3. Testowanie decyzji
9.3.1. Opis metody
9.3.2. Testowanie decyzji a testowanie gałęzi
9.3.3. Przykład
9.4. Testowanie warunków
9.4.1. Opis metody
9.4.2. Przykład
9.5. Testowanie warunków/decyzji
9.5.1. Opis metody
9.5.2. Przykład
9.6. Testowanie wielokrotnych warunków
9.6.1. Opis metody
9.6.2. Zwarcie (semantyka short-circuit)
9.6.3. Nieosiągalne kombinacje warunków9.6.4. Przykład
9.7. Testowanie warunków znaczących (MC/DC)
9.7.1. Opis metody
9.7.2. Dwa warianty kryterium MC/DC
9.7.3. Skorelowane a ścisłe pokrycie warunków znaczących
9.7.4. Wyznaczanie wartości warunków pobocznych dla warunku
znaczącego
9.7.5. Przykład
9.8. Pokrycie MUMCUT oraz kryteria z nim związane
9.8.1. Opis metody
9.8.2. Kryteria MUTP, MNFP, CUTPNFP i MUMCUT
9.8.3. Przykład
9.8.4. Zdolność metody MUMCUT do wykrywania określonych typów
błędów
9.9. Testowanie pętli
9.9.1. Opis metody
9.9.2. Pętle zagnieżdżone
9.9.3. Testowanie wzorców pętli
9.9.4. Przykład
9.10. Liniowa sekwencja kodu i skok (LSKiS)
9.10.1. Opis metody
9.10.2. Przykład
9.10.3. LSKiS a DD-ścieżki i bloki podstawowe
9.11. Testowanie ścieżek pierwszych
9.11.1. Opis metody
9.11.2. Algorytm wyznaczania ścieżek pierwszych
9.11.3. Przykład
9.12. Analiza ścieżek
9.12.1. Wstęp
9.12.2. Testowanie wszystkich ścieżek
9.12.3. Ścieżki liniowo niezależne i testowanie ścieżek bazowych
9.12.4. Wyznaczanie ścieżek bazowych
9.12.5. Przykład
9.12.6. Inne kryteria pokrycia związane z testowaniem ścieżek
9.13. Testowanie przepływu danych9.13.1. Wstęp
9.13.2. Pokrycie wszystkich definicji (all-defs)
9.13.3. Pokrycie wszystkich użyć (all-uses)
9.13.4. Pokrycie wszystkich du-ścieżek (all-du-paths)
9.13.5. Uwagi o kryteriach pokrycia przepływu danych
9.13.6. Przykład
9.14. Objazdy i ścieżki poboczne (detours, sidetrips)
9.15. Testowanie mutacyjne
9.15.1. Wstęp
9.15.2. Rodzaje mutantów
9.15.3. Proces testowania mutacyjnego
9.15.4. Operatory mutacyjne
9.16. Subsumpcja kryteriów
10. Techniki oparte na defektach i na doświadczeniu
10.1. Wstrzykiwanie błędów
10.2. Taksonomie
10.3. Zgadywanie błędów
10.3.1. Opis metody
10.3.2. Przykład
10.4. Testowanie oparte na liście kontrolnej
10.5. Testowanie eksploracyjne
10.5.1. Opis metody
10.5.2. Przykład sesji testowania eksploracyjnego
10.6. Ataki usterkowe
10.7. Testowanie ad hoc
11. Wybór odpowiednich technik
12. Priorytetyzacja przypadków testowych
12.1. Wprowadzenie
12.2. Ocena priorytetyzacji – miara APFD
12.3. Techniki priorytetyzacji
CZĘŚĆ III. TESTOWANIE CHARAKTERYSTYK JAKOŚCIOWYCH
13. Model jakości według ISO 912614. Modele jakości według ISO 25010
15. Testowanie jakości użytkowej
15.1. Testowanie efektywności (effectiveness)
15.2. Testowanie wydajności (efficiency)
15.3. Testowanie satysfakcji (satisfaction)
15.3.1. Przydatność (usefulness)
15.3.2. Zaufanie (trust)
15.3.3. Przyjemność (pleasure)
15.3.4. Komfort (comfort)
15.4. Testowanie wolności od ryzyka (freedom from risk)
15.4.1. Ryzyko ekonomiczne (economic risk)
15.4.2. Ryzyko dotyczące zdrowia i bezpieczeństwa (health and safety
risk)
15.4.3. Ryzyko związane ze środowiskiem (environmental risk)
15.5. Testowanie kontekstu użycia (context coverage)
15.5.1. Zupełność kontekstu (context completeness)
15.5.2. Elastyczność (flexibility)
16. Testowanie jakości produktu
16.1. Testowanie funkcjonalnej przydatności (functional suitability)
16.1.1. Zupełność funkcjonalności (functional completeness)
16.1.2. Poprawność funkcjonalności (functional correctness)
16.1.3. Stosowność funkcjonalności (functional appropriateness)
16.2. Testowanie wydajności w działaniu (performance efficiency)
16.2.1. Zachowanie w czasie (time behaviour)
16.2.2. Zużycie zasobów (resource utilization)
16.2.3. Pojemność (capacity)
16.2.4. Techniki testowania wydajności
16.3. Testowanie zgodności (compatibility)
16.3.1. Współistnienie (co-existence)
16.3.2. Współdziałanie (interoperability)
16.3.3. Przykład
16.4. Testowanie użyteczności (usability)
16.4.1. Zrozumiałość (appropriateness recognizability)
16.4.2. Łatwość nauki (learnability)16.4.3. Łatwość użycia (operability)
16.4.4. Ochrona przed błędami użytkownika (user error protection)
16.4.5. Estetyka interfejsu użytkownika (user interface aesthetics)
16.4.6. Dostęp (accessibility)
16.5. Heurystyki dotyczące użyteczności
16.5.1. Heurystyki Nielsena
16.5.2. Laboratorium badania użyteczności (usability lab)
16.6. Testowanie niezawodności (reliability)
16.6.1. Dojrzałość (maturity)
16.6.2. Odporność na błędy (fault tolerance, robustness)
16.6.3. Odtwarzalność (recoverability)
16.6.4. Dostępność (availability)
16.7. Testowanie zabezpieczeń (security)
16.7.1. Poufność (confidentiality)
16.7.2. Integralność (integrity)
16.7.3. Niezaprzeczalność (non-repudiation)
16.7.4. Odpowiedzialność (accountability)
16.7.5. Uwierzytelnianie (authenticity)
16.8. Testowanie pielęgnowalności (maintainability)
16.8.1. Modularność (modularity)
16.8.2. Powtórne użycie (reusability)
16.8.3. Analizowalność (analyzability)
16.8.4. Modyfikowalność (modifiability)
16.8.5. Testowalność (testability)
16.9. Testowanie przenaszalności (portability)
16.9.1. Adaptowalność (adaptability)
16.9.2. Instalowalność (installability)
16.9.3. Zastępowalność (replaceability)
17. Testowanie jakości danych
17.1. Model jakości danych
17.2. Charakterystyki inherentne
17.2.1. Dokładność (accuracy)
17.2.2. Zupełność (completeness)
17.2.3. Spójność (consistency)
17.2.4. Wiarygodność (credibility)17.2.5. Aktualność (currentness)
17.3. Charakterystyki inherentne i zależne od systemu
17.3.1. Dostępność (accessibility)
17.3.2. Zgodność (compliance)
17.3.3. Poufność (confidentiality)
17.3.4. Wydajność (efficiency)
17.3.5. Precyzja (precision)
17.3.6. Identyfikowalność (traceability)
17.3.7. Zrozumiałość (understandability)
17.4. Charakterystyki zależne od systemu
17.4.1. Dostępność (availability)
17.4.2. Przenaszalność (portability)
17.4.3. Odtwarzalność (recoverability)
CZĘŚĆ IV. ZARZĄDZANIE TESTOWANIEM
18. Zarządzanie testowaniem w kontekście
18.1. Kontekst ograniczeń projektowych
18.2. Kontekst interesariuszy procesu testowego
18.3. Kontekst produkcji oprogramowania
18.4. Kontekst cyklu życia oprogramowania
18.5. Kontekst testów
18.6. Kontekst czynnika ludzkiego
19. Testowanie oparte na ryzyku
19.1. Czym jest ryzyko?
19.2. Zalety testowania opartego na ryzyku
19.3. Rodzaje ryzyka
19.4. Zarządzanie ryzykiem w cyklu życia
19.5. Identyfikacja ryzyka
19.5.1. Analiza interesariuszy (stakeholder analysis)
19.5.2. Technika „władza versus zainteresowanie”
19.5.3. Techniki identyfikacji ryzyk
19.5.4. Przykłady ryzyk produktowych
19.6. Analiza ryzyka
19.6.1. Klasyfikacja ryzyk19.6.2. Czynniki wpływające na prawdopodobieństwo i wpływ ryzyka
19.6.3. Ilościowa i jakościowa ocena ryzyka produktowego
19.6.4. Osiąganie konsensusu w procesie decyzyjnym
19.6.5. Priorytetyzacja ryzyk
19.7. Łagodzenie ryzyka
19.7.1. Sposoby łagodzenia ryzyka
19.7.2. Łagodzenie ryzyka przez testowanie
19.7.3. Estymacja kosztów łagodzenia ryzyka
19.8. Monitorowanie ryzyka
19.8.1. Macierz identyfikowalności ryzyk
19.8.2. Aktualizacja ryzyk oraz ich parametrów
19.8.3. Raportowanie
19.9. Techniki analizy ryzyka
19.9.1. PRAM (Pragmatic Risk Analysis and Management)
19.9.2. SST (Systematic Software Testing)
19.9.3. Przykład: zastosowanie SST do systemu ELROJ
19.9.4. PRisMa (Product Risk Management)
19.9.5. Przykład: zastosowanie PRisMa do systemu ELROJ
19.9.6. Analiza zagrożeń (hazard analysis)
19.9.7. Koszt ekspozycji ryzyka (cost of exposure)
19.9.8. FMEA (Failure Mode and Effect Analysis)
19.9.9. Przykład: zastosowanie FMEA do systemu ELROJ
19.9.10. QFD (Quality Function Deployment)
19.9.11. Przykład: zastosowanie QFD do systemu ELROJ
19.9.12. FTA (Fault Tree Analysis)
19.9.13. Przykład: zastosowanie FTA do systemu ELROJ
19.10. TMap (Test Management Approach)
19.11. TestGoal – testowanie oparte na wynikach
20. Pozostałe strategie testowania
20.1. Testowanie oparte na wymaganiach
20.1.1. Testowanie wymagań
20.1.2. Projektowanie testów opartych na wymaganiach
20.1.3. Przykład: obliczanie punktacji w grze w kręgle
20.2. Podejście oparte na modelu (profile operacyjne)
20.3. Podejście metodyczne (listy kontrolne)20.4. Podejście oparte na standardzie
20.5. Inne podejścia
20.5.1. Podejście reaktywne
20.5.2. Podejście good enough
20.5.3. Podejście konsultacyjne
21. Dokumentacja w zarządzaniu testowaniem
21.1. Dokumenty organizacyjnego procesu testowego
21.1.1. Polityka testów
21.1.2. Przykład polityki testów
21.1.3. Organizacyjna strategia testowania
21.1.4. Przykład organizacyjnej strategii testowania
21.2. Dokumenty procesu zarządzania testowaniem
21.2.1. Plan testów (test plan)
21.2.2. Przykład planu testów
21.2.3. Jednopoziomowy plan testów (level test plan)
21.2.4. Przykład jednopoziomowego planu testów
21.2.5. Raport o stanie testów (test status report)
21.2.6. Przykład raportu o stanie testów
21.2.7. Raport końcowy z testowania (test completion report)
21.2.8. Przykład raportu końcowego z testowania
21.3. Dokumenty dynamicznych procesów testowych
21.3.1. Specyfikacja testów (test design specification)
21.3.2. Przykład specyfikacji testów
21.3.3. Specyfikacja przypadku testowego (test case specification)
21.3.4. Przykład specyfikacji przypadku testowego
21.3.5. Specyfikacja procedury testowej (test procedure specification)
21.3.6. Przykład specyfikacji procedury testowej
21.3.7. Wymagania co do danych testowych (test data requirements)
21.3.8. Przykład wymagania co do danych testowych
21.3.9. Wymagania co do środowiska testowego (test environment
requirements)
21.3.10. Przykład wymagań co do środowiska testowego
21.3.11. Raport o gotowości danych testowych (test data readiness report)
21.3.12. Raport o gotowości środowiska testowego (test environment
readiness report)21.3.13. Otrzymane wyniki (actual results)
21.3.14. Wynik testu (test result)
21.3.15. Dziennik wykonania testów (test execution log)
21.3.16. Raport o incydencie (test incident report)
21.3.17. Raport z sesji testowania eksploracyjnego (exploratory testing
session report)
21.3.18. Przykład raportu z sesji testowania eksploracyjnego
22. Szacowanie testów
22.1. Czynniki wpływające na szacowanie
22.2. Techniki szacowania
22.2.1. Intuicja, zgadywanie, doświadczenie
22.2.2. Estymacja przez analogię
22.2.3. Struktura podziału prac (Work Breakdown Structure, WBS)
22.2.4. Estymacja grupowa
22.2.5. Dane przemysłowe
22.2.6. Analiza punktów testowych (Test Point Analysis)
22.2.7. Modele matematyczne (parametryczne)
22.3. Negocjacje i redukcja zakresu testów
23. Nadzór i kontrola postępu testów
23.1. Wprowadzenie
23.2. Przykłady metryk
23.2.1. Metryki ryzyka produktowego
23.2.2. Metryki defektów
23.2.3. Metryki przypadków testowych
23.2.4. Metryki pokrycia
23.2.5. Metryki pewności
23.3. Zarządzanie testowaniem opartym na sesji
24. Biznesowa wartość testowania
24.1. Wprowadzenie
24.2. Koszt jakości
24.3. Wartość ekonomiczna oprogramowania i problemy z nią związane
24.4. Dług technologiczny w kontekście testowania
25. Testowanie rozproszone, zakontraktowane i zewnętrzne25.1. Zespoły w ramach organizacji
25.2. Zespoły w ramach innych oddziałów organizacji
25.3. Dostawcy sprzętu i oprogramowania
25.4. Dostawcy usług testowych
25.5. TaaS (Testing as a Service)
26. Zarządzanie wdrażaniem standardów przemysłowych
27. Zarządzanie incydentami
27.1. Cykl życia defektu
27.2. Atrybuty defektu i ODC
27.2.1. Atrybuty defektów
27.2.2. ODC (Orthogonal Defect Classification)
27.2.3. Przykładowe analizy przy użyciu ODC
27.3. Metryki zarządzania incydentami
27.4. Zawartość raportu o incydencie
27.5. Komunikowanie incydentów
CZĘŚĆ V. LUDZIE I NARZĘDZIA
28. Ludzie i ich kompetencje – tworzenie zespołu
28.1. Budowanie zespołu testowego
28.1.1. Opis stanowiska
28.1.2. Analiza CV
28.1.3. Rozmowa kwalifikacyjna
28.1.4. Asymilacja nowego członka zespołu
28.1.5. Zakończenie zatrudnienia
28.2. Rozwój zespołu testowego
28.2.1. Indywidualny rozwój poszczególnych członków zespołu
28.2.2. Wyznaczanie celów
28.2.3. Dynamika zespołu testowego
28.2.4. Określanie ról i odpowiedzialności
28.2.5. Umiejętności zespołu jako całości: gap analysis
28.2.6. Indywidualne osobowości i role w zespole
28.2.7. Rozwój umiejętności i szkolenia
28.2.8. Mentoring28.2.9. Okresowa ocena członków zespołu
28.3. Przywództwo
28.3.1. Zarządzanie a przywództwo
28.3.2. Model zmiany według Satir
28.3.3. Cechy charakteru lidera i zasady przywództwa
28.3.4. Informowanie i komunikacja
28.3.5. Lojalność, zaufanie i odpowiedzialność
28.3.6. Budowanie zespołu
28.4. Poziomy niezależności zespołu testowego
28.5. Komunikacja
29. Techniki pracy grupowej
29.1. Proces podejmowania decyzji
29.2. Techniki wspomagania kreatywności
29.2.1. Burza mózgów
29.2.2. NGT (Nominal Group Technique)
29.2.3. Metoda analogii
29.2.4. JAD (Joint Application Development)
29.3. Techniki porządkowania i priorytetyzacji
29.3.1. Diagram podobieństwa (affinity diagram)
29.3.2. Macierz i graf priorytetyzacji
29.3.3. Mapa myśli
29.4. Techniki szacowania i oceny
29.4.1. Wielokrotne głosowanie
29.4.2. Metoda delficka i Wideband Delphi
29.4.3. Poker planistyczny (planning poker)
29.4.4. Model Saaty’ego (Analytic Hierarchy Process)
29.5. Definiowanie problemu, przyczyny źródłowej lub możliwości
29.5.1. Analiza pola sił (force field analysis)
29.5.2. Immersja
29.5.3. Diagram rybiej ości (diagram Ishikawy)
30. Testowanie wspierane narzędziami
30.1. Podstawowe zagadnienia związane z użyciem narzędzi
30.1.1. Wybór odpowiedniego narzędzia
30.1.2. Koszty wdrożenia narzędzia30.1.3. Ryzyka związane z wdrożeniem narzędzia
30.1.4. Korzyści z wdrożenia narzędzia
30.1.5. Strategie wdrażania automatyzacji
30.1.6. Integracja i wymiana informacji między narzędziami
30.1.7. Klasyfikacja narzędzi testowych
30.2. Automatyzacja testów
30.2.1. Czynniki sukcesu udanej automatyzacji
30.2.2. Metryki dla automatyzacji testów
30.3. Generyczna architektura automatyzacji testów
30.3.1. Warstwa generowania testów
30.3.2. Warstwa definiowania testów
30.3.3. Warstwa wykonania testów
30.3.4. Warstwa adaptacji testów
30.3.5. Zarządzanie konfiguracją
30.4. Automatyczna generacja danych testowych
30.5. Metody i techniki automatyzacji testów
30.5.1. Kroki procesu projektowania architektury
30.5.2. Podejścia do automatyzacji przypadków testowych
30.5.3. Podejście nagraj i odtwórz
30.5.4. Skrypty linearne
30.5.5. Skrypty zorganizowane
30.5.6. Testowanie oparte na danych
30.5.7. Testowanie oparte na słowach kluczowych
30.5.8. Testowanie oparte na procesie
30.5.9. Testowanie oparte na modelu
30.5.10. Języki i notacje dla definicji testów
30.6. Katalog narzędzi testowych
30.6.1. Narzędzia do zarządzania testami
30.6.2. Narzędzia do wykonywania/organizacji testów
30.6.3. Narzędzia do testowania mutacyjnego, posiewu usterek
i wstrzykiwania błędów
30.6.4. Narzędzia do testów bezpieczeństwa
30.6.5. Symulatory i emulatory
30.6.6. Narzędzia do analizy statycznej i dynamicznej
30.6.7. Narzędzia do testów wydajnościowych30.6.8. Narzędzia typu capture and replay
30.6.9. Narzędzia do testów jednostkowych
30.6.10. Testowanie w metodyce BDD
30.6.11. Narzędzia do testowania opartego na modelu
30.6.12. Narzędzia do śledzenia defektów, zarządzania incydentami
i raportowania
30.7. Wdrażanie narzędzi w organizacji
30.7.1. Cykl życia narzędzia
30.7.2. Użycie narzędzi open source
30.7.3. Przykładowe problemy związane z użyciem narzędzi
CZĘŚĆ VI. UDOSKONALANIE PROCESU TESTOWEGO
31. Kontekst udoskonalania procesu
31.1. Po co udoskonalać?
31.2. Co można udoskonalić?
31.3. Spojrzenia na jakość
31.3.1. Jakość oparta na produkcie
31.3.2. Jakość oparta na użytkowniku
31.3.3. Jakość oparta na wytwarzaniu
31.3.4. Jakość oparta na wartości
31.3.5. Jakość transcendentna
31.4. Generyczny proces udoskonalania
31.4.1. Cykl Deminga–Shewarta (PDCA)
31.4.2. IDEAL
31.4.3. Podstawowe zasady doskonałości (Fundamental Concepts of
Excellence)
31.5. Przegląd podejść do udoskonalania
31.5.1. Podejścia oparte na modelu
31.5.2. Podejścia analityczne
31.5.3. Podejścia hybrydowe
31.5.4. Inne podejścia do udoskonalania procesu testowego
32. Udoskonalanie oparte na modelu
32.1. Wprowadzenie
32.2. Udoskonalanie procesów organizacyjnych i biznesowych32.2.1. TQM (Total Quality Management)
32.2.2. ISO 9000
32.2.3. EFQM Excellence Model
32.2.4. Six Sigma
32.2.5. Lean
32.3. Udoskonalanie procesu produkcji oprogramowania
32.3.1. CMMI
32.3.2. ISO/IEC 15504
32.3.3. ITIL (Information Technology Infrastructure Library)
32.3.4. TSP (Team Software Process)
32.3.5. BOOTSTRAP
32.4. Udoskonalanie procesu testowego – modele referencyjne procesu
32.4.1. TPI Next (Test Process Improvement)
32.4.2. TMMi (Test Maturity Model integration)
32.4.3. Porównanie TPI Next i TMMi
32.5. Udoskonalanie procesu testowego – modele referencyjne zawartości
32.5.1. STEP (Systematic Test and Evaluation Process)
32.5.2. CTP (Critical Testing Processes)
33. Podejście analityczne
33.1. Wprowadzenie
33.2. Analiza przyczynowa
33.2.1. Opis metody
33.2.2. Wybór elementów do analizy przyczynowej
33.2.3. Zebranie i zorganizowanie informacji
33.2.4. Identyfikacja przyczyny źródłowej przez analizę zebranych
informacji
33.2.5. Wyciągnięcie wniosków
33.3. Podejście GQM (Goal–Question–Metric)
33.3.1. Opis metody
33.3.2. Fazy GQM
33.3.3. Dwa paradygmaty metody GQM
33.3.4. Wzorzec definiowania celu
33.3.5. Siedem pytań
33.3.6. Przykład
33.4. Miary, metryki i wskaźniki34. Wybór metody usprawniania
35. Proces udoskonalania
35.1. Wprowadzenie
35.2. Rozpoczęcie procesu doskonalenia
35.2.1. Określenie powodu doskonalenia (stymulacja do zmiany)
35.2.2. Ustanowienie celów dla doskonalenia testowania
35.2.3. Określenie kontekstu
35.2.4. Pozyskanie wsparcia
35.2.5. Stworzenie infrastruktury dla procesu udoskonalania
35.3. Diagnozowanie aktualnej sytuacji
35.3.1. Scharakteryzowanie obecnego oraz pożądanego stanu procesu
35.3.2. Rekomendacje akcji naprawczych
35.4. Ustanowienie planu doskonalenia procesu testowego
35.4.1. Ustanowienie priorytetów dla wdrażania planu doskonalenia
35.4.2. Opracowanie podejścia do wdrożenia
35.4.3. Zaplanowanie działań związanych z wdrożeniem
35.5. Działanie w celu wdrożenia udoskonaleń
35.5.1. Stworzenie rozwiązania
35.5.2. Rozwiązania pilotażowe/testowe
35.5.3. Doprecyzowanie rozwiązania
35.5.4. Zaimplementowanie rozwiązania
35.6. Wyciąganie wniosków z projektu doskonalenia testów
35.6.1. Analiza i weryfikacja przeprowadzonych działań
35.6.2. Propozycje przyszłych rozwiązań
36. Organizacja, role i umiejętności
36.1. Organizacja
36.1.1. Zakres działań GPT
36.1.2. Organizacja Grupy procesu testowego
36.1.3. Właściwości Grupy procesu testowego
36.2. Role i umiejętności
36.2.1. Doskonalący proces testowy
36.2.2. Główny oceniający
36.2.3. Oceniający
36.2.4. Umiejętności doskonalącego proces testowy37. Czynniki sukcesu
CZĘŚĆ VII. JAKOŚĆ OPROGRAMOWANIA
38. Czym jest jakość oprogramowania?
38.1. Testowanie oprogramowania a jakość oprogramowania
38.2. Model Kano
38.3. Dojrzałość procesu i standardy jakości
38.3.1. SPR
38.3.2. Ocena Malcolma Baldridge’a
38.4. Co mierzyć, jak mierzyć i po co mierzyć?
39. Podstawy teorii pomiarów
39.1. Metryka, miara, wskaźnik, pomiar
39.2. Skale pomiarowe
39.2.1. Skala nominalna
39.2.2. Skala porządkowa
39.2.3. Skala interwałowa
39.2.4. Skala stosunkowa
39.2.5. Podsumowanie rodzajów skal pomiarowych
39.3. Typy metryk
39.3.1. Metryka bezpośrednia (podstawowa)
39.3.2. Suma/różnica
39.3.3. Stosunek
39.3.4. Proporcja
39.3.5. Odsetek
39.3.6. Miary iloczynowe
39.3.7. Tempo
39.4. Spójność i odpowiedniość pomiaru
39.5. Błędy pomiarowe
39.6. Podstawowe zasady analizy danych
39.6.1. Miary tendencji centralnej
39.6.2. Miary rozproszenia
39.6.3. Korelacja i regresja liniowa
39.6.4. Przyczynowość40. Narzędzia kontroli jakości
40.1. Klasyfikacja narzędzi
40.2. Rodzaje narzędzi oraz obszary ich zastosowań
40.3. Statystyczna kontrola procesu
40.3.1. Wykres przebiegu
40.3.2. Karty kontrolne
40.4. Wykres czasu cyklu
40.5. Narzędzia analizy i zapobiegania przyczynom źródłowym
40.5.1. 5 pytań „dlaczego?” i diagram „why–why”
40.5.2. Macierz „jest–nie jest”
40.5.3. Kaizen
40.5.4. Poka yoke
41. Metryki wielkości oprogramowania
41.1. Metryki wolumenowe
41.1.1. LOC
41.1.2. Współczynnik produktywności języka
41.1.3. Pomiar specyfikacji i projektu
41.2. Metryki funkcjonalności
41.2.1. Punkty funkcyjne
41.2.2. Punkty obiektowe i rozszerzone punkty obiektowe
41.2.3. Punkty cech
41.2.4. Punkty przypadków użycia
42. Metryki charakterystyk jakościowych
42.1. Metryki dla funkcjonalności
42.2. Metryki dla niezawodności
42.3. Metryki dla użyteczności
42.4. Metryki dla wydajności
42.5. Metryki dla pielęgnowalności
42.6. Metryki dla przenaszalności
43. Metryki złożoności oprogramowania
43.1. Metryki Halsteada
43.2. Złożoność cyklomatyczna McCabe’a
43.2.1. Gęstość złożoności cyklomatycznej43.2.2. ECC (Essential Cyclomatic Complexity)
43.3. Konstrukcje składniowe
43.4. Metryki struktur
43.5. Metryki złożoności systemu
43.5.1. Indeks utrzymywalności
43.5.2. Metryka złożoności systemu Agrestiego–Carda–Glassa
43.6. Metryki obiektowe
43.6.1. Metryki Lorenza
43.6.2. Metryki CK
43.7. Metryki złożoności dokumentacji
43.8. Metryki złożoności algorytmicznej
44. Metryki i modele wysiłku
44.1. Modele oparte na zgadywaniu i intuicji
44.2. Modele oparte na dekompozycji
44.3. Modele oparte na wiedzy eksperckiej
44.4. Modele oparte na benchmarkach
44.5. Modele oparte na porównaniu
44.5.1. Porównanie proste (naiwne)
44.5.2. Porównanie z uwzględnieniem różnic
44.6. Modele parametryczne
44.6.1. Tworzenie własnego modelu
44.6.2. Model COCOMO II
44.7. Łączenie modeli i uwagi na temat estymacji
45. Metryki i modele dla defektów
45.1. Natura defektów
45.2. Metryki defektów
45.3. Modele statyczne defektów
45.3.1. Model wprowadzania/usuwania defektów
45.3.2. Model fazowy
45.3.3. Model dwufazowy
45.3.4. Efektywność usuwania defektów i powstrzymanie fazowe
45.3.5. Modele zmian w kodzie
45.4. Modele dynamiczne defektów
45.4.1. Model Rayleigha45.4.2. Model wykładniczy i S-kształtny
45.4.3. Model COQUALMO
45.5. Analiza mutacyjna
45.6. Metryki dynamicznej stylometrii
46. Metryki i modele przyrostu niezawodności
46.1. Wprowadzenie
46.2. Matematyczne podstawy teorii niezawodności
46.2.1. Funkcja niezawodności i funkcja awarii
46.2.2. Metryka MTTF (średniego czasu do awarii)
46.2.3. Rozkłady prawdopodobieństwa modelujące występowanie awarii
46.2.4. Rozkład wykładniczy i jego związek z metryką MTTF
46.2.5. Funkcja częstości awarii oraz ryzyko (hazard rate)
46.2.6. Prawdopodobieństwo awarii do czasu t
46.3. Modele przyrostu niezawodności
46.3.1. Model Jelinskiego–Morandy
46.3.2. Model niedoskonałego debugowania Goela–Okumoto
46.3.3. Niejednorodny model procesu Poissona Goela–Okumoto
46.3.4. Logarytmiczny model Poissona czasu wykonywania Musy–
Okumoto
46.3.5. Model S-kształtny
46.3.6. Inne modele niezawodności
47. Metryki i modele dostępności
47.1. Dostępność
47.1.1. Dostępność ciągła a dostępność wysoka
47.1.2. Metody zwiększające dostępność systemu
47.1.3. Ilościowa miara dostępności i jej obliczanie
47.2. Odmładzanie oprogramowania
47.2.1. Powody degradacji i sposoby odmładzania oprogramowania
47.2.2. Wpływ odmładzania na dostępność systemu
48. Metryki dla procesu testowego
49. Metryki zadowolenia klienta
49.1. Proces pomiaru zadowolenia klienta
49.1.1. Wybór metody i sposobu przeprowadzenia badania49.1.2. Opracowanie ankiety/kwestionariusza lub innego narzędzia
badań
49.1.3. Wybór metody próbkowania
49.1.4. Wybór rozmiaru próby
49.1.5. Zebranie, opracowanie i analiza danych
49.2. Dział wsparcia klienta
50. Sposób prezentowania danych
50.1. Prezentowanie danych graficznych
50.2. Prezentowanie metryk
Dodatek A
Dodatek B
Dodatek C
Dodatek D
Bibliografia
O autorze

Citation preview

Spis treści

Spis ilustracji Spis tabel Spis listingów Znaki handlowe Wstęp CZĘŚĆ I. PODSTAWY TESTOWANIA 1. Wprowadzenie do testowania 1.1. Dlaczego testowanie jest niezbędne 1.2. Definicja testowania 1.3. Słynne przykłady awarii oprogramowania 1.4. Rys historyczny 1.5. Ogólne zasady testowania 1.6. Jak dużo testować i kiedy skończyć? 1.7. Psychologia testowania 1.8. Kodeks etyczny testera 1.9. Role związane z testowaniem

2. Podstawowe definicje

3. Proces testowy 3.1. Podstawowy proces testowy 3.1.1. Planowanie 3.1.2. Monitorowanie i nadzór 3.1.3. Analiza testów 3.1.4. Projektowanie testów 3.1.5. Implementacja testów 3.1.6. Wykonanie testów 3.1.7. Ocena spełnienia kryteriów wyjścia oraz raportowanie 3.1.8. Czynności zamykające testowanie 3.2. Proces testowy wg ISO/IEC/IEEE 29119

4. Testowanie w cyklu życia oprogramowania 4.1. Modele wytwarzania oprogramowania 4.1.1. Model kaskadowy 4.1.2. Model V 4.1.3. Model W 4.1.4. Rational Unified Process (RUP) 4.1.5. Rapid Application Development (RAD) 4.1.6. Model spiralny Boehma 4.1.7. Metodyki zwinne 4.1.8. Metodologia Cleanroom 4.2. Weryfikacja i walidacja 4.3. Poziomy testów 4.3.1. Testy jednostkowe 4.3.2. Testy integracyjne 4.3.3. Testy systemowe 4.3.4. Testy akceptacyjne 4.3.5. Pozostałe poziomy testów 4.4. Typy testów 4.4.1. Testy funkcjonalne 4.4.2. Testy niefunkcjonalne 4.4.3. Testy strukturalne 4.4.4. Testy związane ze zmianami 4.5. Poziomy a typy testów

CZĘŚĆ II. TECHNIKI PROJEKTOWANIA TESTÓW 5. Testowanie oparte na modelu 5.1. Cechy dobrego modelu 5.2. Taksonomia modeli 5.3. Przykład wykorzystania modelu 5.4. Modele działania oprogramowania 5.4.1. Graf przepływu sterowania 5.4.2. Ograniczenia w stosowaniu grafu przepływu sterowania 5.4.3. Graf przepływu danych 5.4.4. Ścieżki, ścieżki testowe i ścieżki nieosiągalne

6. Techniki testowania statycznego 6.1. Przeglądy 6.1.1. Proces dla testowania statycznego 6.1.2. Metody sprawdzania oraz możliwe wyniki przeglądu 6.1.3. Role 6.1.4. Aspekt psychologiczny przeglądów 6.1.5. Typy przeglądów 6.1.6. Biznesowa wartość przeglądów 6.1.7. Wdrażanie przeglądów 6.1.8. Kryteria sukcesu przeglądów 6.2. Analiza statyczna 6.2.1. Analiza przepływu sterowania 6.2.2. Poprawność sekwencji operacji 6.2.3. Analiza przepływu danych 6.2.4. Narzędzia do parsowania kodu 6.2.5. Testowanie zgodności ze standardami oprogramowania 6.2.6. Metryki złożoności kodu 6.2.7. Formalne dowodzenie poprawności 6.2.8. Symboliczne wykonywanie kodu 6.2.9. Analiza statyczna strony internetowej 6.2.10. Grafy wywołań

7. Analiza dynamiczna 7.1. Wykrywanie wycieków pamięci

7.2. Wykrywanie dzikich i wiszących wskaźników 7.3. Błędy API 7.4. Analiza wydajności (profiling)

8. Techniki oparte na specyfikacji (czarnoskrzynkowe) 8.1. Podział na klasy równoważności 8.1.1. Opis metody 8.1.2. Formalna definicja podziału 8.1.3. Poprawne i niepoprawne klasy równoważności 8.1.4. Procedura tworzenia przypadków testowych 8.1.5. Przykład 8.1.6. Przykład śledzenia artefaktów procesu testowego 8.2. Analiza wartości brzegowych 8.2.1. Opis metody 8.2.2. Metody dwóch oraz trzech wartości granicznych 8.2.3. Które wartości rozważać jako brzegowe? 8.2.4. Przypadek zmiennych ciągłych 8.2.5. Przykład 8.3. Tablice decyzyjne 8.3.1. Opis metody 8.3.2. Wartości nieistotne i minimalizacja tablicy decyzyjnej 8.3.3. Przykład 8.4. Grafy przyczynowo-skutkowe 8.4.1. Opis metody 8.4.2. Przekształcanie między grafami P-S i tablicami decyzyjnymi 8.4.3. Metoda śledzenia wstecznego – redukcja liczby testów 8.4.4. Przykład 8.5. Testowanie przejść między stanami 8.5.1. Opis metody 8.5.2. Tabelaryczne reprezentacje przejść 8.5.3. Kryteria pokrycia dla maszyny stanowej 8.5.4. Diagram maszyny stanowej w UML 8.5.5. Przykład 8.6. Kategoria-podział (Category-Partition) 8.6.1. Opis metody 8.6.2. Przykład

8.7. Drzewa klasyfikacji 8.7.1. Opis metody 8.7.2. Budowa drzewa klasyfikacji 8.7.3. Asortyment produktów programowych i model cech 8.7.4. Przykład 8.8. Metody kombinacyjne 8.8.1. Opis metody 8.8.2. Each Choice 8.8.3. Base Choice 8.8.4. Multiple Base Choice 8.8.5. Pair-wise testing 8.8.6. Pokrycie n-tupletów (n-wise}) 8.8.7. Pełne pokrycie kombinatoryczne 8.8.8. Subsumpcja kryteriów kombinacyjnych 8.9. Testowanie dziedziny 8.9.1. Opis metody 8.9.2. Hiperpłaszczyzny i podprzestrzenie 8.9.3. Wyznaczanie hiperpłaszczyzny przez punkty 8.9.4. Punkty IN, OUT, ON i OFF 8.9.5. Strategia IN-OUT dla testowania dziedziny 8.9.6. Strategia N-ON × M-OFF dla testowania wartości brzegowych 8.9.7. Ograniczenia nieliniowe 8.9.8. Przykład 8.10. Testowanie oparte na przypadkach użycia 8.10.1. Opis metody 8.10.2. Przykład 8.11. Testowanie oparte na scenariuszach 8.11.1. Opis metody 8.11.2. Przykład 8.12. Testowanie oparte na historyjkach użytkownika 8.12.1. Opis metody 8.12.2. Przykład 8.13. Testowanie losowe 8.13.1. Opis metody 8.13.2. Wady i zalety testowania losowego

8.13.3. Automatyzacja i problem wyroczni 8.13.4. Adaptive Random Testing (ART) 8.13.5. Losowanie danych wejściowych 8.13.6. Przykład 8.14. Testowanie oparte na składni 8.14.1. Opis metody 8.14.2. Notacja Backusa–Naura (BNF) 8.14.3. Tworzenie przypadków testowych 8.14.4. Przykład 8.15. Testowanie CRUD 8.15.1. Opis metody 8.15.2. Rozszerzenie metody 8.15.3. Przykład 8.16. Wybór i łączenie technik ze sobą

9. Techniki oparte na strukturze (białoskrzynkowe) 9.1. Testowanie instrukcji 9.1.1. Opis metody 9.1.2. Przykład 9.2. Testowanie gałęzi 9.2.1. Opis metody 9.2.2. Przykład 9.3. Testowanie decyzji 9.3.1. Opis metody 9.3.2. Testowanie decyzji a testowanie gałęzi 9.3.3. Przykład 9.4. Testowanie warunków 9.4.1. Opis metody 9.4.2. Przykład 9.5. Testowanie warunków/decyzji 9.5.1. Opis metody 9.5.2. Przykład 9.6. Testowanie wielokrotnych warunków 9.6.1. Opis metody 9.6.2. Zwarcie (semantyka short-circuit) 9.6.3. Nieosiągalne kombinacje warunków

9.6.4. Przykład 9.7. Testowanie warunków znaczących (MC/DC) 9.7.1. Opis metody 9.7.2. Dwa warianty kryterium MC/DC 9.7.3. Skorelowane a ścisłe pokrycie warunków znaczących 9.7.4. Wyznaczanie wartości warunków pobocznych dla warunku znaczącego 9.7.5. Przykład 9.8. Pokrycie MUMCUT oraz kryteria z nim związane 9.8.1. Opis metody 9.8.2. Kryteria MUTP, MNFP, CUTPNFP i MUMCUT 9.8.3. Przykład 9.8.4. Zdolność metody MUMCUT do wykrywania określonych typów błędów 9.9. Testowanie pętli 9.9.1. Opis metody 9.9.2. Pętle zagnieżdżone 9.9.3. Testowanie wzorców pętli 9.9.4. Przykład 9.10. Liniowa sekwencja kodu i skok (LSKiS) 9.10.1. Opis metody 9.10.2. Przykład 9.10.3. LSKiS a DD-ścieżki i bloki podstawowe 9.11. Testowanie ścieżek pierwszych 9.11.1. Opis metody 9.11.2. Algorytm wyznaczania ścieżek pierwszych 9.11.3. Przykład 9.12. Analiza ścieżek 9.12.1. Wstęp 9.12.2. Testowanie wszystkich ścieżek 9.12.3. Ścieżki liniowo niezależne i testowanie ścieżek bazowych 9.12.4. Wyznaczanie ścieżek bazowych 9.12.5. Przykład 9.12.6. Inne kryteria pokrycia związane z testowaniem ścieżek 9.13. Testowanie przepływu danych

9.13.1. Wstęp 9.13.2. Pokrycie wszystkich definicji (all-defs) 9.13.3. Pokrycie wszystkich użyć (all-uses) 9.13.4. Pokrycie wszystkich du-ścieżek (all-du-paths) 9.13.5. Uwagi o kryteriach pokrycia przepływu danych 9.13.6. Przykład 9.14. Objazdy i ścieżki poboczne (detours, sidetrips) 9.15. Testowanie mutacyjne 9.15.1. Wstęp 9.15.2. Rodzaje mutantów 9.15.3. Proces testowania mutacyjnego 9.15.4. Operatory mutacyjne 9.16. Subsumpcja kryteriów

10. Techniki oparte na defektach i na doświadczeniu 10.1. Wstrzykiwanie błędów 10.2. Taksonomie 10.3. Zgadywanie błędów 10.3.1. Opis metody 10.3.2. Przykład 10.4. Testowanie oparte na liście kontrolnej 10.5. Testowanie eksploracyjne 10.5.1. Opis metody 10.5.2. Przykład sesji testowania eksploracyjnego 10.6. Ataki usterkowe 10.7. Testowanie ad hoc

11. Wybór odpowiednich technik 12. Priorytetyzacja przypadków testowych 12.1. Wprowadzenie 12.2. Ocena priorytetyzacji – miara APFD 12.3. Techniki priorytetyzacji

CZĘŚĆ III. TESTOWANIE CHARAKTERYSTYK JAKOŚCIOWYCH 13. Model jakości według ISO 9126

14. Modele jakości według ISO 25010 15. Testowanie jakości użytkowej 15.1. Testowanie efektywności (effectiveness) 15.2. Testowanie wydajności (efficiency) 15.3. Testowanie satysfakcji (satisfaction) 15.3.1. Przydatność (usefulness) 15.3.2. Zaufanie (trust) 15.3.3. Przyjemność (pleasure) 15.3.4. Komfort (comfort) 15.4. Testowanie wolności od ryzyka (freedom from risk) 15.4.1. Ryzyko ekonomiczne (economic risk) 15.4.2. Ryzyko dotyczące zdrowia i bezpieczeństwa (health and safety risk) 15.4.3. Ryzyko związane ze środowiskiem (environmental risk) 15.5. Testowanie kontekstu użycia (context coverage) 15.5.1. Zupełność kontekstu (context completeness) 15.5.2. Elastyczność (flexibility)

16. Testowanie jakości produktu 16.1. Testowanie funkcjonalnej przydatności (functional suitability) 16.1.1. Zupełność funkcjonalności (functional completeness) 16.1.2. Poprawność funkcjonalności (functional correctness) 16.1.3. Stosowność funkcjonalności (functional appropriateness) 16.2. Testowanie wydajności w działaniu (performance efficiency) 16.2.1. Zachowanie w czasie (time behaviour) 16.2.2. Zużycie zasobów (resource utilization) 16.2.3. Pojemność (capacity) 16.2.4. Techniki testowania wydajności 16.3. Testowanie zgodności (compatibility) 16.3.1. Współistnienie (co-existence) 16.3.2. Współdziałanie (interoperability) 16.3.3. Przykład 16.4. Testowanie użyteczności (usability) 16.4.1. Zrozumiałość (appropriateness recognizability) 16.4.2. Łatwość nauki (learnability)

16.4.3. Łatwość użycia (operability) 16.4.4. Ochrona przed błędami użytkownika (user error protection) 16.4.5. Estetyka interfejsu użytkownika (user interface aesthetics) 16.4.6. Dostęp (accessibility) 16.5. Heurystyki dotyczące użyteczności 16.5.1. Heurystyki Nielsena 16.5.2. Laboratorium badania użyteczności (usability lab) 16.6. Testowanie niezawodności (reliability) 16.6.1. Dojrzałość (maturity) 16.6.2. Odporność na błędy (fault tolerance, robustness) 16.6.3. Odtwarzalność (recoverability) 16.6.4. Dostępność (availability) 16.7. Testowanie zabezpieczeń (security) 16.7.1. Poufność (confidentiality) 16.7.2. Integralność (integrity) 16.7.3. Niezaprzeczalność (non-repudiation) 16.7.4. Odpowiedzialność (accountability) 16.7.5. Uwierzytelnianie (authenticity) 16.8. Testowanie pielęgnowalności (maintainability) 16.8.1. Modularność (modularity) 16.8.2. Powtórne użycie (reusability) 16.8.3. Analizowalność (analyzability) 16.8.4. Modyfikowalność (modifiability) 16.8.5. Testowalność (testability) 16.9. Testowanie przenaszalności (portability) 16.9.1. Adaptowalność (adaptability) 16.9.2. Instalowalność (installability) 16.9.3. Zastępowalność (replaceability)

17. Testowanie jakości danych 17.1. Model jakości danych 17.2. Charakterystyki inherentne 17.2.1. Dokładność (accuracy) 17.2.2. Zupełność (completeness) 17.2.3. Spójność (consistency) 17.2.4. Wiarygodność (credibility)

17.2.5. Aktualność (currentness) 17.3. Charakterystyki inherentne i zależne od systemu 17.3.1. Dostępność (accessibility) 17.3.2. Zgodność (compliance) 17.3.3. Poufność (confidentiality) 17.3.4. Wydajność (efficiency) 17.3.5. Precyzja (precision) 17.3.6. Identyfikowalność (traceability) 17.3.7. Zrozumiałość (understandability) 17.4. Charakterystyki zależne od systemu 17.4.1. Dostępność (availability) 17.4.2. Przenaszalność (portability) 17.4.3. Odtwarzalność (recoverability)

CZĘŚĆ IV. ZARZĄDZANIE TESTOWANIEM 18. Zarządzanie testowaniem w kontekście 18.1. Kontekst ograniczeń projektowych 18.2. Kontekst interesariuszy procesu testowego 18.3. Kontekst produkcji oprogramowania 18.4. Kontekst cyklu życia oprogramowania 18.5. Kontekst testów 18.6. Kontekst czynnika ludzkiego

19. Testowanie oparte na ryzyku 19.1. Czym jest ryzyko? 19.2. Zalety testowania opartego na ryzyku 19.3. Rodzaje ryzyka 19.4. Zarządzanie ryzykiem w cyklu życia 19.5. Identyfikacja ryzyka 19.5.1. Analiza interesariuszy (stakeholder analysis) 19.5.2. Technika „władza versus zainteresowanie” 19.5.3. Techniki identyfikacji ryzyk 19.5.4. Przykłady ryzyk produktowych 19.6. Analiza ryzyka 19.6.1. Klasyfikacja ryzyk

19.6.2. Czynniki wpływające na prawdopodobieństwo i wpływ ryzyka 19.6.3. Ilościowa i jakościowa ocena ryzyka produktowego 19.6.4. Osiąganie konsensusu w procesie decyzyjnym 19.6.5. Priorytetyzacja ryzyk 19.7. Łagodzenie ryzyka 19.7.1. Sposoby łagodzenia ryzyka 19.7.2. Łagodzenie ryzyka przez testowanie 19.7.3. Estymacja kosztów łagodzenia ryzyka 19.8. Monitorowanie ryzyka 19.8.1. Macierz identyfikowalności ryzyk 19.8.2. Aktualizacja ryzyk oraz ich parametrów 19.8.3. Raportowanie 19.9. Techniki analizy ryzyka 19.9.1. PRAM (Pragmatic Risk Analysis and Management) 19.9.2. SST (Systematic Software Testing) 19.9.3. Przykład: zastosowanie SST do systemu ELROJ 19.9.4. PRisMa (Product Risk Management) 19.9.5. Przykład: zastosowanie PRisMa do systemu ELROJ 19.9.6. Analiza zagrożeń (hazard analysis) 19.9.7. Koszt ekspozycji ryzyka (cost of exposure) 19.9.8. FMEA (Failure Mode and Effect Analysis) 19.9.9. Przykład: zastosowanie FMEA do systemu ELROJ 19.9.10. QFD (Quality Function Deployment) 19.9.11. Przykład: zastosowanie QFD do systemu ELROJ 19.9.12. FTA (Fault Tree Analysis) 19.9.13. Przykład: zastosowanie FTA do systemu ELROJ 19.10. TMap (Test Management Approach) 19.11. TestGoal – testowanie oparte na wynikach

20. Pozostałe strategie testowania 20.1. Testowanie oparte na wymaganiach 20.1.1. Testowanie wymagań 20.1.2. Projektowanie testów opartych na wymaganiach 20.1.3. Przykład: obliczanie punktacji w grze w kręgle 20.2. Podejście oparte na modelu (profile operacyjne) 20.3. Podejście metodyczne (listy kontrolne)

20.4. Podejście oparte na standardzie 20.5. Inne podejścia 20.5.1. Podejście reaktywne 20.5.2. Podejście good enough 20.5.3. Podejście konsultacyjne

21. Dokumentacja w zarządzaniu testowaniem 21.1. Dokumenty organizacyjnego procesu testowego 21.1.1. Polityka testów 21.1.2. Przykład polityki testów 21.1.3. Organizacyjna strategia testowania 21.1.4. Przykład organizacyjnej strategii testowania 21.2. Dokumenty procesu zarządzania testowaniem 21.2.1. Plan testów (test plan) 21.2.2. Przykład planu testów 21.2.3. Jednopoziomowy plan testów (level test plan) 21.2.4. Przykład jednopoziomowego planu testów 21.2.5. Raport o stanie testów (test status report) 21.2.6. Przykład raportu o stanie testów 21.2.7. Raport końcowy z testowania (test completion report) 21.2.8. Przykład raportu końcowego z testowania 21.3. Dokumenty dynamicznych procesów testowych 21.3.1. Specyfikacja testów (test design specification) 21.3.2. Przykład specyfikacji testów 21.3.3. Specyfikacja przypadku testowego (test case specification) 21.3.4. Przykład specyfikacji przypadku testowego 21.3.5. Specyfikacja procedury testowej (test procedure specification) 21.3.6. Przykład specyfikacji procedury testowej 21.3.7. Wymagania co do danych testowych (test data requirements) 21.3.8. Przykład wymagania co do danych testowych 21.3.9. Wymagania co do środowiska testowego (test environment requirements) 21.3.10. Przykład wymagań co do środowiska testowego 21.3.11. Raport o gotowości danych testowych (test data readiness report) 21.3.12. Raport o gotowości środowiska testowego (test environment readiness report)

21.3.13. Otrzymane wyniki (actual results) 21.3.14. Wynik testu (test result) 21.3.15. Dziennik wykonania testów (test execution log) 21.3.16. Raport o incydencie (test incident report) 21.3.17. Raport z sesji testowania eksploracyjnego (exploratory testing session report) 21.3.18. Przykład raportu z sesji testowania eksploracyjnego

22. Szacowanie testów 22.1. Czynniki wpływające na szacowanie 22.2. Techniki szacowania 22.2.1. Intuicja, zgadywanie, doświadczenie 22.2.2. Estymacja przez analogię 22.2.3. Struktura podziału prac (Work Breakdown Structure, WBS) 22.2.4. Estymacja grupowa 22.2.5. Dane przemysłowe 22.2.6. Analiza punktów testowych (Test Point Analysis) 22.2.7. Modele matematyczne (parametryczne) 22.3. Negocjacje i redukcja zakresu testów

23. Nadzór i kontrola postępu testów 23.1. Wprowadzenie 23.2. Przykłady metryk 23.2.1. Metryki ryzyka produktowego 23.2.2. Metryki defektów 23.2.3. Metryki przypadków testowych 23.2.4. Metryki pokrycia 23.2.5. Metryki pewności 23.3. Zarządzanie testowaniem opartym na sesji

24. Biznesowa wartość testowania 24.1. Wprowadzenie 24.2. Koszt jakości 24.3. Wartość ekonomiczna oprogramowania i problemy z nią związane 24.4. Dług technologiczny w kontekście testowania

25. Testowanie rozproszone, zakontraktowane i zewnętrzne

25.1. Zespoły w ramach organizacji 25.2. Zespoły w ramach innych oddziałów organizacji 25.3. Dostawcy sprzętu i oprogramowania 25.4. Dostawcy usług testowych 25.5. TaaS (Testing as a Service)

26. Zarządzanie wdrażaniem standardów przemysłowych 27. Zarządzanie incydentami 27.1. Cykl życia defektu 27.2. Atrybuty defektu i ODC 27.2.1. Atrybuty defektów 27.2.2. ODC (Orthogonal Defect Classification) 27.2.3. Przykładowe analizy przy użyciu ODC 27.3. Metryki zarządzania incydentami 27.4. Zawartość raportu o incydencie 27.5. Komunikowanie incydentów

CZĘŚĆ V. LUDZIE I NARZĘDZIA 28. Ludzie i ich kompetencje – tworzenie zespołu 28.1. Budowanie zespołu testowego 28.1.1. Opis stanowiska 28.1.2. Analiza CV 28.1.3. Rozmowa kwalifikacyjna 28.1.4. Asymilacja nowego członka zespołu 28.1.5. Zakończenie zatrudnienia 28.2. Rozwój zespołu testowego 28.2.1. Indywidualny rozwój poszczególnych członków zespołu 28.2.2. Wyznaczanie celów 28.2.3. Dynamika zespołu testowego 28.2.4. Określanie ról i odpowiedzialności 28.2.5. Umiejętności zespołu jako całości: gap analysis 28.2.6. Indywidualne osobowości i role w zespole 28.2.7. Rozwój umiejętności i szkolenia 28.2.8. Mentoring

28.2.9. Okresowa ocena członków zespołu 28.3. Przywództwo 28.3.1. Zarządzanie a przywództwo 28.3.2. Model zmiany według Satir 28.3.3. Cechy charakteru lidera i zasady przywództwa 28.3.4. Informowanie i komunikacja 28.3.5. Lojalność, zaufanie i odpowiedzialność 28.3.6. Budowanie zespołu 28.4. Poziomy niezależności zespołu testowego 28.5. Komunikacja

29. Techniki pracy grupowej 29.1. Proces podejmowania decyzji 29.2. Techniki wspomagania kreatywności 29.2.1. Burza mózgów 29.2.2. NGT (Nominal Group Technique) 29.2.3. Metoda analogii 29.2.4. JAD (Joint Application Development) 29.3. Techniki porządkowania i priorytetyzacji 29.3.1. Diagram podobieństwa (affinity diagram) 29.3.2. Macierz i graf priorytetyzacji 29.3.3. Mapa myśli 29.4. Techniki szacowania i oceny 29.4.1. Wielokrotne głosowanie 29.4.2. Metoda delficka i Wideband Delphi 29.4.3. Poker planistyczny (planning poker) 29.4.4. Model Saaty’ego (Analytic Hierarchy Process) 29.5. Definiowanie problemu, przyczyny źródłowej lub możliwości 29.5.1. Analiza pola sił (force field analysis) 29.5.2. Immersja 29.5.3. Diagram rybiej ości (diagram Ishikawy)

30. Testowanie wspierane narzędziami 30.1. Podstawowe zagadnienia związane z użyciem narzędzi 30.1.1. Wybór odpowiedniego narzędzia 30.1.2. Koszty wdrożenia narzędzia

30.1.3. Ryzyka związane z wdrożeniem narzędzia 30.1.4. Korzyści z wdrożenia narzędzia 30.1.5. Strategie wdrażania automatyzacji 30.1.6. Integracja i wymiana informacji między narzędziami 30.1.7. Klasyfikacja narzędzi testowych 30.2. Automatyzacja testów 30.2.1. Czynniki sukcesu udanej automatyzacji 30.2.2. Metryki dla automatyzacji testów 30.3. Generyczna architektura automatyzacji testów 30.3.1. Warstwa generowania testów 30.3.2. Warstwa definiowania testów 30.3.3. Warstwa wykonania testów 30.3.4. Warstwa adaptacji testów 30.3.5. Zarządzanie konfiguracją 30.4. Automatyczna generacja danych testowych 30.5. Metody i techniki automatyzacji testów 30.5.1. Kroki procesu projektowania architektury 30.5.2. Podejścia do automatyzacji przypadków testowych 30.5.3. Podejście nagraj i odtwórz 30.5.4. Skrypty linearne 30.5.5. Skrypty zorganizowane 30.5.6. Testowanie oparte na danych 30.5.7. Testowanie oparte na słowach kluczowych 30.5.8. Testowanie oparte na procesie 30.5.9. Testowanie oparte na modelu 30.5.10. Języki i notacje dla definicji testów 30.6. Katalog narzędzi testowych 30.6.1. Narzędzia do zarządzania testami 30.6.2. Narzędzia do wykonywania/organizacji testów 30.6.3. Narzędzia do testowania mutacyjnego, posiewu usterek i wstrzykiwania błędów 30.6.4. Narzędzia do testów bezpieczeństwa 30.6.5. Symulatory i emulatory 30.6.6. Narzędzia do analizy statycznej i dynamicznej 30.6.7. Narzędzia do testów wydajnościowych

30.6.8. Narzędzia typu capture and replay 30.6.9. Narzędzia do testów jednostkowych 30.6.10. Testowanie w metodyce BDD 30.6.11. Narzędzia do testowania opartego na modelu 30.6.12. Narzędzia do śledzenia defektów, zarządzania incydentami i raportowania 30.7. Wdrażanie narzędzi w organizacji 30.7.1. Cykl życia narzędzia 30.7.2. Użycie narzędzi open source 30.7.3. Przykładowe problemy związane z użyciem narzędzi

CZĘŚĆ VI. UDOSKONALANIE PROCESU TESTOWEGO 31. Kontekst udoskonalania procesu 31.1. Po co udoskonalać? 31.2. Co można udoskonalić? 31.3. Spojrzenia na jakość 31.3.1. Jakość oparta na produkcie 31.3.2. Jakość oparta na użytkowniku 31.3.3. Jakość oparta na wytwarzaniu 31.3.4. Jakość oparta na wartości 31.3.5. Jakość transcendentna 31.4. Generyczny proces udoskonalania 31.4.1. Cykl Deminga–Shewarta (PDCA) 31.4.2. IDEAL 31.4.3. Podstawowe zasady doskonałości (Fundamental Concepts of Excellence) 31.5. Przegląd podejść do udoskonalania 31.5.1. Podejścia oparte na modelu 31.5.2. Podejścia analityczne 31.5.3. Podejścia hybrydowe 31.5.4. Inne podejścia do udoskonalania procesu testowego

32. Udoskonalanie oparte na modelu 32.1. Wprowadzenie 32.2. Udoskonalanie procesów organizacyjnych i biznesowych

32.2.1. TQM (Total Quality Management) 32.2.2. ISO 9000 32.2.3. EFQM Excellence Model 32.2.4. Six Sigma 32.2.5. Lean 32.3. Udoskonalanie procesu produkcji oprogramowania 32.3.1. CMMI 32.3.2. ISO/IEC 15504 32.3.3. ITIL (Information Technology Infrastructure Library) 32.3.4. TSP (Team Software Process) 32.3.5. BOOTSTRAP 32.4. Udoskonalanie procesu testowego – modele referencyjne procesu 32.4.1. TPI Next (Test Process Improvement) 32.4.2. TMMi (Test Maturity Model integration) 32.4.3. Porównanie TPI Next i TMMi 32.5. Udoskonalanie procesu testowego – modele referencyjne zawartości 32.5.1. STEP (Systematic Test and Evaluation Process) 32.5.2. CTP (Critical Testing Processes)

33. Podejście analityczne 33.1. Wprowadzenie 33.2. Analiza przyczynowa 33.2.1. Opis metody 33.2.2. Wybór elementów do analizy przyczynowej 33.2.3. Zebranie i zorganizowanie informacji 33.2.4. Identyfikacja przyczyny źródłowej przez analizę zebranych informacji 33.2.5. Wyciągnięcie wniosków 33.3. Podejście GQM (Goal–Question–Metric) 33.3.1. Opis metody 33.3.2. Fazy GQM 33.3.3. Dwa paradygmaty metody GQM 33.3.4. Wzorzec definiowania celu 33.3.5. Siedem pytań 33.3.6. Przykład 33.4. Miary, metryki i wskaźniki

34. Wybór metody usprawniania 35. Proces udoskonalania 35.1. Wprowadzenie 35.2. Rozpoczęcie procesu doskonalenia 35.2.1. Określenie powodu doskonalenia (stymulacja do zmiany) 35.2.2. Ustanowienie celów dla doskonalenia testowania 35.2.3. Określenie kontekstu 35.2.4. Pozyskanie wsparcia 35.2.5. Stworzenie infrastruktury dla procesu udoskonalania 35.3. Diagnozowanie aktualnej sytuacji 35.3.1. Scharakteryzowanie obecnego oraz pożądanego stanu procesu 35.3.2. Rekomendacje akcji naprawczych 35.4. Ustanowienie planu doskonalenia procesu testowego 35.4.1. Ustanowienie priorytetów dla wdrażania planu doskonalenia 35.4.2. Opracowanie podejścia do wdrożenia 35.4.3. Zaplanowanie działań związanych z wdrożeniem 35.5. Działanie w celu wdrożenia udoskonaleń 35.5.1. Stworzenie rozwiązania 35.5.2. Rozwiązania pilotażowe/testowe 35.5.3. Doprecyzowanie rozwiązania 35.5.4. Zaimplementowanie rozwiązania 35.6. Wyciąganie wniosków z projektu doskonalenia testów 35.6.1. Analiza i weryfikacja przeprowadzonych działań 35.6.2. Propozycje przyszłych rozwiązań

36. Organizacja, role i umiejętności 36.1. Organizacja 36.1.1. Zakres działań GPT 36.1.2. Organizacja Grupy procesu testowego 36.1.3. Właściwości Grupy procesu testowego 36.2. Role i umiejętności 36.2.1. Doskonalący proces testowy 36.2.2. Główny oceniający 36.2.3. Oceniający 36.2.4. Umiejętności doskonalącego proces testowy

37. Czynniki sukcesu CZĘŚĆ VII. JAKOŚĆ OPROGRAMOWANIA 38. Czym jest jakość oprogramowania? 38.1. Testowanie oprogramowania a jakość oprogramowania 38.2. Model Kano 38.3. Dojrzałość procesu i standardy jakości 38.3.1. SPR 38.3.2. Ocena Malcolma Baldridge’a 38.4. Co mierzyć, jak mierzyć i po co mierzyć?

39. Podstawy teorii pomiarów 39.1. Metryka, miara, wskaźnik, pomiar 39.2. Skale pomiarowe 39.2.1. Skala nominalna 39.2.2. Skala porządkowa 39.2.3. Skala interwałowa 39.2.4. Skala stosunkowa 39.2.5. Podsumowanie rodzajów skal pomiarowych 39.3. Typy metryk 39.3.1. Metryka bezpośrednia (podstawowa) 39.3.2. Suma/różnica 39.3.3. Stosunek 39.3.4. Proporcja 39.3.5. Odsetek 39.3.6. Miary iloczynowe 39.3.7. Tempo 39.4. Spójność i odpowiedniość pomiaru 39.5. Błędy pomiarowe 39.6. Podstawowe zasady analizy danych 39.6.1. Miary tendencji centralnej 39.6.2. Miary rozproszenia 39.6.3. Korelacja i regresja liniowa 39.6.4. Przyczynowość

40. Narzędzia kontroli jakości 40.1. Klasyfikacja narzędzi 40.2. Rodzaje narzędzi oraz obszary ich zastosowań 40.3. Statystyczna kontrola procesu 40.3.1. Wykres przebiegu 40.3.2. Karty kontrolne 40.4. Wykres czasu cyklu 40.5. Narzędzia analizy i zapobiegania przyczynom źródłowym 40.5.1. 5 pytań „dlaczego?” i diagram „why–why” 40.5.2. Macierz „jest–nie jest” 40.5.3. Kaizen 40.5.4. Poka yoke

41. Metryki wielkości oprogramowania 41.1. Metryki wolumenowe 41.1.1. LOC 41.1.2. Współczynnik produktywności języka 41.1.3. Pomiar specyfikacji i projektu 41.2. Metryki funkcjonalności 41.2.1. Punkty funkcyjne 41.2.2. Punkty obiektowe i rozszerzone punkty obiektowe 41.2.3. Punkty cech 41.2.4. Punkty przypadków użycia

42. Metryki charakterystyk jakościowych 42.1. Metryki dla funkcjonalności 42.2. Metryki dla niezawodności 42.3. Metryki dla użyteczności 42.4. Metryki dla wydajności 42.5. Metryki dla pielęgnowalności 42.6. Metryki dla przenaszalności

43. Metryki złożoności oprogramowania 43.1. Metryki Halsteada 43.2. Złożoność cyklomatyczna McCabe’a 43.2.1. Gęstość złożoności cyklomatycznej

43.2.2. ECC (Essential Cyclomatic Complexity) 43.3. Konstrukcje składniowe 43.4. Metryki struktur 43.5. Metryki złożoności systemu 43.5.1. Indeks utrzymywalności 43.5.2. Metryka złożoności systemu Agrestiego–Carda–Glassa 43.6. Metryki obiektowe 43.6.1. Metryki Lorenza 43.6.2. Metryki CK 43.7. Metryki złożoności dokumentacji 43.8. Metryki złożoności algorytmicznej

44. Metryki i modele wysiłku 44.1. Modele oparte na zgadywaniu i intuicji 44.2. Modele oparte na dekompozycji 44.3. Modele oparte na wiedzy eksperckiej 44.4. Modele oparte na benchmarkach 44.5. Modele oparte na porównaniu 44.5.1. Porównanie proste (naiwne) 44.5.2. Porównanie z uwzględnieniem różnic 44.6. Modele parametryczne 44.6.1. Tworzenie własnego modelu 44.6.2. Model COCOMO II 44.7. Łączenie modeli i uwagi na temat estymacji

45. Metryki i modele dla defektów 45.1. Natura defektów 45.2. Metryki defektów 45.3. Modele statyczne defektów 45.3.1. Model wprowadzania/usuwania defektów 45.3.2. Model fazowy 45.3.3. Model dwufazowy 45.3.4. Efektywność usuwania defektów i powstrzymanie fazowe 45.3.5. Modele zmian w kodzie 45.4. Modele dynamiczne defektów 45.4.1. Model Rayleigha

45.4.2. Model wykładniczy i S-kształtny 45.4.3. Model COQUALMO 45.5. Analiza mutacyjna 45.6. Metryki dynamicznej stylometrii

46. Metryki i modele przyrostu niezawodności 46.1. Wprowadzenie 46.2. Matematyczne podstawy teorii niezawodności 46.2.1. Funkcja niezawodności i funkcja awarii 46.2.2. Metryka MTTF (średniego czasu do awarii) 46.2.3. Rozkłady prawdopodobieństwa modelujące występowanie awarii 46.2.4. Rozkład wykładniczy i jego związek z metryką MTTF 46.2.5. Funkcja częstości awarii oraz ryzyko (hazard rate) 46.2.6. Prawdopodobieństwo awarii do czasu t 46.3. Modele przyrostu niezawodności 46.3.1. Model Jelinskiego–Morandy 46.3.2. Model niedoskonałego debugowania Goela–Okumoto 46.3.3. Niejednorodny model procesu Poissona Goela–Okumoto 46.3.4. Logarytmiczny model Poissona czasu wykonywania Musy– Okumoto 46.3.5. Model S-kształtny 46.3.6. Inne modele niezawodności

47. Metryki i modele dostępności 47.1. Dostępność 47.1.1. Dostępność ciągła a dostępność wysoka 47.1.2. Metody zwiększające dostępność systemu 47.1.3. Ilościowa miara dostępności i jej obliczanie 47.2. Odmładzanie oprogramowania 47.2.1. Powody degradacji i sposoby odmładzania oprogramowania 47.2.2. Wpływ odmładzania na dostępność systemu

48. Metryki dla procesu testowego 49. Metryki zadowolenia klienta 49.1. Proces pomiaru zadowolenia klienta 49.1.1. Wybór metody i sposobu przeprowadzenia badania

49.1.2. Opracowanie ankiety/kwestionariusza lub innego narzędzia badań 49.1.3. Wybór metody próbkowania 49.1.4. Wybór rozmiaru próby 49.1.5. Zebranie, opracowanie i analiza danych 49.2. Dział wsparcia klienta

50. Sposób prezentowania danych 50.1. Prezentowanie danych graficznych 50.2. Prezentowanie metryk

Dodatek A Dodatek B Dodatek C Dodatek D Bibliografia O autorze

Spis ilustracji

1.1. U-kształtna zależność gęstości defektów od rozmiaru modułu 1.2. Dwa przykłady skumulowanej liczby zgłoszonych i naprawionych defektów 3.1. Podstawowy proces testowy (wg ISTQB) 3.2. Przykład obustronnego śledzenia między artefaktami projektu 3.3. Przypadek testowy wysokiego poziomu 3.4. Przypadek testowy niskiego poziomu 3.5. Histogram odsetka wymagań pokrytych testami 3.6. Wielowarstwowy model procesu testowego wg ISO/IEC/IEEE 29119 3.7. Proces projektowania i implementacji testów wg ISO/IEC/IEEE 29119 4.1. Model kaskadowy (wodospadowy) 4.2. Model V 4.3. Model W 4.4. Model RUP 4.5. Model szybkiego prototypowania RAD 4.6. Model spiralny Boehma 4.7. Model Scrum 4.8. TDD 4.9. Metodologia Cleanroom 4.10. Hierarchia czynności weryfikacji i walidacji wg ISO/IEC/IEEE 29119 4.11. Projekt architektury przykładowego programu 5.1. Taksonomia modeli 5.2. Generacja kodu Graya o długości 3 z kodu o długości 2 5.3. Model 8-bitowego konwertera Gray2Order 5.4. Podstawowe elementy grafu przepływu sterowania 5.5. Transformacja konstrukcji algorytmicznych na grafy przepływu sterowania

5.6. Podział kodu na bloki podstawowe i odpowiadający mu graf przepływu sterowania 5.7. Problematyczne programy dla modelowania przepływu sterowania 5.8. Graf przepływu danych dla funkcji compute_prefix 5.9. Kod oraz odpowiadający mu CFG z nieosiągalną ścieżką 6.1. Porównanie miejsc powstawania i wykrywania defektu bez stosowania przeglądów (u góry) i z przeglądami (na dolej) 6.2. Podstawowy model inspekcji Fagana 6.3. Porównanie nakładu pracy w projekcie z inspekcjami i bez nich 6.4. Graf przepływu sterowania

dla

programu wykorzystującego

kolejkę

z ograniczoną pojemnością 6.5. Graf przepływu sterowania i jego złożoność cyklomatyczna 6.6. Przykład złego grafu przepływu sterowania oraz jego korekta 6.7. Wykonanie symboliczne kodu zamieniającego wartości zmiennych 6.8. Struktura strony www sklepu internetowego 6.9. Przykład niezrównoważonej i zrównoważonej struktury strony internetowej 6.10. Podstawowe typy wywołań między modułami 6.11. Graf wywołań dla programu grep.c 8.1. Podział dziedziny na klasy równoważności 8.2. Niepoprawny podział dziedziny 8.3. Obustronne śledzenie między artefaktami procesu testowego 8.4. Klasy równoważności i wartości brzegowe dla programu ZniżkaMPK 8.5. Metody dwóch oraz trzech wartości granicznych 8.6. Drzewo możliwych wartości wszystkich warunków 8.7. Ilustracja minimalizacji tablicy decyzyjnej 8.8. Opcje programu Word 8.9. Elementy składowe grafów przyczynowo-skutkowych 8.10. Wykorzystanie operatorów O i M dla reprezentacji przyczyn i skutków o więcej niż dwóch możliwych wartościach 8.11. Fragment grafu P-S odpowiadający jednej regule tablicy decyzyjnej 8.12. Graf P-S stworzony na podstawie tablicy decyzyjnej 8.13. Graficzna ilustracja reguł śledzenia wstecznego 8.14. Graf P-S do którego zastosowano śledzenie wsteczne 8.15. Ogólny model stanów oraz przejść między nimi. Dwie konwencje zapisu 8.16. Maszyna stanowa dla automatu biletowego

8.17. Graficzna reprezentacja różnych typów stanów 8.18. Maszyna stanowa z warunkami na niektórych przejściach 8.19. Przykładowa maszyna stanowa 8.20. Graf de Brujina dla ścieżek długości 2 8.21. Maszyna stanowa dla procesu opisującego pracę redakcji czasopisma 8.22. System komputerowego rozpoznawania obiektów 8.23. Typy wierzchołków w drzewie klasyfikacji 8.24. Drzewo klasyfikacji dla systemu SKRO wraz z macierzą testową 8.25. Notacja dla modelu cech 8.26. Model cech dla asortymentu produktu programowego smartfonu 8.27.

Drzewo

klasyfikacji

wraz

z

tabelą

kombinacji

dla

kryterium

minimalistycznego 8.28. Etapy ręcznego tworzenia przypadków dla kryterium pokrycia par 8.29. 2-(4, 4, 1)-tablica ortogonalna i jej przekształcenie na zbiór przypadków testowych 8.30. Hierarchia subsumpcji dla kombinacyjnych kryteriów pokrycia 8.31. Hiperpłaszczyzny i podprzestrzenie w przestrzeni dwu- i trójwymiarowej 8.32. Obszar z brzegiem „otwartym” 8.33. Przykłady punktów IN, OUT, ON i OFF dla testowanego obszaru 8.34. Zdolność punktów ON-OFF do wykrywania błędów dziedziny 8.35. Redukcja liczby punktów ON i OFF dla kombinacji ograniczeń 8.36. Graficzna reprezentacja warunków dla kategoryzacji urządzeń ciśnieniowych 8.37. Warunki testowe na połączeniach linii 8.38. Model przepływu zdarzeń dla oprogramowania bankomatu 8.39. Typowe wzorce obszarów wejścia powodujących awarie 8.40. Ilustracja technik adaptacyjnego testowania losowego 8.41. Macierz CRUD 9.1. Kod i CFG dla programu SortBąbelkowe 9.2. Krawędzie CFG jako elementy pokrycia w testowaniu gałęzi 9.3. Graf przepływu sterowania dla programu Bisekcja 9.4. CFG i jego drzewo zagnieżdżeń pętli 9.5. Symboliczna reprezentacja LSKiS 9.6. DD-ścieżki a bloki podstawowe 9.7. Przykładowy CFG 9.8. CFG oraz ścieżki pierwsze dla funkcji CzyPierwsza

9.9. CFG z wykładniczą liczbą ścieżek testowych względem decyzji 9.10. Przykładowy CFG i jego zbiór ścieżek bazowych 9.11. CFG dla programu TypTrójkąta oraz dwa zbiory ścieżek bazowych 9.12. Przykładowy graf przepływu danych 9.13. Objazdy i ścieżki poboczne 9.14. Proces testowania mutacyjnego 9.15. Hierarchia subsumpcji dla białoskrzynkowych kryteriów pokrycia 10.1. Proces wstrzykiwania usterek i ulepszania zestawu testów (za [131]) 10.2. Karta testu przed rozpoczęciem sesji testowania eksploracyjnego 10.3. Stworzenie etykiety w MS Word 10.4. Stworzenie podpisu tablicy w MS Word 10.5. Stworzenie w tekście odsyłacza do tablicy w MS Word 10.6. Karta testu po zakończeniu sesji testowania eksploracyjnego 10.7. Model usterki powodowanej interakcją programu i środowiska 11.1. Heurystyczny model strategii testowej 12.1. Geometryczna interpretacja miary APFD 13.1. Model jakości według ISO 9126 14.1. Model jakości McCalla 14.2. Model jakości użytkowej według ISO 25010 14.3. Model jakości produktu według ISO 25010 14.4. Model jakości danych według ISO 25012 16.1. Ekran programu ELROJ – przykład niepoprawnej funkcjonalności 16.2. Przykłady testowania obciążeniowego 16.3. Przykład złej skalowalności systemu 16.4.

Przykładowy

wynik

analizy

WAMMI

(źródło:

www.wammi.com/whatis.html) 16.5. Informowanie użytkownika o stanie systemu 16.6. Brak zgodności między systemem a rzeczywistością 16.7. Kontrola użytkownika nad systemem 16.8. Spójność interfejsów w obrębie rodziny produktów MS Office 16.9. Zapobieganie błędom: okno kalendarza zamiast pola na ręczne wpisanie daty 16.10. Wybór lokalizacji zamiast ręcznego wpisywania ścieżki 16.11. Zaawansowane opcje niewidoczne dla początkującego użytkownika 16.12. Prosty i estetyczny interfejs elektronicznego kalkulatora 16.13. Przykłady złych komunikatów o błędach

16.14.

Laboratorium

badania

użyteczności

(źródło:

www.designperspectives.com/usability.html) 16.15. Mapa ciepła (heat map) dla śledzenia wzroku 16.16. Relacje między metrykami MTTR, MTTF i MTBF 16.17. Średni czas między awariami 16.18. Zależności między modułami i odpowiadająca im macierz struktury projektu 17.1. Przykład zgodności danych z zewnętrznymi regulacjami 17.2. Przykład poufności danych (hasło do poczty) 17.3. Przykład identyfikowalności danych 18.1. Klasyczny trójkąt ograniczeń projektowych 18.2. Skomplikowana struktura zależności między rolami w projekcie 19.1. Prawdopodobieństwo ryzyka a jego wpływ 19.2. Zależność między wielkością zużycia zasobów a osiąganym poziomem jakości 19.3. Model zarządzania ryzykiem 19.4. Macierz „władza versus zainteresowanie” 19.5. Czynniki wpływające na prawdopodobieństwo i wpływ ryzyka 19.6. Przykłady macierzy ryzyka produktowego 19.7. Poziom ryzyka według jego klasyfikacji 19.8. Przykład symulatora dla łagodzenia ryzyka 19.9. Macierz identyfikowalności ryzyka-testy 19.10. Przykład wysokopoziomowego raportu o ryzykach 19.11. Systemy o wysokiej i niskiej wierności 19.12. Czynności w analizie ryzyka według metody SST 19.13. Dwie postaci macierzy ryzyka produktowego w metodzie PRisMa 19.14. Proces PRisMa 19.15. Macierz ryzyka PRisMa i reguła koła 19.16. Techniki projektowania testów a kwadranty macierzy PRisMa 19.17. Macierz ryzyka produktowego dla systemu ELROJ 19.18. Związek między zagrożeniem, ryzykiem a awarią 19.19. Macierz QFD (tzw. dom jakości) 19.20. Macierz QFD dla systemu ELROJ 19.21. Notacja drzew awarii 19.22. Przykładowe drzewo awarii

19.23. Drzewo awarii dla systemu ELROJ 19.24. Model TMap 19.25. TestGoal – dziesięć zasad testowania sterowanego wynikami 20.1. Podział wymagań 20.2. Łańcuch Markowa jako model profilu operacyjnego 20.3. Wartość testowania w podejściu good enough 21.1. Hierarchia dokumentacji testowej wg ISO/IEE 29119-3 21.2. Przykładowy dziennik wykonania testów 22.1. Struktura podziału prac dla liczby testów systemu ELROJ 22.2. Schemat APT 23.1. Wymiary postępu testowania 23.2.

Macierz

identyfikowalności

testy



ryzyka

oraz

historia

testów

przykładowego projektu 23.3. Raport z pokrycia ryzyk 23.4. Raport o defektach 23.5. Raport o defektach w podziale na ich umiejscowienie 23.6. Dwa raporty o testach i defektach dla modułu B 23.7. Raport o przypadkach testowych 23.8. Raport o testach uwzględniający wysiłek i podział na cechy jakościowe 23.9. Raport z pokrycia 24.1. Model kosztu jakości 24.2. Klasyczne i współczesne spojrzenie na ekonomiczny model kosztu jakości 25.1. Testowanie rozproszone, zakontraktowane i zewnętrzne 25.2. Klasyfikacja usług TaaS 27.1. Cykl życia incydentu 27.2. ODC: rozkład defektów dla atrybutu „wpływ” 27.3. Raport z narzędzia do zarządzania incydentami 27.4. Raporty na podstawie danych z dziennika incydentów 28.1. Przykładowe ogłoszenie o pracę dla testera 28.2. Hierarchia potrzeb – piramida Maslowa 28.3. Model Tuckmana dynamiki zespołu 28.4. Matryca RACI 28.5. Inwentarz umiejętności i gap-analysis 28.6. Wskaźnik osobowości Myers–Briggs 28.7. Model analizy transakcyjnej Wagnera

28.8. Style uczenia się według Honeya i Mumforda 28.9. Model zmiany według V. Satir 28.10. Cechy charakteru lidera 28.11. Komunikacja kierownika testów z innymi podmiotami 29.1. Generyczny proces podejmowania decyzji 29.2. Diagram podobieństwa – przykład 29.3. Graf priorytetyzacji – przykład 29.4. Mapa myśli – przykład 29.5. Wielokrotne głosowanie – przykład 29.6. Metoda delficka – przykład 29.7. Proces metody Wideband Delphi 29.8. Poker planistyczny w formie aplikacji na smartfona 29.9. AHP – struktura przykładowego problemu decyzyjnego 29.10. Analiza pola sił dla problemu zmiany w organizacji 29.11. Diagram rybiej ości (Ishikawy) – przykład 30.1. Automatyzacja vs testowanie ręczne – koszty 30.2. Koszty związane z wdrożeniem narzędzia 30.3. Ryzyka związane z wykorzystaniem narzędzi w testowaniu 30.4. Model generycznej architektury automatyzacji testów 30.5. Proces projektowania architektury automatyzacji testów 30.6. Różne sposoby reprezentacji elementów GUI: a) check-box; b) radio button; c) lista rozwijana 30.7. Formularz ze strony www obliczającej indeks BMI 30.8. Widok okna Selenium po nagraniu przypadku testowego 30.9. Selenium – odtworzenie nagranych akcji przypadku testowego 30.10. Oczekiwana informacja na ekranie systemu ELROJ 30.11. Skrypt oparty na słowach kluczowych z wykorzystaniem programu Excel 30.12. Języki i notacje stosowane w automatyzacji testów 30.13. Diagramy UML 2.5 30.14. Cykl życia narzędzia 31.1. Kontekst udoskonalania procesu testowego 31.2. Przykładowe obszary udoskonalania testowania 31.3. Cykl Deminga i jego zastosowanie w kontekście udoskonalania 31.4. Model IDEAL 31.5. Przegląd metod udoskonalania

31.6. Model People CMM 32.1. Model doskonałości EFQM (Excellence Model) 32.2. Six Sigma – rozkład normalny i zmienność procesu 32.3. CMMI – model z reprezentacją etapową 32.4. Obszary procesowe CMMI: weryfikacja i walidacja 32.5. Model TPI Next 32.6. TPI Next – macierz dojrzałości testów 32.7. TPI Next – macierz dojrzałości dla przykładowego procesu 32.8. TPI Next – bazowy układ klastrów 32.9. TPI Next – przykładowa sekwencja doskonalenia oparta na klastrach 32.10. TMMi – poziomy dojrzałości i odpowiadające im obszary procesów 32.11. TMMi – struktura modelu 32.12. STEP – elementy modelu 32.13. STEP – struktura aktywności w podziale na poziomy testów 32.14. CTP – model procesu 33.1. Analiza Pareto 33.2. Przestrzenna i czasowa identyfikacja grup defektów 33.3. Rozkład normalny 33.4. Wykres X–Y dla identyfikacji defektów do analizy przyczynowej 33.5. Techniki zbierania i organizacji informacji 33.6. Rozszerzenie diagramu Ishikawy w celu przeprowadzenia analizy 33.7. GQM – schemat modelu 33.8. Paradygmat GQM – koncepcja pomiaru 33.9. Paradygmat QIP – koncepcja udoskonalania 33.10. GQM – wzorzec definiowania celu 35.1. Korporacyjna tablica rozdzielcza – przykład 35.2. Zrównoważona karta wyników – przykład 36.1. Struktura Grupy procesu testowego dla dużej organizacji 36.2. Umiejętności doskonalącego proces testowy 38.1. Model Kano 39.1. Hierarchia abstrakcji pojęć w teorii pomiaru 39.2. Przykład zastosowania skali nominalnej 39.3. Graficzna reprezentacja spójności i odpowiedniości pomiaru 39.4. Rozkład dobrze opisywany przez modę 39.5. Różne stopnie korelacji

39.6. Model liniowy zależności defektów od punktów funkcyjnych 39.7. Podatność korelacji na wartości odstające 39.8. Kwartet Anscombe’a 40.1. Wykres przebiegu dla dostępności systemu 40.2. S-kształtny (skumulowany) wykres przebiegu 40.3. Karta kontrolna i przykłady anomalii 40.4. Karta kontrolna 40.5. Wykres czasu cyklu 40.6. Diagram why–why 40.7. Macierz jest–nie jest 40.8. Kaizen 41.1. Zależność gęstości defektów od rozmiaru programu 41.2. Procedura obliczania punktów funkcyjnych 41.3. Punkty funkcyjne – pięć typów funkcji 43.1. Ustrukturalizowane i nieustrukturalizowane elementy CFG 43.2. Redukcja CFG w celu obliczenia ECC 43.3. Przykładowy system 43.4. Przykładowa hierarchia klas 44.1. Model szacowania przez porównanie 44.2. Porównanie modeli parametrycznych dla wysiłku 44.3. Dane o PF i wysiłku oraz krzywa do nich dopasowana 45.1. Wykorzystanie modeli defektów 45.2. Dynamika i występowanie defektów – przykłady 45.3. Gęstość defektów w kolejnych wersjach oprogramowania 45.4. Model wprowadzania/usuwania defektów 45.5. Model wprowadzania i usuwania defektów w pojedynczej fazie 45.6. Macierz defektów i obliczanie metryk PCE, DRE i Dunna 45.7. Model Rayleigha dla różnych wartości współczynnika skali 45.8. Rozkład Weibulla dla różnych współczynników kształtu 45.9. Graficzna reprezentacja zaobserwowanych danych 45.10. Predykcja na podstawie modelu Rayleigha 45.11. Rozkład wykładniczy – gęstość i dystrybuanta 45.12. COQUALMO – podmodele wprowadzania i usuwania defektów 45.13. Model dynamicznej stylometrii – predykcja gęstości defektów 46.1. Indeks niezawodności

46.2. Dystrybuanta rozkładu wykładniczego 46.3. Model Jelinskiego–Morandy – chwilowa częstość awarii 47.1. Strategie odmładzania i reagowania a dostępność systemu 49.1. Metody pomiaru zadowolenia klienta 50.1. Marsz wojsk napoleońskich na Moskwę – przykład idealnego wykresu 50.2. Przykłady błędnie prezentowanych danych 50.3. Przykład niepożądanej i pożądanej redundancji 50.4. Wykres z dodanymi informacjami kontekstowymi 50.5. Przykład drążenia danych A.1. Przykładowy ekran obsługiwany przez program ELROJ A.2. Schemat logiczny systemu ELROJ C.1. Granica funkcji C.2. Geometryczna interpretacja pochodnej funkcji w punkcie C.3. Geometryczna interpretacja całki oznaczonej C.4. Ilustracja prawdopodobieńs twa warunkowego

Spis tabel

1.1. Najważniejsze odkrycia w historii testowania oprogramowania 1.2. Koszt usuwania defektu w podziale na fazy cyklu życia 3.1. Typowe poziomy testów i odpowiadająca im podstawa testów 3.2. Mapowanie wymagań na elementy projektowe systemu ELROJ 3.3. Możliwe relacje między rzeczywistym a uzyskanym wynikiem testu 5.1. Przypadki testowe dla konwertera Gray2Order 5.2. Modele działania oprogramowania 6.1. Efektywność

usuwania

defektów

dla

wybranych statycznych metod

testowania 6.2. Prosta analiza kosztów i zysków z przeprowadzenia przeglądu 6.3. Możliwe typy następstw operacji na zmiennych 6.4. Możliwe następstwa operacji dla zmiennej i w programie compute_prefix 8.1. Podsumowanie metody podziału na klasy równoważności 8.2. Zestaw warunków testowych dla programu TypTrójkąta na podstawie charakterystyki „typ trójkąta” 8.3. Zestaw warunków testowych dla programu TypTrójkąta na podstawie charakterystyk Ch2, Ch3 i Ch4 8.4. Przypadki testowe pokrywające klasy równoważności dla programu TypTrójkąta 8.5. Macierz identyfikowalności dla elementów pokrycia i przypadków testowych 8.6. Podsumowanie metody analizy wartości brzegowych 8.7. Porównanie efektywności

różnych podejść do

identyfikacji

wartości

granicznych 8.8. Przypadki testowe w analizie wartości brzegowych dla programu CarPanel 8.9. Ogólna postać tablicy decyzyjnej

8.10. Podsumowanie metody tablicy decyzyjnej 8.11. Tablica decyzyjna dla programu Egzaminator 8.12. Zminimalizowana tablica decyzyjna dla programu Egzaminator 8.13. Jeszcze bardziej zminimalizowana tablica decyzyjna programu Egzaminator 8.14. Tablica decyzyjna dla programu Bankier 8.15. Zminimalizowana tablica decyzyjna dla programu Bankier 8.16. Przypadki testowe dla programu Bankier 8.17. Przykładowa tablica decyzyjna z wymaganiami nieosiągalnymi 8.18. Przykładowa

tablica

decyzyjna

z wymaganiami

nieosiągalnymi

po

zminimalizowaniu 8.19. Podsumowanie metody grafów przyczynowo-skutkowych 8.20. Wynikowa tablica decyzyjna po zastosowaniu metody śledzenia wstecznego dla skutku S1 8.21. Tabelaryczna postać maszyny stanowej automatu biletowego 8.22. Pełna tabela przejść maszyny dla automatu biletowego 8.23. Krawędzie wychodzące z poszczególnych stanów 8.24. Obliczanie ścieżek o długości 2 8.25. Podsumowanie metody grafów przyczynowo-skutkowych 8.26. Przypadki testowe dla kryterium 0-przełączeń dla programu Czasopismo 8.27. Przypadki testowe dla kryterium 1-przełączeń dla programu Czasopismo 8.28. Podsumowanie metody kategoria-podział 8.29. Parametry i kategorie metody getDisplayString 8.30. Parametry, kategorie i wybory dla metody getDisplayString 8.31. Podsumowanie metody drzew decyzyjnych 8.32. Przypadki testowe dla programu SKRO 8.33. Przypadki testowe spełniające kryterium Each Choice 8.34. Elementy pokrycia dla kryterium Base Choice 8.35. Elementy pokrycia dla kryterium Multiple Base Choice 8.36. Podsumowanie metod kombinacyjnych 8.37. Podsumowanie metod analizy dziedziny 8.38. Kategoryzacja urządzeń ciśnieniowych w zależności od dopuszczalnego ciśnienia i pojemności 8.39. Przypadki testowe dla programu Kategoryzacja 8.40. Przypadek użycia dla szybkiej wypłaty z bankomatu

8.41.

Przypadek

testowy

dla

scenariusza

głównego

przypadku

użycia

SzybkaWypłata 8.42. Przypadek testowy dla scenariusza alternatywnego przypadku użycia SzybkaWypłata 8.43. Podsumowanie metody testowania opartego na przypadkach użycia 8.44. Podsumowanie metody testowania opartego na scenariuszach 8.45. Przypadek testowy dla scenariusza „udana wypłata” 8.46. Przypadek testowy dla scenariusza „nierozpoznana karta” 8.47. Przypadek testowy dla scenariusza „trzykrotny błędny PIN” 8.48. Przypadek testowy dla scenariusza „niewystarczająca ilość środków na koncie” 8.49. Podsumowanie metody testowania opartego na historyjkach użytkownika 8.50. Przypadki testowe dla historyjki użytkownika 8.51. Podsumowanie metody testowania losowego 8.52. Testowanie losowe – przypadki testowe dla modułu Przekształcenie 8.53. Podsumowanie metody testowania opartego na składni 8.54. Przypadki testowe dla techniki opartej na składni 8.55. Przypadki testowe wyprowadzone z macierzy CRUD 8.56. Różne realizacje możliwych operacji CRUD 8.57. Macierz CRUD dla systemu ELROJ 8.58. Przekształcona macierz CRUD dla systemu ELROJ 9.1. Podsumowanie metody testowania instrukcji 9.2. Przypadki testowe dla pokrycia instrukcji 9.3. Podsumowanie metody testowania gałęzi 9.4. Przypadki testowe spełniające kryterium pokrycia gałęzi – wariant I 9.5. Przypadki testowe spełniające kryterium pokrycia gałęzi – wariant II 9.6. Podsumowanie metody testowania decyzji 9.7. Przypadki testowe dla pokrycia decyzji 9.8. Przypadki testowe dla decyzji złożonej z 3 warunków 9.9. Podsumowanie metody testowania warunków 9.10. Przypadki testowe dla testowania warunków 9.11. Podsumowanie metody testowania warunków/decyzji 9.12. Przypadki testowe dla pokrycia warunków/decyzji 9.13. Podsumowanie metody testowania warunków wielokrotnych 9.14. Przypadki testowe nieuwzględniające zwarcia

9.15. Testy dla pokrycia wielokrotnych warunków z short-circuit 9.16. Przypadki testowe dla kryterium pokrycia wielokrotnych warunków 9.17. Wartościowanie warunków

dla

skorelowanego

pokrycia

warunków

znaczących 9.18. Skorelowane pokrycie warunków znaczących – po usunięciu duplikatów 9.19. Wartościowania warunków dla ścisłego pokrycia warunków znaczących 9.20. Ścisłe pokrycie warunków znaczących – przypadki po usunięciu duplikatów 9.21. Tablica prawdy dla predykatu D = p ∧ (q ∨ r) 9.22. Podsumowanie metody testowania warunków znaczących 9.23. Tablica prawdy dla operatora XOR 9.24. Tablica prawdy dla predykatu funkcji leapYear 9.25. Przypadki testowe spełniające kryterium MC/DC dla funkcji leapYear 9.26. Podsumowanie metody testowania pętli 9.27. Pokrycie ścieżek iterowanych w metodzie wzorców pętli 9.28. Przypadki testowe dla pokrycia pętli w programie SortBąbelkowe 9.29. Podsumowanie metody testowania LSKiS 9.30. Możliwe LSKiS dla programu Potęgowanie 9.31. Przypadki testowe pokrywające LSKiS 9.32. Podsumowanie metody testowania ścieżek pierwszych 9.33. Przypadki testowe dla pokrycia ścieżek pierwszych programu CzyPierwsza 9.34. Podsumowanie metody testowania ścieżek 9.35. Testy dla TypTrójkąta spełniające kryteria pokrycia ścieżek i ścieżek bazowych 9.36. Podsumowanie metody testowania przepływu danych 9.37. Miejsca i typy wystąpienia zmiennych w procedurze RównanieKwadratowe 9.38. Przypadki testowe dla pokrycia wszystkich definicji 9.39. Przypadki testowe dla pokrycia wszystkich użyć 9.40. Podsumowanie metody testowania mutacyjnego 10.1. Tasonomia defektów oparta na przyczynie źródłowej (root-cause) 10.2. Lista kontrolna dla sprawdzenia użyteczności strony www 12.1. Przypadki testowe i usterki przez nie wykrywane 16.1. Ochrona przed błędami użytkownika – przykłady dobrych i złych rozwiązań 17.1. Baza danych adresów urzędów wojewódzkich 19.1. Ilościowe szacowanie ryzyka 19.2. Przykłady ryzyk produktowych i projektowych

19.3. Lista ryzyk z określonym poziomem i kategorią 19.4. Priorytetyzacja ryzyk 19.5. Poziomy krytyczności wg normy DO-178C 19.6. Poziomy nienaruszalności bezpieczeństwa wg IEC 61508 19.7. Obliczanie poziomu ryzyka w metodzie SST 19.8. Lista ryzyk dla programu ELROJ 19.9. Wagi czynników w PRisMa 19.10. Szacowanie ryzyka przez kierownika projektu 19.11. Szacowanie ryzyka przez analityka biznesowego 19.12. Szacowanie ryzyka przez architekta systemu 19.13. Uśrednione oceny 19.14. Ważona ocena ryzyk produktowych 19.15. Metody łagodzenia ryzyka dla systemu ELROJ 19.16. Wpływ redukcji ryzyka 19.17. FMEA dla programu ELROJ 20.1. Postać wymagań i proponowane techniki ich testowania 20.2. Profil operacyjny dla wyboru funkcji bankomatu 22.1. Przykładowe szacowanie kosztów testów z korektą 22.2. Średnie przemysłowe parametrów dotyczących testowania 27.1. ODC: przykład dwuwymiarowej analizy 29.1. Macierz priorytetyzacji – przykład 30.1. Zestaw słów kluczowych dla programu ELROJ 30.2. Frameworki wspierające testowanie jednostkowe 30.3. Przykładowe problemy z używaniem narzędzi 32.1. Doskonalenie oparte na modelu – korzyści i ryzyka 32.2. Obszary procesowe TMMi oraz ich cele i praktyki specyficzne 32.3. Porównanie TPI Next i TMMi 32.4. STEP – struktura poziomu testów 32.5. CTP – przykładowe metryki 33.1. Przykłady kategoryzacji defektów dla ich wyboru w analizie przyczynowej 35.1. Porównanie podejść do wdrożenia doskonalenia procesu testowego 36.1. Typy zdolności emocjonalnych według Mayera i Saloveya 39.1. Skale pomiarowe – podsumowanie 39.2. Dane o punktach funkcyjnych i liczbie defektów dla modelu regresji 40.1. Narzędzia kontroli jakości

40.2. Parametry kart kontrolnych 40.3. Pomiary dla karty kontrolnej 41.1. Tablica produktywności dla wybranych języków programowania 41.2. Wagi dla poszczególnych złożoności typów funkcji 41.3. Obliczanie złożoności typu funkcji danych 41.4. Obliczanie złożoności typu transakcji dla plików wewnętrznych 41.5. Obliczanie złożoności typu transakcji dla plików/zapytań zewnętrznych 41.6. Punkty funkcyjne dla systemu ELROJ 43.1. Metryki Halsteada 43.2. Złożoność cyklomatyczna a ryzyko 43.3. Złożoność cyklomatyczna a prawdopodobieństwo złej poprawki 43.4. Metryki Lorenza 43.5. Indeks Flescha – interpretacja wyników testu 44.1. Pracochłonność projektu testowego według podziału prac i czynności 44.2. Pracochłonność projektu testowego według podziału systemu 44.3. Rekomendowane wartości współczynnika D 44.4. Przykładowe dane historyczne z poprzednich projektów 44.5. Czynniki modyfikujące dla metody szacowania przez porównanie 44.6. Szacowanie wysiłku i kosztu przez porównanie 44.7. Dane historyczne o wysiłku i punktach funkcyjnych 44.8. COCOMO II – wagi czynników skali w zależności od ich wpływu 45.1. Wykorzystanie modelu fazowego 45.2. Modele zmian w kodzie – porównanie 47.1. Przykładowe dostępności systemu i odpowiadające im czasy niedostępności 47.2. Związek między gęstością defektów i wartością MTTF 49.1. Zebrane od użytkowników dane z ankiet A.1. Wymagania funkcjonalne dla programu ELROJ C.1. Tablica

prawdy

dla

operatorów

i równoważności C.2. Tablica prawdy dla operatora negacji

alternatywy,

koniunkcji,

implikacji

Spis listingów

1.1. Procedura przetwarzania komunikatów w oprogramowaniu AT&T 1.2. Dwa sposoby pobrania ciągu znaków ze strumienia wejściowego 1.3. Przykładowy program z pętlą 2.1. Algorytm sortowania bąbelkowego 2.2. Przykładowy program ilustrujący stopień pokrycia 3.1. Implementacja wysokopoziomowego przypadku testowego 3.2. Implementacja niskopoziomowego przypadku testowego 4.1. Historyjka użytkownika w podejściu BDD 5.1. Kod w C obliczający tablicę prefiksów dla algorytmu Knutha–Morrisa–Pratta 5.2. Fragment kodu z dynamicznym wiązaniem 6.1. Przykład programu z martwym kodem 6.2. Fragment kodu z dwoma definicjami zmiennej x 6.3. Przykład źle napisanego programu w C 6.4. Program obliczający silnię 6.5. Program obliczający silnię z wstawionymi asercjami 6.6. Program zamieniający miejscami wartości dwóch zmiennych 6.7. Kod źródłowy programu grep.c 7.1. Pseudokod programu Collatz 7.2. Zinstrumentowany program Collatz 7.3. Kod z wiszącymi i dzikimi wskaźnikami 7.4. Przeprowadzenie analizy wydajności 7.5. Przykładowy płaski profil programu generowany przez gprof 7.6. Graf wywołań wygenerowany przez gprof 8.1. Program obliczający końcową ocenę z przedmiotu 8.2. Algorytm redukcji liczby testów dla grafu P-S

8.3.

Konfiguracja

środowiska

testowego

dla

wykonania

testu

metody

getDisplayString 8.4. Procedura InParameterOrder dla pokrycia par 9.1. Fragment programu z martwym kodem 9.2. Prosty program 9.3. Program Bisekcja 9.4. Funkcja sprawdzająca przestępność roku 9.5. Funkcja RównanieKwadratowe 9.6. Powtórzony kod programu SortBąbelkowe 9.7. Szybkie potęgowanie 9.8. Wyznaczanie ścieżek pierwszych 9.9. Funkcja CzyPierwsza 9.10. Zdolność metody ścieżek bazowych do wykrywania defektów 9.11. Kod programu TypTrójkąta 9.12. Procedura RównanieKwadratowe napisana w Adzie 9.13. Program i jego pięć przykładowych mutantów 16.1. Fragment kodu html obowiązkowego w responsive web design 17.1. Skrypt PL/SQL z poleceniem ROLLBACK 19.1. Symulacja Monte Carlo szacująca prawdopodobieństwo awarii głównej 30.1. Page Object dla strony www wyszukiwania Google 30.2. Test z wykorzystaniem Page Object Pattern 30.3. Skrypt w Pythonie dla nagranego przypadku testowego 30.4. Pseudokod zorganizowanego skryptu 30.5. Skrypt dla testowania opartego na danych 30.6. Przypadki testowe dla systemu ELROJ oparte na słowach kluczowych 30.7. Fragment skryptu napisanego w Perlu 30.8. Skrypt VBScript wyszukujący dane w arkuszu kalkulacyjnym 30.9. Kod w OCL definiujący ograniczenia dla metody calcArea 30.10. Plik XML z rozkładem jazdy dla systemu ELROJ 33.1. Test statystyczny t-Studenta dla czasu naprawy defektów 39.1. Skrypt w pakiecie R budujący model liniowy 43.1. Kod programu w C++ sortującego tablicę 45.1. Skrypt wykorzystujący model Rayleigha do predykcji defektów 46.1. Model Jelinskiego–Morandy – praktyczne wykorzystanie 49.1. Statystyki opisowe dla zebranych danych

Znaki handlowe

CMM ® oraz CMMI® są zarejstrowanymi znakami Carnegie Mellon University. IDEALSM , PSPSM i TSPSM są zarejestrowanymi znakami Instytutu Inżynierii Oprogramowania (SEI), Carnegie Mellon University. EFQM Excellence ModelTM jest zarejestrowanym znakiem handlowym European Foundation for Quality Management. ISTQB® jest zarejestrowanym znakiem International Software Qualifications Board. ITILTM jest zarejestrowanym znakiem Office of Government Commerce. TMM TM jest zarejestrowanym znakiem Illinois Institute of Technology. TMMi ® jest zarejestrowanym znakiem TMMi Foundation. TPI® oraz TPI Next® są zarejestrowanymi znakami Sogeti Nederland B.V.

Testing

Wstęp

Mniej więcej w latach dziewięćdziesiątych XX wieku inżynieria oprogramowania weszła w erę jakości. Jakość, odnosząca się zarówno do produktu, jak i do procesu wytwórczego, stała się celem numer jeden w każdym poważnym projekcie IT. Wszystkie szanujące się firmy przeprowadzają u siebie audyty, aby wykazać zgodność swoich działań z takimi normami jak ISO 9001 czy określić poziom dojrzałości procesów według modelu CMMI (ang. Capability Maturity Model Integration). Powstało wiele metodologii zarządzania jakością, głównie na użytek „klasycznego” przemysłu. Równocześnie, do branży informatycznej przenikały i znalazły poczesne miejsce takie idee jak statystyczna kontrola procesu czy metoda Six Sigma. Jednym ze sposobów zapewniania jakości tworzonych aplikacji jest testowanie oprogramowania. Testowanie jest tak stare jak wytwarzanie oprogramowania i można bez cienia przesady powiedzieć, że istniało od momentu powstania pierwszego programu napisanego na komputerze. Mimo tak szacownej historii testowanie, zwłaszcza w polskich firmach, ciągle pozostaje mało docenianą dziedziną inżynierii oprogramowania. Wciąż można spotkać szefów firm, którzy – chcąc pokazać przyjazne nastawienie ich organizacji wobec wszystkich kandydatów – wyrażają gotowość zatrudniania osób z niewielkim doświadczeniem, oferując im stanowiska testerów. Uważają bowiem, że jest to zawód, który mogą wykonywać osoby bez jakiejkolwiek znajomości testowania. Wynika to z braku świadomości tego, że testowanie jest nie tylko dyscypliną trudną, lecz także odgrywa kluczową rolę w zapewnianiu jakości tworzonego oprogramowania. Taka postawa właścicieli firm produkujących oprogramowanie jest zupełnie niezrozumiała. Gdyby zaproponować im zatrudnienie programisty-amatora,

który dopiero chce się nauczyć profesjonalnego pisania kodu, zaprotestowaliby gwałtownie i odpowiedzieliby zapewne, że nie mogą sobie pozwolić na takie ryzyko, bo to obniżyłoby jakość tworzonego produktu. Paradoksalnie jednak, nie dbając o doświadczenie zatrudnianych testerów, pozwalają sobie na ryzyko o wiele większe, gdyż konsekwencje późnego wykrycia defektów (na skutek nieumiejętnego testowania), po przekazaniu oprogramowania klientowi, są o wiele poważniejsze, niż gdy wykrycie to następuje we wczesnych fazach cyklu życia. Takie podejście kierownictwa często wynika z powierzchownej wiedzy o testowaniu oprogramowania. Wielu menedżerów wyższego szczebla kojarzy proces testowania z praktykantem, który „siedzi, klika i patrzy, czy coś się nie zepsuje”. Nie zdają sobie sprawy, jak rozległą, trudną i skomplikowaną dziedziną jest testowanie. Często nie rozumieją, że efektywne testowanie oprogramowania wymaga wielkiego nakładu pracy, alokacji sporych środków finansowych, sprawnego zarządzania oraz nierzadko umiejętności zastosowania wysoce nietrywialnych technik. Proces testowania jest tak samo trudny, a może nawet trudniejszy, jak proces tworzenia oprogramowania. O znaczeniu testowania może świadczyć powstanie w ostatnich latach różnych organizacji międzynarodowych, takich jak np. ISTQB, które przeprowadzają egzaminy na certyfikowanych testerów, organizują konferencje czy publikują materiały opisujące dobre praktyki w testowaniu. Także na naszym, polskim podwórku pojawia się coraz więcej ciekawych inicjatyw związanych z jakością oprogramowania, np. Mistrzostwa Polski w Testowaniu czy też seria konferencji „Test Well” zainicjowana w 2013 roku. Te ostatnie przykłady pozwalają patrzeć optymistycznie na rozwój społeczności testersko-jakościowej w Polsce, ale trzeba pamiętać, że jest jeszcze wiele do zrobienia. I po to, między innymi, powstała ta książka. Niniejszy podręcznik został napisany dla: początkujących testerów, aby już na samym początku swojej kariery zawodowej poznali dobre praktyki oraz najefektywniejsze techniki i metody testowania, a także aby mogli spojrzeć na tę dziedzinę z lotu ptaka, zobaczyć, z jakich obszarów się składa i jakie są zależności między nimi; zawodowych testerów, aby ugruntowali i poszerzyli swoją wiedzę o zaawansowane techniki testowania;

inżynierów jakości, aby efektywnie wykorzystywali istniejące metody, modele i procesy zapewniania jakości oprogramowania; kierowników, menedżerów i dyrektorów testów, aby sprawniej zarządzali procesem testowym w organizacji i efektywnie go ulepszali; osób doskonalących lub chcących doskonalić proces w organizacji, aby nabyli niezbędną do tego celu wiedzę;

testowy

osób przygotowujących się do egzaminów na certyfikaty ISTQB (poziom podstawowy, zaawansowany i ekspercki), aby dostarczyć im zwarte źródło materiałów pomocnych w nauce do egzaminu; dyrektorów działów IT, aby mogli lepiej zrozumieć rolę testowania w organizacji; pracowników naukowych, którzy prowadzą lub zamierzają prowadzić zajęcia dydaktyczne i/lub badania naukowe w obszarze testowania i jakości oprogramowania; obecnych lub przyszłych pasjonatów testowania, którzy planują związać swój rozwój zawodowy z tą dziedziną inżynierii jakości oprogramowania. Podręcznik ten abstrahuje od konkretnych języków programowania czy narzędzi testowych. Wyjątkiem jest rozdział 30, gdzie podałem przykładowy katalog konkretnych aplikacji wspomagających proces testowania. W książce skupiłem się wyłącznie na metodach i technikach testowania, zarządzania testowaniem oraz zapewniania jakości, dzięki czemu zawarta w niej wiedza jest uniwersalna i przydatna dla każdego testera w każdej organizacji, niezależnie od typu projektu, w jakim uczestniczy, oraz narzędzi, jakich używa. Słowo „narzędzia” w podtytule książki jest związane z abstrakcyjnymi metodami pomocnymi w pracy inżyniera jakości, a nie konkretnymi aplikacjami typu CASE (ang. Compute-Aided Software Engineering) czy CAST (ang. Computer-Aided Software Testing). projekt (ang. project) – zestaw skoordynowanych i kontrolowanych aktywności o określonym czasie rozpoczęcia i zakończenia; jest powoływany, aby osiągnąć cel zgodnie z określonymi wymaganiami, w tym ograniczeniami czasowymi, kosztowymi i zasobowymi [1]

CASE (ang. Computer-Aided Software Engineering) – inżynieria oprogramowania wspomagana komputerowo CAST (ang. Computer-Aided Software Testing) – testowanie oprogramowania wspomagane komputerowo; patrz także: automatyzacja testowania Książka składa się z siedmiu części. Pierwsza jest wprowadzeniem w dyscyplinę testowania oprogramowania. Zawiera niezbędne pojęcia, opisuje proces testowy i wyjaśnia, jak ten proces jest umiejscowiony w różnych cyklach życia oprogramowania. Definiuje także typowe poziomy i typy testów. Część druga to obszerny przegląd technik projektowania testów. Opisane zostały w nim wszystkie najważniejsze oraz najczęściej stosowane techniki: czarnoskrzynkowe, białoskrzynkowe, oparte na defektach i oparte na doświadczeniu. Omówione są również techniki statyczne, takie jak przeglądy czy analiza statyczna, oraz metody analizy dynamicznej. W części trzeciej zostały opisane zagadnienia związane z testowaniem niefunkcjonalnym, czyli testowaniem cech jakościowych. Materiał tej części w dużym stopniu jest oparty na normie ISO/IEC/IEEE 25010 [2], która zastąpiła kilka lat temu normę ISO/IEC 9126 [3]. Część

czwarta

jest

przeznaczona

głównie

dla

osób

zajmujących

się

testowaniem od strony zarządzania, takich jak: kierownicy testów, menedżerowie jakości, kierownicy zespołów testowych. Omówione są w niej techniki zarządzania z naciskiem na metody oparte na ryzyku. Zawiera także przegląd dokumentacji testowej zgodny z normą ISO/IEC 29119-3 [4], która zastąpiła dotychczas stosowaną normą IEEE 829 [5]. Część piąta jest poświęcona ludziom i narzędziom w procesie testowym. Przedstawione są w niej zagadnienia komunikacji w zespole, kompetencji oraz motywacji. Zawiera także szeroki przegląd typów narzędzi wraz z konkretnymi przykładami ich zastosowań. Część szósta dotyczy metod doskonalenia procesu testowego. Jest przeznaczona głównie dla menedżerów, osób odpowiedzialnych w firmie za usprawnianie procesu testowego oraz dla kandydatów przygotowujących się do egzaminu ISTQB Expert Level – Test Process Improvement. Część siódma jest poświęcona zagadnieniom jakości oprogramowania: metrykom i modelom stosowanym w inżynierii jakości.

W książce, oprócz tekstu podstawowego, występują dwa inne rodzaje informacji: definicje oraz listingi (kody źródłowe): definicja – definicja pojęcia (wraz z jego angielskim odpowiednikiem) jest opisana na szarym tle; większość definicji jest podana za Słownikiem wyrażeń związanych z testowaniem pod redakcją Lucjana Stappa, opublikowanym przez Stowarzyszenie Jakości Systemów Informatycznych [6]

Kod źródłowy lub pseudokod programu – napisany czcionką Courier New, między dwiema poziomymi liniami oznaczającymi początek i koniec kodu. Linie kodu mogą być numerowane w celu odwołania się do nich w tekście. Nazwy zmiennych, funkcji oraz programów w zwykłym tekście są oznaczone kursywą. Fragmenty kodu w tekście są podane, podobnie jak listingi, czcionką maszynową (Courier New). Wartości logiczne są opisywane albo słownie, jako prawda i fałsz, albo za pomocą liczb 1 i 0, pogrubioną czcionką. Książka zawiera cztery dodatki. W Dodatku A został opisany system ELROJ (ELektroniczny ROzkład Jazdy), który będzie przykładem przewodnim służącym do ilustracji wielu metod i technik testowania. Dodatek ten zawiera opis systemu, specyfikację wymagań oraz fragment projektu architektury. Zaleca się, aby Czytelnik przed lekturą właściwej części książki zapoznał się z opisem tego systemu. W Dodatku B zebrano wszystkie najważniejsze normy i standardy bezpośrednio lub pośrednio związane z testowaniem i jakością oprogramowania. Dodatek C zawiera formalne definicje pojęć matematycznych oraz definicje z zakresu teoretycznych podstaw informatyki, które są wykorzystywane w książce. W Dodatku D zostały opisane najbardziej znane i cenione w środowisku testerów programy certyfikacji na testerów lub inżynierów jakości.

Część I Podstawy testowania

Początek to najważniejsza część pracy Platon

W części pierwszej wprowadzono podstawowe, niezbędne pojęcia z zakresu testowania oprogramowania, które będą wykorzystywane w całej książce. W rozdziale 1 opisano rolę i doniosłość procesu testowania w organizacji. Konsekwencje jego braku lub niedoskonałości są zilustrowane słynnymi przykładami awarii oprogramowania. Wprowadzono formalną definicję testowania oraz rys historyczny tej dyscypliny. Omówiono siedem podstawowych zasad testowania, a także – nie mniej ważne – kwestie psychologiczne oraz ekonomiczne związane z czynnościami testowymi. W rozdziale 2 wprowadzono niezbędne definicje związane z testowaniem. Pojęcia te występują w dalszej części podręcznika, dlatego Czytelnik powinien się z nimi dobrze zaznajomić. Rozdział 3 zawiera opis dwóch przykładowych procesów testowych: Podstawowego Procesu Testowego proponowanego przez ISTQB oraz procesu zdefiniowanego w normie ISO/IEC/IEEE 29119 Software Testing Standard. Procesy te są dosyć podobne i cała treść książki odnosząca się do zagadnień testowania (części I– VI) jest wyłożona zgodnie z tymi procesami. W rozdziale 4 omówiono testowanie w cyklu życia oprogramowania. Przedstawiono najpopularniejsze modele cyklu życia z uwzględnieniem roli ich testowania w tych modelach. W drugiej części tego rozdziału opisano poziomy oraz typy testów, które zwyczajowo przyjęło się stosować w branży IT.

Więcej na: www.ebook4all.pl

1. Wprowadzenie do testowania

1.1. Dlaczego testowanie jest niezbędne Komputery i systemy informatyczne stały się nieodzownym elementem naszego codziennego życia. Oprogramowanie – czy to w postaci aplikacji, czy też jako systemy wbudowane – można znaleźć prawie w każdym urządzeniu, poczynając od rakiety kosmicznej, a skończywszy na pralce czy kuchence mikrofalowej. Wszyscy nieustannie korzystamy z takich urządzeń, dlatego każdy z nas znalazł się wielokrotnie w sytuacji, w której używany przez niego sprzęt lub program komputerowy nie działał tak, jak powinien. Takie sytuacje nie są dla użytkownika komfortowe. Tracimy przez nie czas (ponowne uruchomienie komputera) i pieniądze (zakup nowego sprzętu po awarii). O ile niektóre objawy źle działającego oprogramowania mogą powodować jedynie irytację czy złość wywołaną np. koniecznością ponownego uruchomienia aplikacji, o tyle błędy w systemach o znaczeniu krytycznym mogą doprowadzić nawet do utraty zdrowia czy życia. Przykłady takich błędów zostały opisane w podrozdziale 1.3. system (ang. system) – zbiór modułów zorganizowany w celu osiągnięcia zadanej funkcjonalności [7] system krytyczny ze względów bezpieczeństwa (ang. safety critical system) – system, którego awaria lub nieprawidłowe działanie może skutkować śmiercią lub poważnymi obrażeniami ludzi, a także utratą uszkodzeniami urządzeń lub zanieczyszczeniem środowiska

Główną przyczyną awarii oprogramowania są tkwiące w nim usterki, które z kolei wynikają z pomyłek programistów. Programiści są tylko ludźmi, a każdy człowiek popełnia błędy, z wielu różnych przyczyn. Skomplikowany i złożony kod, praca pod presją czasu, wykorzystanie zaawansowanej technologii, duża interakcja między tworzonymi systemami czy po prostu zwykłe zmęczenie – każdy z tych czynników może stać się przyczyną popełnienia błędu. Awarie oprogramowania mogą występować również na skutek czynników niezależnych od ludzi. Na przykład wielu z nas spotkało się z sytuacją, w której nasz telefon komórkowy przestał działać na skutek czynników atmosferycznych (niska temperatura). Inne tego typu powody to pole magnetyczne, zanieczyszczenie środowiska, czy uszkodzenia mechaniczne sprzętu. Praktycznie każdy nietrywialny program komputerowy zawiera usterki. Jak już wspomnieliśmy, usterki te mogą mieć bardzo poważne konsekwencje. Profesjonalne, dobrze zaplanowane testowanie systemów oraz dokumentacji pozwala w znacznym stopniu zmniejszyć ryzyko awarii systemu, jeśli znalezione usterki zostaną usunięte w procesie debugowania. Często zdarza się, że testowanie jest wręcz wymogiem zawartym w kontrakcie na tworzenie aplikacji lub wynikającym z przepisów prawnych. Może też być obowiązkiem narzuconym przez normę lub standard przemysłowy, który jest stosowany w procesie budowy oprogramowania. Na przykład norma DO-178C1 „Software Considerations in Airborne Systems and Equipment Certification” [8], stosowana m.in. przy produkcji oprogramowania do awioniki, wymaga wprost zastosowania odpowiednich technik testowania kodu oraz kryteriów pokrycia, w zależności od poziomu krytyczności tworzonego systemu. Testowanie wpływa również bezpośrednio na jakość tworzonego produktu. Dobrze zdefiniowane metryki i modele stosowane podczas procesu testowego pozwalają w sposób ilościowy wyrazić jakość oprogramowania, mierząc np. liczbę znalezionych usterek, odnieść ich liczbę do przewidywanej łącznej liczby usterek ukrytych w kodzie, czy zmierzyć poziom obecnego wciąż w systemie ryzyka (tzw. ryzyko rezydualne). Dzięki tak przeprowadzonym pomiarom testowanie może służyć jako czynnik budujący zaufanie do tworzonego kodu: jeśli zespół testowy nie znajduje więcej usterek, lub częstotliwość ich znajdowania jest bardzo niska, może oznaczać to, że system ma wysoką jakość i jest gotowy do przekazania klientowi. Należy jednak pamiętać, że budowanie zaufania do kodu możliwe jest tylko wtedy, kiedy proces testowania przebiega prawidłowo

i zgodnie z zasadami. Tworzone przypadki testowe muszą być zaplanowane, zaprojektowane i wykonane poprawnie. W skrajnym bowiem przypadku, gdy zespół testowy nie wykona ani jednego testu i liczba znalezionych usterek będzie oczywiście równa zeru, nie oznacza to, że jakość tak „przetestowanego” systemu jest wysoka!

1.2. Definicja testowania W powszechnym rozumieniu testowanie jest kojarzone głównie z wykonywaniem testów, czyli z uruchamianiem oprogramowania. Jest to jednak tylko jeden z wielu etapów składających się na cały proces testowania. Zarówno przed, jak i po wykonaniu testów występuje wiele innych czynności związanych z testowaniem, takich jak planowanie, nadzór, kontrola, wybór warunków testowych, projektowanie i wykonanie przypadków testowych, sprawdzanie wyników, ocena spełnienia kryteriów zakończenia, raportowanie czy czynności zamykające fazę testowania. Wszystkie te etapy składają się na Podstawowy proces testowy omówiony w rozdziale 3. Istnieje wiele różnych definicji testowania. Według Myersa ([9], testowanie to

„proces

wykonywania

programu

lub

systemu

z

[10]), intencją

znajdowania w nim błędów”. Definicja ta nie uwzględnia technik statycznych testowania oraz tego, że testowaniu powinny podlegać wszystkie artefakty wytwarzane w procesie produkcji, a więc nie tylko kod, ale też takie dokumenty, jak wymagania czy projekt architektury systemu. Skupiając się wyłącznie na znajdowaniu defektów, nie uwzględniamy kwestii szerzej pojętej jakości i zaufania do kodu. Co więcej, przyjęcie tej definicji oznacza, że proces testowania może rozpocząć się dopiero po tym, gdy kod zostanie napisany. Hetzel [11] przez testowanie rozumie każdą czynność nakierowaną na sprawdzanie atrybutów i możliwości programu lub systemu oraz weryfikację tego, czy testowany system spełnia założone wymagania. Definicja ta bierze pod uwagę aspekt jakościowy tworzonego systemu. Norma IEEE 610 [7], będąca słownikiem pojęć używanych w inżynierii oprogramowania, podaje dwie definicje testowania. Pierwsza mówi, że testowanie to proces (lub przeprowadzanie procesu) obsługi systemu lub komponentu, w określonych warunkach, obserwowania lub nagrywania rezultatów działania i ewaluacji jego cech. Druga definicja odwołuje się z kolei do

normy IEEE 829 [5], która określa testowanie jako proces analizy elementu oprogramowania

w celu wykrycia

różnic (usterek)

między

istniejącymi

a wymaganymi warunkami oraz oceny cech tego elementu. Miller [12] z kolei pisze, że głównym celem testowania jest potwierdzenie jakości testowanego systemu przez systematyczne wykonywanie go w kontrolowanych warunkach. Sylabus ISTQB [13] wskazuje następujące cele testowania: znajdowanie usterek, nabieranie zaufania do poziomu jakości, dostarczanie informacji potrzebnych do podejmowania decyzji, zapobieganie defektom. Jest to dosyć dobra charakterystyka celów testowania, chociaż o ile dwa pierwsze cele są jasne i zrozumiałe, o tyle dwa ostatnie wymagają komentarza. Cel trzeci nie jest sformułowany wystarczająco precyzyjnie – nie jest on charakterystyczny wyłącznie dla testowania. Należałoby tu dodać, że informacje, o których mowa w tym celu, dotyczą właściwości testowanego oprogramowania, takich jak: liczba znalezionych błędów, odsetek wymagań pokrytych testami czy aktualny poziom jakości. Cel czwarty – zapobieganie defektom – pochodzi z klasycznej książki Beizera [14]. Twierdzi on, że sama czynność projektowania testów jest najlepszą metodą zapobiegania defektom. Część ekspertów uznaje testowanie wyłącznie za czynność „destrukcyjną”, nastawioną na znajdowanie usterek, a nie na zapobieganie im. Zapobieganie defektom odbywa się na ogół nie tylko przez sam proces projektowania, lecz także przez analizę statyczną – np. przeglądy formalne wymagań – zanim jeszcze rozpocznie się pisanie kodu. Niektórzy autorzy nie zaliczają metod statycznych do repertuaru technik testowania, ale wydaje się, że stanowią oni mniejszość. testowanie, ewaluacja (ang. testing, evaluation) – proces złożony z wszystkich czynności cyklu życia, zarówno statycznych, jak i dynamicznych, skoncentrowany na planowaniu, przygotowaniu i ewaluacji oprogramowania oraz powiązanych produktów w celu określenia, czy spełniają one wyspecyfikowane wymagania, wykazania ich dopasowania do założonych celów oraz wykrywania usterek Każdy z powyższych celów można odnieść do różnych etapów i celów projektu tworzenia oprogramowania. Na przykład podczas początkowej fazy testów

zwykle zależy nam na tym, aby znaleźć możliwie największą liczbę usterek. W końcowych fazach testowania, np. podczas testów akceptacyjnych, głównym celem może być nabranie zaufania do oprogramowania, przez sprawdzenie, czy spełnia wymagania i czy działa tak, jak powinien. Testowanie produkcyjne zwykle ocenia jakościowe cechy oprogramowania, takie jak funkcjonalność, bezpieczeństwo, niezawodność czy dostępność. Czasami testowanie nie ma na celu usuwania usterek, a jedynie ocenę jakości oprogramowania i dostarczenie interesariuszom informacji o ryzyku związanym z użytkowaniem systemu. Takie działanie może występować np. w sytuacji, gdy musimy zintegrować nasz system z innym, zewnętrznym systemem dostępnym w kilku różnych wersjach. Testowanie zewnętrznego systemu pozwoli ocenić jakość każdej z wersji i wybrać tę obarczoną najmniejszym ryzykiem lub tę, która spełnia nasze wymagania w największym stopniu. Testowanie skupiające się na tym, aby wykazać, że oprogramowanie nie działa, jest nazywane testowaniem negatywnym. Jest ono bardziej związane z postawą testerów niż z konkretnym podejściem czy techniką testowania. Często wykorzystuje np. nieprawidłowe dane wejściowe lub wymusza występowanie wyjątków podczas działania aplikacji. testowanie negatywne, brudne testowanie (ang. negative testing, dirty testing) – testowanie, którego celem jest pokazanie, że oprogramowanie nie działa poprawnie [14] debugowanie (ang. debugging) – proces znajdowania, izolowania, analizowania i usuwania przyczyn awarii obsługa wyjątków (ang. exception handling) – zachowanie modułu lub systemu w odpowiedzi na błędne wejście wprowadzone przez użytkownika lub inny moduł bądź system zachowanie (ang. behavior) – odpowiedź modułu lub systemu na zestaw wartości wejściowych i warunków wstępnych Przy okazji testowania wypada również wspomnieć o debugowaniu, gdyż są to pojęcia bardzo często mylone lub utożsamiane ze sobą. Debugowanie jest zupełnie inną czynnością niż testowanie i nie ma z nim nic wspólnego [15]. Jest niejako

czynnością „odwrotną”, czy też komplementarną do testowania: o ile testowanie służy do wykazywania obecności awarii w testowanym programie, o tyle debugowanie jest procesem usuwania defektów będących źródłem tych awarii, czyli naprawy wadliwego oprogramowania. Debugowanie jest oddzielną dyscypliną inżynierii oprogramowania. Efektywne debugowanie jest zwykle bardzo trudne. Teoria debugowania ma bardzo wiele wyspecjalizowanych, skomplikowanych narzędzi służących do szybkiej analizy kodu oraz izolacji miejsca zawierającego usterkę (patrz np. [16]).

1.3. Słynne przykłady awarii oprogramowania W tym podrozdziale przedstawimy słynne przykłady awarii oprogramowania. Niektóre są śmieszne, inne kosztowne, jeszcze inne – niestety – tragiczne w skutkach. Każdy z nich stanowi bardzo pouczającą lekcję mówiącą o tym, jak ważne jest testowanie i jak poważne skutki niesie za sobą jego brak lub niedbałe wykonanie. Przy każdym przykładzie zaznaczymy, jakie techniki testowania mogły być zastosowane tak, aby uniknąć awarii. Techniki te zostaną omówione dokładnie w rozdziałach 6–10. „Król Lew” Disneya. W 1994 roku Disney wydał na święta Bożego Narodzenia grę „Król Lew” na podstawie filmu o tym samym tytule. W owym czasie był to wielki hit kinowy i Disney liczył również na duże zyski ze sprzedaży gry. Była ona wydana w porozumieniu z Compaq Computers jako pre-instalowana aplikacja na laptopach firmy. Dzień po Wigilii centrum wsparcia technicznego Disneya zostało zasypane lawiną telefonów od zdenerwowanych klientów. Gra nie chciała się uruchomić na komputerach większości klientów. Okazało się, że nie została przetestowana na najbardziej popularnych konfiguracjach sprzętowych, czyli nie została wystarczająco przetestowana pod kątem przenaszalności. Można było użyć tu takich technik jak drzewa klasyfikacji czy testowanie kombinacyjne, np. testowanie par. Awaria ta spowodowała znaczne szkody wizerunkowe popularnej wytwórni. Rakieta Ariane 5. 4 czerwca 1996 roku Europejska Agencja Kosmiczna wystrzeliła rakietę Ariane 5. Po 39 sekundach lotu oprogramowanie rakiety uruchomiło procedurę samozniszczenia ze względu na niespodziewaną zmianę trajektorii lotu. Przyczyną błędu była konwersja liczby 64-bitowej na liczbę 16bitową [17], co spowodowało tzw. błąd operandu. Konwertowana liczba była

związana z wartością prędkości horyzontalnej i w poprzedniej wersji rakiety, Ariane 4, działała bez zarzutu. Niestety, w przypadku Ariane 5, wartość tej zmiennej mogła uzyskiwać o wiele wyższe wartości na skutek innej niż w Ariane 4 trajektorii lotu. Większość instrukcji była chroniona przed błędami przekroczenia wartości, jednak w tym przypadku to nie nastąpiło. Powody tego faktu są nieznane. Koszt całej misji to 500 milionów dolarów. Błąd można było wykryć, stosując statyczne techniki testowania, takie jak przeglądy kodu oraz techniki dynamicznego testowania opartego na profilu operacyjnym (czyli oczekiwany sposób użycia oprogramowania przez użytkownika). Awaria sieci AT&T. 15 stycznia 1990 roku nastąpiła wielka awaria sieci telekomunikacyjnej w Stanach Zjednoczonych. Od godziny 2:25 w nocy centrum operacyjne AT&T w Bedminster zaczęło odbierać komunikaty ostrzegawcze z różnych części sieci. Awaria postępowała błyskawicznie, infekując lawinowo stacje przekaźnikowe. Problemem okazał się defekt w kodzie mającym usprawniać szybkość przetwarzania komunikatów. Program wyglądał mniej więcej tak jak na listingu 1.1.

1 while (bufor odbieranych połączeń niepusty and bufor pomocniczy niepusty) do 2 3

inicjalizuj wskaźnik na pierwszy komunikat w buforze pomocniczym lub buforze połączeń pobierz kopię bufora

4

switch (komunikat)

5 6 7 8 9 10 11 12 13 14 15

case (incoming_message): if (przekaźnik nie działa) then if (bufor zapisu pusty) then wyślij komunikat “in service” else break end if end if przetwórz przychodzący komunikat, ustaw wskaźniki na opcjonalne parametry break end switch

16

przetwórz opcjonalne parametry

Listing 1.1. Procedura przetwarzania komunikatów w oprogramowaniu AT&T Gdy docelowy przekaźnik odbierał drugą spośród nadchodzących po sobie w bardzo krótkim czasie wiadomości, mógł być jeszcze zajęty przetwarzaniem pierwszej (bufor zapisu niepusty w linii 7.). W takim przypadku program miał opuścić ciało instrukcji if (linie 7.–8.) i przejść do linii 12., gdzie komunikat był przetwarzany, a wskaźniki miały być aktualizowane w bazie. Niestety, instrukcja break w linii 10. powodowała opuszczenie ciała całej instrukcji switch-case i przechodziła do przetwarzania parametrów opcjonalnych (linia 16.), co powodowało nadpisywanie danych w bazie. Oprogramowanie korygujące błędy wykrywało fakt nadpisania informacji w bazie i wyłączało przekaźnik, aby mógł zostać zresetowany. Wszystkie przekaźniki działały na tym samym oprogramowaniu, dlatego resetowanie urządzeń następowało powodując globalną awarię całej sieci.

kaskadowo,

bufor (ang. buffer) – urządzenie lub pamięć używana do czasowego przechowywania danych koniecznego w wyniku różnic czasowych w przepływie danych lub pojawienia się różnej ilości danych, jaką mogą przyjąć urządzenia lub procesy zaangażowane w przesyłanie lub wykorzystywanie tych danych [7] Koszt tej awarii szacuje się na 60 milionów dolarów. Obecnie większość kompilatorów ma mechanizmy, które podczas kompilacji wykonują elementy analizy statycznej kodu i potrafią ostrzec przed „podejrzanym” użyciem instrukcji break. Problem mógłby również zostać rozwiązany za pomocą inspekcji formalnych czy przeglądów kodu. kompilator (ang. compiler) – narzędzie tłumaczące program napisany w języku wysokiego poziomu na jego odpowiednik w języku maszynowym [7] Błąd przepełnienia w procesie dla protokołu finger. W 1988 roku Robert Morris stworzył pierwszego w historii robaka internetowego (worm), który – jak podają różne źródła – zainfekował od dwóch do sześciu tysięcy komputerów. Robak wykorzystywał trzy techniki ataków, przy czym tą, która odniosła

największy sukces w procesie infekowania była metoda przepełnienia bufora. Unixowy proces finger wykorzystywał polecenie gets, które pobiera ciąg znaków ze strumienia wejściowego. Pobranie ciągu wejściowego może być dokonane na dwa sposoby:

char * fgets(char * restrict str, int size, FILE * restrict stream); char * gets(char *str); Listing 1.2. Dwa sposoby pobrania ciągu znaków ze strumienia wejściowego W drugim przypadku argument funkcji gets nie ma ograniczenia na długość łańcucha str. Zbyt długi łańcuch wejściowy może spowodować przepełnienie bufora, co powoduje nadpisanie stosu i możliwość przejęcia kontroli nad komputerem. Testowanie wartości brzegowych, uwzględniające ekstremalne wartości i długości używanych zmiennych, zwykle bywa bardzo skuteczne w wykrywaniu tego typu problemów. Dobre rezultaty dadzą też metody testowania oparte na doświadczeniu lub na defektach (tzw. atak usterkowy). przepełnienie bufora (ang. buffer overflow) – wyjątek dostępu do pamięci na skutek usiłowania umieszczenia przez proces danych poza granicami wyznaczonego bufora. W rezultacie zostają nadpisane sąsiednie obszary pamięci; patrz także: bufor Błąd w MS Excel 2007. 23 września 2007 roku na grupie dyskusyjnej poświęconej Excelowi użytkownik Molham Serry zgłosił błąd polegający na tym, że jeżeli w Excelu wykona się mnożenie 850 ⋅ 77,1, to zamiast oczekiwanego wyniku 65 535 Excel zwraca... 100 000. Okazało się, że problem dotyczył tylko sposobu prezentacji wartości – Excel obliczał działanie poprawnie, ale przy prezentowaniu wyniku na ekran wypisywał błędną wartość na skutek defektu w kodzie formatującym wypisywane wartości. Zwrócona wartość 65 535 jest największą liczbą całkowitą, jaką można zapisać w zmiennej 16-bitowej. Prawdopodobnie kod formatujący wyświetlany wynik miał jakiś związek z tym faktem. Do wykrywania tego typu usterek jest przydatna analiza wartości brzegowych dla danych wyjściowych. To pouczający przykład błędu, gdyż często zapomina się, że techniki projektowania

testów można stosować nie tylko przez analizę i wykorzystanie danych wejściowych, lecz także przez wymuszenie zwrócenia przez program konkretnych wartości. Generator liczb losowych w systemie Kerberos. Kerberos to protokół uwierzytelniania i autoryzacji. Używa algorytmów kryptograficznych, więc wykorzystuje również generator liczb losowych. Okazało się, że przez dziewięć lat, od 1988 do 1996 roku, protokół ten wykorzystywał generator losowy z błędnie ustawionym tzw. ziarnem (ang. seed), czyli początkową wartością, na której podstawie był później generowany ciąg liczb losowych. W rezultacie, w sposób trywialny można było złamać zabezpieczenia systemu. Tego typu błąd najczęściej można odkryć podczas inspekcji formalnych oraz innych technik analizy statycznej kodu. Sonda kosmiczna Mariner I. 22 lipca 1962 roku NASA wystrzeliła sondę kosmiczną Marine I, mającą za zadanie zebrać informacje na temat planety Wenus. 294 sekundy po starcie oficer bezpieczeństwa zadecydował o zdalnym zniszczeniu sondy ze względu na niestabilną trajektorię lotu, co groziło upadkiem rakiety na Ziemię i spowodowaniem dużych zniszczeń lub nawet śmierci ludzi (dodatkowego smaczku całej historii dodaje fakt, że sygnał o zniszczeniu można było wysłać najpóźniej do 5 minut po starcie sondy. Gdyby oficer zwlekał z podjęciem decyzji 6 sekund dłużej, kto wie, jakie sonda spowodowałaby zniszczenia). Tak naprawdę do końca nie wiadomo, co było bezpośrednią przyczyną awarii. Większość źródeł mówi o symbolu kreski (łącznika), który został błędnie przepisany do kodu źródłowego z kartki zawierającej równania matematyczne wykorzystywane w oprogramowaniu, przez co prasa okrzyknęła całą historię „najdroższym łącznikiem w historii”. W astronautyce symbol kreski nad zmienną oznacza operację wygładzania. Omyłka, polegająca na opuszczeniu tego symbolu, spowodowała, że w programie wartości jednej ze zmiennych nie były wygładzane, powodując duże skoki wartości, co skutkowało zaburzeniem toru lotu rakiety wynoszącej sondę. Najlepszą techniką do wykrywania tego typu błędów są wszelkiego rodzaju inspekcje i przeglądy kodu. Mars Climate Orbiter to sonda NASA, która miała służyć do badania pogody i klimatu Marsa. Wystrzelono ją w 1998 roku, ale podczas dokonywania manewru wejścia na orbitę sonda znalazła się na złej wysokości, po czym stracono z nią kontakt i efekt projektu o łącznej wartości 125 milionów dolarów spłonął w atmosferze Marsa. Przyczyną awarii było stosowanie różnych systemów

metrycznych przez różne zespoły pracujące nad oprogramowaniem sondy. Oprogramowanie stosowane przez kontrolę naziemną stosowało dla parametru siły jednostki anglosaskie (funty), podczas gdy oprogramowanie sondy używało jednostek SI (niutonów). Tego typu błędy można wykryć podczas testów integracyjnych, których najprawdopodobniej nie wykonano. Dobrą praktyką jest również ścisłe trzymanie się ustalonych standardów (np. rodzaju systemu metrycznego wykorzystywanego w projekcie). Po tym wypadku NASA wprowadziła regulacje dotyczące takiej standaryzacji. Therac-25 to maszyny do terapii nowotworów stosowane w latach 80. Między 1985 a 1987 rokiem urządzenia te były przyczyną serii tragicznych wypadków. W wyniku podania pacjentom dawek promieniowania wyższych o kilka rzędów wielkości niż dawki dopuszczalne śmierć poniosło 6 osób. Przyczyną nieprawidłowego funkcjonowania urządzeń był tzw. wyścig (ang. race condition). W przypadku Therac-25 polegał on na tym, że gdy operator maszyny obsługiwał ją w szybkim tempie, nie wszystkie parametry programu były właściwie inicjalizowane. Późniejsze dochodzenie wykazało także poważne usterki w dokumentacji, która była niekompletna i w szczególności nie zawierała opisu kodów błędów zgłaszanych przez maszynę. Cobalt 60. Historia bardzo podobna do sprawy Therac-25 (i równie tragiczna w skutkach) zdarzyła się na początku XX wieku w National Cancer Institute w Panamie. Centrum to używało maszyn Cobalt 60 do radioterapii. Na skutek błędów w oprogramowaniu co najmniej ośmioro pacjentów zmarło z powodu przyjęcia zbyt wysokiej dawki promieniowania. Oprogramowanie pozwalało użytkownikowi na rysowanie na ekranie do czterech „tarcz”, czyli obszarów ciała pacjenta, które były chronione przed promieniowaniem w trakcie zabiegu. Technicy potrzebowali jednak definiować pięć takich tarcz. Poradzili sobie z tym w ten sposób, że reprezentację pięciu tarcz definiowali w programie przez rysowanie jednej dużej tarczy z otworem w środku. Niestety, sposób interpretacji przez program tego, co jest tarczą, a co otworem, zależał od sposobu rysowania. Gdy otwór był rysowany zgodnie z ruchem wskazówek zegara, program interpretował to inaczej, niż gdy był rysowany w przeciwnym kierunku. To powodowało, że w niektórych przypadkach zamiast podawać dawkę promieniowania w miejsce otworu, nakierowane było ono na całe ciało pacjenta z wyjątkiem tego obszaru. Tego typu błędy są trudne do wykrycia. Odpowiednią strategią mogłoby tu być testowanie GUI przy zastosowaniu technik opartych na

doświadczeniu, takich jak testowanie eksploracyjne czy zgadywanie błędów. Skuteczne stosowanie tych technik wymaga dostępności doświadczonego testera.

1.4. Rys historyczny Testowanie jest tak stare, jak stary jest pierwszy napisany program komputerowy. Na przestrzeni ostatnich 50 lat dojrzałość procesu testowania bardzo wzrosła. Zostało to wymuszone głównie czynnikami technologicznymi: rozwojem języków czwartej generacji (4GL), wzrostem roli utrzymania i aktualizacji oprogramowania, wynalezieniem metod formalnych inżynierii oprogramowania, powstaniem nowych metodyk wytwarzania oprogramowania itd. Podczas tych kilkudziesięciu lat zmieniało się także podejście do testowania oraz rozumienie jego roli (widać to zresztą wyraźnie, gdy porówna się definicje testowania opisane w rozdziale 1.2). Dalej przedstawiamy główne fazy rozwoju testowania oraz najważniejsze odkrycia w historii tej dziedziny, zarówno teoretyczne, jak i techniczne. Treść tego podrozdziału jest oparta na pracy Luo [18], która odwołuje się do artykułu Gelperina i Hetzela [19] opisującego rozwój testowania na przestrzeni kilkudziesięciu ostatnich lat. Faza pierwsza (przed rokiem 1956). Era debugowania. Przed 1956 rokiem testowanie nie było odróżniane od debugowania – były to pojęcia tożsame. W 1950 roku Turing opublikował swój słynny artykuł [20], w którym zadał pytanie o to, jak stwierdzić, że maszyna jest obdarzona inteligencją. Jeśli wymaganiem byłoby zbudowanie takiego inteligentnego programu, to pytanie Turinga można by przeformułować na pytanie o zgodność programu z wymaganiami. Test zaproponowany przez Turinga, pozwalający odróżnić maszynę od myślącego człowieka, jest chyba pierwszym w historii informatyki przykładem formy testowania funkcjonalnego. W latach pięćdziesiątych XX w. pojęcia: sprawdzania, debugowania i testowania programu nie były jeszcze jasno rozróżniane. Faza druga (lata 1957–1978). Era dowodzenia poprawności. W 1957 roku Charles Baker w recenzji książki Dana McCrackena o programowaniu komputerów odróżnił debugowanie od testowania. Sprawdzanie programu (ang. program checkout) miało dwa cele: upewnić się, że program działa oraz upewnić się, że program poprawnie rozwiązuje postawiony przed nim problem. Baker uznał cel pierwszy za domenę debugowania, a cel drugi – za domenę testowania. Sformułowanie „upewnić się” odnosiło się do postrzegania testowania jako

procesu, który ma na celu wykazać (udowodnić), że program poprawnie realizuje swoje funkcje. Lata 70. XX w. to także ogromny rozwój technik, które dziś nazywamy testowaniem pokrycia ścieżek. Nurt ten wypływał z idei, aby oprogramowanie testować wyczerpująco (ang. exhaustively). Faza trzecia (lata 1979–1982). Era dowodzenia niepoprawności. Okres ten wiąże się z publikacją w 1979 roku klasycznej już książki Myersa [10], z której pochodzi definicja testowania jako „procesu wykonywania programu z intencją znajdowania błędów”. Po raz pierwszy zaczęto postrzegać „niszczycielski” charakter testowania. Podkreślano, że przypadek testowy ma większą wartość, gdy jego wykonanie skutkuje wykryciem błędu. Przed 1979 rokiem testerzy mogli tworzyć, zupełnie nieświadomie, przypadki testowe, które cechowały się niskim prawdopodobieństwem wykrycia błędu. Po wydaniu podręcznika Myersa sytuacja diametralnie się zmieniła: mając na uwadze wykrywanie błędów jako podstawowy cel testowania, testerzy skupili się na konstruowaniu zestawów testów (zwanych również suitami testowymi), które były zdolne do wykrywania usterek z dużym prawdopodobieństwem. Ten paradygmat testowania spowodował, że zauważono, jak ważne jest rozpoczynanie czynności testowych już od najwcześniejszych faz cyklu życia oprogramowania. Takie podejście do testowania najpełniej chyba uwidacznia się w modelu V, który omówimy w punkcie 4.1.2. Faza czwarta (lata 1983–1987). Era ewaluacji. W 1983 roku Institute for Computer Sciences and Technology of the National Bureau of Standards opublikował dokument zatytułowany „Guideline for Lifecycle Validation, Verification, and Testing of Computer Software”. Została w nim opisana metodologia oceny oprogramowania w trakcie całego cyklu życia. Metodologia ta obejmowała i integrowała ze sobą analizę, przeglądy i czynności testowe. Żywiono przekonanie, iż precyzyjnie dobrany zbiór technik walidacji, weryfikacji i testowania pozwoli na wyprodukowanie oraz bezproblemowe utrzymywanie wysokiej jakości systemów. Pojęcia walidacji i weryfikacji zostały później zaadaptowane przez społeczność inżynierów jakości i funkcjonują do dziś. Faza piąta (od 1988 roku). Era zapobiegania. W 1990 roku Beizer opublikował książkę „Software Testing Techniques” [14], uznawaną za jedną z najważniejszych w historii testowania oprogramowania. Zawiera ona wiele technik projektowania testów. Beizer napisał w niej, że „czynność tworzenia testów jest

jedną z najefektywniejszych metod zapobiegania usterkom”. To sformułowanie rozszerzyło definicję testowania, które od tego momentu było ukierunkowane nie tylko na wykrywanie, lecz także zapobieganie defektom. Beizer opisuje cztery etapy myślenia o testowaniu: 1) testować, by oprogramowanie działało, 2) testować, by zepsuć oprogramowanie, 3) testować, by zredukować ryzyko, 4) testowanie jako „dyscyplina umysłowa” (ang. mental discipline), tzn. zapewnianie testowalności oprogramowania w ciągu całego cyklu życia. O ile podejście w fazie czwartej skupiało się na analizie i technikach przeglądu innych niż testowanie, o tyle podejście w fazie piątej jako głównych narzędzi używa planowania, analizy i projektowania testów. Książkę Beizera można uznać za tę, która ufundowała paradygmat wczesnego testowania opartego na ryzyku. O tym, że jest to paradygmat ważny i skuteczny, niech świadczy fakt, że funkcjonuje on z powodzeniem do dziś jako jedna z podstawowych zasad testowania oprogramowania. W tabeli 1.1 przedstawiono teorie, metody i techniki, które miały największy wpływ na rozwój testowania (część z nich jest podana za [18]). Tabela 1.1. Najważniejsze odkrycia w historii testowania oprogramowania

Rok Odkrycie 1975

Wybór tes tów na pods tawie analizy krawędzi g rafu przepływu s terowania

1976

Wybór tes tów na pods tawie analizy ś cieżek w g rafie przepływu s terowania

1980 S trateg ia oparta na dziedzinie (ang . domain strategy) 1980 Tes towanie funkcjonalne 1985 S trateg ie oparte na przepływie danych 1989

Formalne metody dla integ racji tes tów opartych na s pecyfikacji i na s trukturze

1990 Tes towanie oparte na log ice

1994 Model s zacowania niezawodnoś ci oprog ramowania oparty na pokryciu 1995 Ortog onalna klas yfikacja defektów (IBM) 1997 Tes towanie integ racyjne oparte na opis ie architektury 1997 Probabilis tyczne tes towanie funkcjonalne 1997 Narzędzia automatyzacji tes towania 2000 Tes towanie integ racyjne oparte na diag ramach UML 2000 Metoda S ixS ig ma dla tworzenia oprog ramowania 2001

Zinteg rowana technika dla oprog ramowania oparteg o na komponentach

2004 Automatyczne narzędzia analizy s tatycznej 2005 Podejś cie Goal-Ques tion-Metric 2008

Automatyczne, zoptymalizowane projektowanie przypadków tes towych

2010 Wczes na analiza ryzyka

1.5. Ogólne zasady testowania W ciągu kilkudziesięciu ostatnich lat pojawiło się wiele zasad dotyczących testowania oprogramowania. Poniższa lista, zaczerpnięta z [13], podaje siedem uniwersalnych zasad, które obowiązują niezależnie od zakresu testowania, rodzaju testowanego systemu czy używanych technik. Zasada 1. Testowanie ujawnia usterki. „Testowanie pokazuje obecność, a nie brak usterek” – to słynne zdanie zostało wygłoszone przez Dijkstrę podczas dyskusji na konferencji „Software Engineering Techniques” w 1969 roku w Rzymie [21]. Test, którego wynik różni się od oczekiwanego, pokazuje istnienie jakiegoś

problemu w kodzie lub dokumentacji. Wystarczy jeden taki test, abyśmy mogli stwierdzić wystąpienie błędu. Jeśli jednak wszystkie testy dadzą poprawne, tzn. zgodne z oczekiwaniem, wyniki, czy jest to dowód poprawności testowanego programu? Niestety nie. W kodzie może istnieć defekt, którego żaden test nie wykrył. Stwierdzenie Dijkstry ma wręcz charakter twierdzenia matematycznego – można je formalnie udowodnić na gruncie teorii rozstrzygalności. Formalnie, problem stwierdzenia poprawności programu jest nierozstrzygalny, to znaczy nie istnieje algorytm stwierdzający, czy program podany mu na wejściu jest poprawny2. Oczywiście testowanie zmniejsza prawdopodobieństwo występowania w programie niewykrytych defektów, niemniej nieznalezienie żadnych usterek nie jest dowodem poprawności oprogramowania. Zasada 2. Testowanie gruntowne jest niewykonalne. Przetestowanie wszystkich możliwych kombinacji warunków początkowych i danych wejściowych do programu jest wykonalne tylko w trywialnych przypadkach. Poza trywialnymi przypadkami nie da się również przetestować wszystkich możliwych ścieżek przepływu sterowania. Jeśli w programie istnieje przynajmniej jedna pętla, to może się ona wykonywać dowolną liczbę razy, zatem istnieje nieskończenie wiele ścieżek wykonania. Nawet jeśli założymy, że dziedzina wejściowa jest ograniczona (w końcu komputer operuje na zbiorach dyskretnych, nie ciągłych i każda zmienna może przyjmować skończoną liczbę możliwych wartości), to i tak liczba kombinacji wejść czy ścieżek jest zbyt duża, aby dała się przetestować w sensownym czasie. zmienna (ang. variable) – element pamięci komputera, dostępny w programie przez swoją nazwę Rozważmy przykład pseudokodu z listingu 1.3.

1 function Pętla(int x) 2 for (int i=1; i0) zamiast if (x>=0). błąd (ang. error) – nieprawidłowy stan wewnętrzny programu (wg [7]) awaria (ang. failure) – widoczne na zewnątrz odchylenie modułu lub systemu od oczekiwanego zachowania lub rezultatu działania (wg [26]) Niektóre źródła (np. [13]) traktują błąd jako formę awarii. W takim przypadku awaria niekoniecznie musi dać o sobie znać w widoczny sposób (bo stan programu jest jego wewnętrzną konfiguracją). Wykrywanie błędnych stanów jest o wiele trudniejsze niż wykrywanie „zwykłych” awarii, gdyż te ostatnie zwykle można dostrzec gołym okiem. Błędy i awarie objawiają się podczas wykonywania testów, co pośrednio umożliwia odkrycie usterki w kodzie. Samym usuwaniem usterek zajmuje się debugowanie debugowanie (ang. debugging) – proces wyszukiwania, i usuwania przyczyn błędów i awarii oprogramowania

analizowania

Jedna usterka może spowodować jeden lub więcej błędów lub awarii. Jedna awaria może być spowodowana jedną lub więcej usterkami. Często odniesienie awarii do konkretnej usterki jest trudne. Te rozważania doprowadziły do stworzenia modelu usterka/awaria, który opisuje sekwencję zdarzeń, jakie muszą nastąpić, aby zaobserwować awarię. Model ten nosi nazwę modelu RIP, od pierwszych liter angielskich słów: reachability, infection i propagation. W języku polskim czasami model ten nazywa się modelem usterka–błąd–awaria. model RIP (ang. RIP model) – model opisujący trzy kroki prowadzące do obserwacji awarii. Kroki te to: osiągalność (ang. reachability) – miejsce w programie, które zawiera usterkę, musi zostać osiągnięte;

infekcja (ang. infection) – po wykonaniu niepoprawnej instrukcji stan programu musi przyjąć nieprawidłową wartość; propagacja (ang. propagation) – zainfekowany stan musi propagować się i wywołać nieprawidłowe wyjście lub inne zewnętrzne zachowanie się programu. Wystąpienie błędu nie zawsze musi oznaczać wystąpienie awarii. Rozważmy program mający za zadanie sortować tablicę T metodą bąbelkową. Jego pseudokod jest przedstawiony na listingu 2.1. Zakładamy, że pola tablic są indeksowane od zera. W linii 4. program zawiera usterkę. Pętla powinna przebiegać od i równego 0, nie 1. Rozważmy następujące dwa wykonania programu SortBąbelkowe w kontekście modelu RIP: SortBąbelkowe((1, 4, 3, 6, 2)) SortBąbelkowe((5, 2, 7, 1, 4))

1 function SortBąbelkowe(int T[]) 2 n=liczba_elementów_T 3 do 4 5 6 7

for (i=1; iT[i+1]) then zamień miejscami T[i] z T[i+1] end if

8 end for 9 n=n-1 10 while (n>1) 11 return T 12 end Listing 2.1. Algorytm sortowania bąbelkowego W pierwszym przypadku po dojściu sterowania do linii 4. nastąpi osiągnięcie defektu (reachability). W tym samym momencie stan programu, niezależnie od postaci wejścia, stanie się błędny (infekcja), gdyż licznik zmiennej i powinien

wskazywać na 0, a nie na 1. W rezultacie, tablica będzie sortowana od drugiego elementu. Jednak tak się szczęśliwie złożyło, że najmniejszym elementem tablicy jest jej pierwszy element, dlatego nie trzeba go sortować. Program zwróci poprawnie posortowaną tablicę (1, 2, 3, 4, 6). W drugim przypadku osiągnięcie defektu i infekcja są analogiczne. Różnica jest taka, że pierwszy element tablicy nie jest najmniejszy, dlatego nastąpi propagacja zainfekowanego stanu (pierwszy element nie będzie podlegał sortowaniu). W rezultacie program ulegnie awarii, gdyż zwróci nieprawidłowy wynik (5, 1, 2, 4, 7). Model RIP odgrywa bardzo ważną rolę w kryteriach pokrycia stosowanych, zwłaszcza w testowaniu mutacyjnym (opiszemy je dokładnie w podrozdz. 9.15) i automatycznej generacji danych testowych [27]. Omówimy teraz pozostałe podstawowe pojęcia, których będziemy używać w kolejnych rozdziałach. Pierwszym z nich jest pojęcie jakości. Jakość jest cechą trudną do zmierzenia ilościowego, jednak jej poziom można odnieść do wartości różnych metryk stosowanych w procesie testowym, takich jak stopień pokrycia przez testy ryzyka zidentyfikowanego w produkcie. Im więcej ryzyk jest pokrytych przez testy, tym większą możemy mieć pewność, co do poziomu jakości testowanego programu. Zagadnienia inżynierii jakości są omówione w rozdziałach 38–49. jakość (ang. quality) – stopień, w jakim moduł, system lub proces spełnia określone wymagania, potrzeby lub oczekiwania (wg [7]) Testy tworzy się na bazie tzw. podstawy testu, czyli wszelkiego rodzaju dokumentów opisujących wymagania dla projektu, np. specyfikacji: wymagań, projektu, architektury. Jeśli dokument może być zmieniony tylko w wyniku formalnego procesu dokonywania zmian, to podstawę testu nazywa się zamrożoną podstawą testu. specyfikacja (ang. specification) – dokument określający w kompletny, precyzyjny i możliwy do weryfikacji sposób wymagania, projekt, zachowanie lub inne właściwości modułu lub systemu, może również opisywać procedury sprawdzania, czy warunki te zostały spełnione Analiza podstawy testu pozwala zdefiniować warunki testowe oraz przedmioty pokrycia (zwane też elementami pokrycia), które następnie staramy

się pokryć przypadkami testowymi. Warunkiem testowym może być np. wykonanie wszystkich instrukcji w programie, wymuszenie prawdziwości oraz fałszywości wszystkich predykatów w instrukcjach warunkowych warunkowej, przetestowanie wszystkich elementów GUI. podstawa testu (ang. test basis) – dokument, z którego można wnioskować o wymaganiach dla modułu lub systemu (wg [28]) zamrożona podstawa testu (ang. frozen test basis) – dokument podstawy testu, który może być zmieniony jedynie przez formalny proces kontroli zmiany podstawa (ang. baseline) – specyfikacja lub oprogramowanie, które było poddane formalnemu przeglądowi lub uprzednio uzgodnione, będące odniesieniem dla dalszych prac programistycznych, które może ulec zmianie tylko przez formalną procedurę zmian (wg [7]) warunek testowy (ang. test condition), inaczej wymaganie testowe, sytuacja testowa (ang. test requirement, test situation) – element lub działanie systemu, którego istnienie bądź poprawność może być zweryfikowana za pomocą jednego lub wielu przypadków testowych (np. funkcja, transakcja, cecha, atrybut jakości lub element struktury) cecha, cecha oprogramowania (ang. feature, software feature) – atrybut modułu lub systemu wyspecyfikowany w dokumentacji wymagań lub wywnioskowany z niej, np. niezawodność, użyteczność, ograniczenia projektowe [29] projekt testu (ang. test design) – 1) patrz: specyfikacja projektu testu; 2) proces przekształcania ogólnych celów testowania w uszczegółowione warunki testowe i przypadki testowe Warunki testowe służą do definiowania elementów pokrycia lub same je wprost stanowią. Na przykład, jeśli warunkiem testowym jest wartość brzegowa (np. wartość leżąca na końcu analizowanego przedziału liczbowego), elementami pokrycia dla tego warunku mogą być: ona sama oraz najbliższa jej wartość leżąca po drugiej stronie granicy (np. dla instrukcji x ≥ 8, gdzie x jest zmienną całkowitą,

warunkiem testowym może być wartość brzegowa 8, a elementami pokrycia liczby 8 oraz 7). Jeśli warunkami testowymi są klasy równoważności, to stanowią one jednocześnie elementy pokrycia. Zwykle warunki testowe i elementy pokrycia są fragmentami modelu opisującego działanie programu (np. wierzchołki lub krawędzie grafu przepływu sterowania, stany maszyny stanowej, kolumny tablicy decyzyjnej). element pokrycia, element testowy, przedmiot pokrycia (ang. coverage item, test item) – obiekt lub właściwość wykorzystywane jako punkt wyjścia do zaprojektowania testów pokrycia, np. klasy równoważności lub pokrycie kodu element przetestowany (ang. exercised) – element oprogramowania określa się jako przetestowany (sprawdzony) przez przypadek testowy, gdy wartość wejściowa powoduje wykonanie tego elementu, np. instrukcji, decyzji lub innego elementu strukturalnego Dla każdego warunku testowego (lub elementu pokrycia) należy stworzyć przypadek testowy pokrywający go. Na przykład, jeśli elementami pokrycia są linie kodu, a przypadek testowy powoduje wykonanie instrukcji nr 1, 2, 5, 8 i 9, to mówimy, że ten test pokrył elementy pokrycia nr 1, 2, 5, 8 i 9. Po wykonaniu tego testu na pokrycie czekają wciąż inne elementy, np. linie 3, 4, 6, 7. przypadek testowy (ang. test case) – zbiór danych wejściowych, wstępnych warunków wykonania, oczekiwanych rezultatów i końcowych warunków wykonania opracowany w celu zweryfikowania zgodności działania programu z oczekiwanym rezultatem lub sprawdzenia warunku testowego (wg [7]) Odsetek pokrytych elementów pokrycia nazywa się stopniem pokrycia. Zawsze dążymy do osiągnięcia stuprocentowego stopnia pokrycia, to znaczy do dostarczenia takiego zestawu testów, że dla każdego elementu pokrycia istnieje przynajmniej jeden test, który go pokrywa. Zestaw taki określa się mianem podstawowego zestawu testów. podstawowy zestaw testów (ang. basis test set) – zestaw przypadków testowych (zwykle najmniejszy możliwy) zapewniający osiągnięcie 100% określonego kryterium pokrycia

analiza pokrycia (ang. coverage analysis) – pomiar pokrycia osiągnięty podczas wykonywania testów wg z góry określonych kryteriów, przeprowadzany w celu określenia czy są potrzebne dodatkowe testy; jeśli odpowiedź brzmi tak, to jest podejmowana decyzja, jakie dodatkowe przypadki testowe wykonać Po wykonaniu testu porównuje się wynik tego testu z wynikiem oczekiwanym. Jeśli rezultaty są zgodne, to mówimy, że test jest zaliczony. W przeciwnym przypadku test nazywamy niezaliczonym. Testy niezaliczone wymagają szczegółowego dalszego badania, gdyż mogą świadczyć o istnieniu błędu. oczekiwany rezultat, wynik oczekiwany, przewidywany wynik (ang. expected result, expected outcome, predicted outcome) – zachowanie modułu lub systemu w ustalonych warunkach określone na podstawie specyfikacji lub innego źródła wynik, wynik testu, rezultat, rezultat testu (ang. outcome, test outcome, result, test result) – konsekwencja, wynik wykonania testu. Zawiera wyjścia na ekran, zmiany danych, raporty oraz wysyłane komunikaty; patrz także: rzeczywisty rezultat rzeczywisty rezultat, rzeczywisty wynik (ang. actual result, actual outcome) – wytworzone/zaobserwowane zachowanie się modułu lub systemu podczas jego testowania zaliczenie, test zaliczony (ang. pass, test pass) – test uważa się za zaliczony, jeśli jego rezultat pasuje do rezultatu oczekiwanego niezaliczenie, niezdanie, test niezaliczony, test niezdany (ang. fail, test fail) – test uznaje się za niezaliczony, jeśli jego rezultat różni się od oczekiwanego kryteria zaliczenia/niezaliczenia (ang. pass/fail criteria) – reguły decyzyjne wykorzystywane do określenia, czy obiekt testowany (funkcja) lub cecha zaliczył(a) test [5] Po wykonaniu wszystkich testów możemy zmierzyć pokrycie, czyli stopień, w jakim zestaw testów pokrył elementy pokrycia. Zwykle jest to miara ilorazowa,

wyrażająca stosunek wykorzystanych elementów pokrycia do wszystkich elementów pokrycia. pokrycie, pokrycie testowe (ang. coverage, test coverage) – stopień, wyrażony w procentach, w jakim zakresie zestaw testowy wykorzystał przedmiot pokrycia Rozważmy prosty przykład kodu z listingu 2.2.

function ProstyProgram(int x, int y) 1 z:=x+y 2 if (z==0) then 3 return 0 4 5 6 7 8 9

else if (x>y) then z:=z+x else z:=z+y return z end

Listing 2.2. Przykładowy program ilustrujący stopień pokrycia Program ten stanowi przedmiot testów. Załóżmy, że warunkiem testowym jest pokrycie instrukcji. Zatem elementami pokrycia są wszystkie linie kodu. Weźmy następujący zestaw przypadków testowych: PT1: (x = 4, y = –4) oczekiwane wyjście: 0 PT2: (x = 0, y = 3) oczekiwane wyjście: 6 Wykonanie PT1 spowoduje przejście przez instrukcje 1, 2, 3 (w linii 1. zmienna z otrzyma wartość 4 – 4 = 0, będzie spełniony warunek instrukcji if w linii 2., zatem nastąpi wykonanie linii 3., co będzie skutkować zwróceniem wartości 0 i zakończeniem działania programu). Wykonanie PT2 spowoduje przejście przez instrukcje 1, 2, 4, 5, 7, 8, 9. W sumie oba przypadki wykorzystały elementy pokrycia

odpowiadające liniom 1, 2, 3, 4, 5, 7, 8, 9. Pokryły więc 8 spośród 9 elementów pokrycia, zatem stopień pokrycia instrukcji dla powyższego zestawu testów wynosi 8/9 ≈ 89%. Gdybyśmy chcieli osiągnąć pokrycie stuprocentowe, musielibyśmy dodać do zestawu testów taki, który wymusi przejście przez linię 6. Nastąpi to tylko wtedy, gdy warunek w instrukcji if w linii 3. nie będzie spełniony i jednocześnie warunek w linii 5. będzie spełniony, czyli gdy x + y ≠ 0 oraz x > y. Warunki te są spełnione np. dla wejścia (x = 5, y = 3). Należy pamiętać, że pokrycie niekoniecznie musi odnosić się wyłącznie do elementów strukturalnych oprogramowania, takich jak linie kodu, decyzje w programie itp. Pokrycie może dotyczyć innych artefaktów procesu wytwórczego, np. wymagań, funkcjonalności, przypadków użycia, ryzyk, cech niefunkcjonalnych oprogramowania. kryterium pokrycia (ang. coverage criterion) – reguła lub zestaw reguł wymuszających wykorzystanie elementów pokrycia przez zbiór przypadków testowych kryteria zaliczenia testu (ang. pass/fail criteria) – reguły decyzyjne wykorzystywane do określenia, czy obiekt testowany lub cecha zaliczył(a) test [5]

3. Proces testowy

Testowanie jest pełnoprawną, osobną dziedziną inżynierii oprogramowania i musi być właściwie zorganizowane, aby było efektywne. Powinno więc być ujęte w ramy dobrze zdefiniowanego procesu. Proces jest sekwencją działań, które na podstawie warunków początkowych (wejść) pozwalają osiągnąć założony cel (wyjścia). Warunki początkowe są nazywane kryteriami wejścia, a warunki wyjściowe – kryteriami wyjścia. Zbiór kryteriów wyjścia nazywa się efektem testu. Proces testowy w odniesieniu do pojedynczego obiektu testów jest nazywany cyklem testowym. kryterium wejścia (ang. entry criteria) – zbiór ogólnych i szczególnych warunków, których spełnienie jest wymagane do kontynuacji procesu dla określonego zadania [30] kryterium wyjścia, kryterium ukończenia, kryterium zakończenia testu (ang. exit criteria, completion criteria, test completion criteria) – zbiór ogólnych i szczególnych warunków, uzgodnionych z udziałowcami, których spełnienie jest wymagane do oficjalnego zakończenia procesu [30] efekt testu (ang. test target) – zbiór kryteriów wyjścia cykl testowy (ang. test cycle) – wykonanie procesu testowego w stosunku do pojedynczego, możliwego do zidentyfikowania wydania testowanego obiektu Kryteria wejścia stosuje się po to, aby ochronić projekt przed wykonaniem zadania, na którego przeprowadzenie nie jesteśmy jeszcze przygotowani. Przedwczesne rozpoczęcie zadania może spowodować zmarnowanie nakładów

pracy, bo np. trzeba je będzie powtórzyć lub poświęcić o wiele więcej czasu na jego ukończenie niż w sytuacji, gdy warunki wejścia są spełnione. Kryteria wyjścia stosuje się z kolei, aby uchronić projekt przez przedwczesnym zakończeniem określonych zadań, gdy nie wszystkie czynności związane z tymi zadaniami zostały wykonane. Kryteria wyjścia są stosowane jako argument przeciwko zakończeniu testów oraz w celu precyzyjnego zdefiniowania momentu ich zakończenia [30]. W każdej firmie proces testowy będzie wyglądał inaczej, jednak praktycznie we wszystkich projektach uwzględniających w jakiś sposób fazę testowania da się wyróżnić wspólny zestaw czynności, takich jak: planowanie, kontrola i nadzór, projektowanie testów, implementacja testów, wykonanie testów, przygotowanie i utrzymanie środowiska testowego, zamykanie czynności testowych. W tym rozdziale omówimy dwa modele procesu testowego. Pierwszy, proponowany przez ISTQB [31] to tzw. Podstawowy proces testowy. Drugi jest opisany przez normę ISO/IEC/IEEE 29119 [32]. Oba procesy mogą być stosowane dla dowolnego modelu cyklu życia oprogramowania. W podrozdziale 3.1, opisując szczegółowo fazy Podstawowego procesu testowego, wprowadzimy jednocześnie wiele definicji występujących także w normie ISO/IEC/IEEE 29119. Dlatego w podrozdziale 3.2 proces proponowany przez tę normę omówimy pobieżnie, skupiając się jedynie na ogólnym opisie oraz wyszczególnieniu różnic między oboma podejściami.

3.1. Podstawowy proces testowy Na rysunku 3.1 przedstawiono czynności Podstawowego procesu testowego. Proces ten, oparty na metodologii TMap [33] składa się z następujących faz: planowanie testów; kontrola i nadzór; analiza testów; projektowanie testów; implementacja testów;

Rysunek 3.1. Podstawowy proces testowy (wg ISTQB) wykonanie testów; ewaluacja kryteriów wyjścia i raportowanie; zamykanie czynności testowych. Sposób prezentacji

poszczególnych faz na

rysunku ma

sugerować, iż

poszczególne etapy procesu nie muszą następować po sobie w sposób sekwencyjny. Na przykład w testowaniu eksploracyjnym projektowanie i implementacja testów następują równolegle z ich wykonaniem. Po zakończeniu fazy analizy testów może się okazać, że zmieniły się istotne warunki projektu (zmiana wymagań, odkrycie nowego, poważnego ryzyka itp.) i należy ponownie przeprowadzić fazę planowania, analizy itd. Proces można zaadaptować dla konkretnego poziomu testów (np. dla poziomu testów systemowych) lub dla całego procesu testowego. W kolejnych podrozdziałach opiszemy, jakie czynności przynależą do poszczególnych faz Podstawowego procesu testowego. proces (ang. process) – powiązane ze sobą działania przetwarzające wejścia w wyjścia (wg [34])

proces testowy (ang. test process) – model opisujący zestaw czynności wykonywanych przez osoby zaangażowane w testowanie oprogramowania; Podstawowy proces testowy składa się z następujących faz: planowanie testów, nadzór i kontrola, analiza, projektowanie, implementacja, wykonanie, ocena kryteriów wyjścia, zamykanie testów

3.1.1. Planowanie Planowanie testów jest czynnością wykonywaną głównie przez kierownika testów przy udziale analityka testów. Zaczyna się na początku procesu testowego dla każdego poziomu testów. Polega na zidentyfikowaniu wszystkich czynności testowych oraz zasobów potrzebnych do osiągnięcia celu opisanego w strategii testów1. Jest to również faza, w której się określa, jakie metryki będą wykorzystywane w monitorowaniu i kontroli procesu testowego oraz definiuje się sposoby ich zbierania. Na tej podstawie planuje się użycie określonych narzędzi do zbierania metryk i raportowania. Należy również zaplanować, jakie szkolenia będą przeprowadzone i kto ma w nich wziąć udział. Jeśli np. testerzy nie mieli dotychczas doświadczenia z metodyką Scrum, która ma być wykorzystywana w projekcie, to powinni zostać przeszkoleni w zakresie zwinnych metodyk wytwarzania oprogramowania. planowanie testów (ang. test planning) – tworzenie planów testów lub ich modyfikacja Dokładne czynności planowania wynikają zawsze z przyjętej strategii testowania2. Jeśli na przykład jest stosowane podejście oparte na ryzyku, to planowanie musi brać pod uwagę analizę ryzyka projektowego (np. jeśli wiadomo, że wiele błędów wynika ze źle rozumianych wymagań, należy zaplanować więcej czasu na przeglądy formalne wymagań i dokumentów projektowych). Należy również określić kryteria priorytetyzacji, które będą wykorzystywane podczas faz projektowania i wykonywania testów. Jeśli stosuje się podejście oparte na standardzie, to trzeba zadbać, aby wszystkie planowane czynności wynikały ze standardu lub były z nim zgodne. Jeśli wykorzystywane będzie podejście reaktywne, to należy precyzyjnie określić, jakie narzędzia będą używane do testowania dynamicznego.

testowanie dynamiczne (ang. dynamic testing) – testowanie, podczas którego jest wykonywany kod modułu lub systemu testowanie reaktywne (ang. reactive testing) – testowanie, które dynamicznie reaguje na testowany system i otrzymane wyniki testowania; w typowym podejściu testowanie reaktywne ma zredukowany cykl planowania, a fazy projektowania oraz implementacji testów nie wykonuje się, dopóki obiekt testów nie zostanie dostarczony Podczas planowania ustala się, jakie poziomy testów będą wykorzystywane (patrz podrozdz. 4.2), jakie są cele dla każdego z tych poziomów oraz jakie techniki projektowania testów będą używane na poszczególnych poziomach. Definiuje się także typy testów, które będą używane w procesie testowym (patrz podrozdz. 4.4). Menedżer testów, w porozumieniu z analitykiem testów, określa specyfikację środowiska testowego oraz upewnia się, że osoby odpowiedzialne za jego budowę i utrzymywanie są kompetentne i będą dostępne przez cały czas wykorzystywania tego środowiska. Identyfikuje również wszystkie ograniczenia, wymagania i obowiązki wynikające z różnego rodzaju umów, takich jak umowa o standard usługi (ang. Service Level Agreement, SLA) czy też z zewnętrznych zależności (np. zależności od innych projektów, usługi zlecane na zewnątrz, dostawcy, licencje). środowisko testowe, osprzęt testowy, podłoże testowe3 (ang. test environment, test rig, test bed) – środowisko, w którego skład wchodzi sprzęt, wyposażenie, symulatory, oprogramowanie oraz inne elementy wspierające, potrzebne do przeprowadzenia testów [7] Bardzo ważne jest, aby zweryfikować oszacowania czasowe i budżetowe nałożone na proces testowy, a także zaplanować testowanie wszystkich artefaktów procesu wytwórczego, nie tylko kodu. Są to na przykład: konfiguracje systemów i środowisk, dokumentacja, procedury instalacyjne. W fazie planowania należy również określić, w jaki sposób proces testowy będzie dopasowany do przyjętego modelu cyklu życia oprogramowania. konfiguracja (ang. configuration) – układ modułów lub system zdefiniowany za pomocą liczby, natury oraz wewnętrznych powiązań jego części składowych

3.1.2. Monitorowanie i nadzór Monitorowanie i nadzór to dwie różne czynności, choć ich nazwy brzmią podobnie i sugerują podobne aktywności. Wrażenie synonimiczności tych pojęć wynikać może z faktu, że są one przeprowadzane jednocześnie, w trakcie całego czasu trwania projektu. Monitorowanie testów jest czynnością zarządczą, polegającą na sprawdzaniu statusu i postępu projektu oraz porównywaniu uzyskanych wyników z zaplanowanymi. Weryfikacji tej dokonuje się za pomocą pomiarów metryk zdefiniowanych w fazie planowania. Przykładami metryk mogą być: procent zakończonych zadań spośród wszystkich zaplanowanych, osiągnięty stopień pokrycia, liczba stworzonych, wykonanych i zaliczonych przypadków testowych itd. W dalszej części książki omówimy wiele takich miar. Faza monitorowania obejmuje również tworzenie raportów z pomiarów. Jeśli okaże się, że jakiś parametr projektu odstaje od normy, to należy przedsięwziąć czynności zaradcze, aby sprowadzić projekt na właściwy tor. Opracowanie oraz wdrażanie takich działań to nadzór. Można więc stwierdzić, że monitorowanie jest czynnością „pasywną”, polegającą na obserwacji, natomiast nadzór jest czynnością „aktywną”, związaną z podejmowaniem działań. Czynności monitorowania i nadzoru są częścią większego procesu zarządczego, mianowicie zarządzania testami. Raportowanie to zwykle zadanie menedżera testów, natomiast samym zbieraniem metryk zajmuje się analityk testów. monitorowanie testów (ang. test monitoring) – zadanie z zakresu zarządzania testowaniem, zajmujące się okresowym sprawdzaniem statusu projektu testowego oraz raportowaniem stanu aktualnego w porównaniu ze stanem planowanym nadzór nad testami (ang. test control) – zadanie z zakresu zarządzania testowaniem, polegające na opracowaniu i wdrażaniu działań korygujących projekt testowy, gdy monitorowanie pokazuje odchylenie od planu zarządzanie testami (ang. test management) – planowanie, szacowanie, monitorowanie oraz kontrola przebiegu testów, na ogół przeprowadzane przez menedżera testów

Przykładowym rezultatem kontroli może być raport w formie graficznej porównujący skumulowaną liczbę znalezionych i naprawionych defektów. Przykład takiego wykresu był wcześniej pokazany na rysunku 1.2b. Nadzór mógłby tutaj polegać na przeprowadzeniu analizy źródłowej problemu i podjęciu decyzji o tym, by wydłużyć daną fazę testów, przeznaczyć więcej programistów do naprawy błędów, przeprowadzić inspekcje kodu pod kątem stosowania wzorców projektowych ułatwiających debugowanie itp.

3.1.3. Analiza testów Analiza testów to faza, w której określa się zakres testów, a więc „co” ma być przetestowane. Najpierw należy wybrać przedmiot testów oraz elementy testowe. Następnie dokonuje się przeglądu podstawy testów, celów testowania bądź analizy ryzyka oraz identyfikuje na ich podstawie warunki testowe i elementy pokrycia. Warunki te mogą służyć jako miary osiągniętego sukcesu i stanowić część warunków wyjściowych. przedmiot testów (ang. test object) – moduł lub system podlegający testowaniu Podstawa testów będzie się różnić w zależności od poziomu testów. W tabeli 3.1 (opracowanej na podstawie [35]) przedstawiono najczęściej spotykane poziomy testów oraz typową podstawę testów dla tych poziomów. analiza testów (ang. test analysis) – faza procesu testowania polegająca na identyfikacji obiektów podlegających testowaniu oraz na wyprowadzeniu warunków testowych z podstawy testów Tabela 3.1. Typowe poziomy testów i odpowiadająca im podstawa testów

Poziom testów

T ypowa podstawa testów

tes towanie wymag ania, projekt komponentów s zczeg ółowy, kod

projekt architektury

T ypowe elementy do pokrycia ins trukcje, decyzje, warunki, ś cieżki interfejs y wewnętrzne, niezmienniki, parametry,

protokoły komunikacji

tes towanie integ racyjne tes towanie s ys temowe

s pecyfikacja wymag ań oprog ramowania

tes ty integ racji projekt produktu 4 s ys temów

wymag ania funkcjonalne i niefunkcjonalne interfejs y zewnętrzne (API), niezmienniki, parametry

tes towanie akceptacyjne

s pecyfikacja wymag ań użytkownika, podręczniki użytkownika

przypadki użycia, s cenarius ze

analiza s tatyczna

dokumenty, których dotyczy analiza

s trony, wymag ania, przypadki tes towe, kod

Warunki testowe mogą być definiowane na różnych poziomach ogólności. Generalnie im wyższy poziom szczegółowości przyjmiemy, tym więcej wyprowadzimy warunków testowych. Na przykład, warunek testowy wysokiego poziomu dla systemu ELROJ (patrz Dodatek A) może mieć postać „przetestuj poprawność wyświetlania informacji na ekranie”, a warunkiem szczegółowym może być np. „przetestuj, czy metoda setActualDate działa poprawnie wtedy, gdy zmienna określająca rok jest liczbą dwucyfrową”. Bardzo ważne jest, aby warunki testowe były precyzyjnie powiązane z podstawą testów. Właściwość ta określana jest mianem śledzenia i w ogólności dotyczy nie tylko warunków testowych, lecz także wszystkich elementów dokumentacji i oprogramowania. Dzięki możliwości śledzenia jesteśmy w stanie kontrolować stopień pokrycia wymagań, ryzyk, elementów projektowych itd. Ponadto, gdy w projekcie pojawia się jakaś zmiana (np. modyfikacja wymagania), możemy przeprowadzić tzw. analizę wpływu, to znaczy, określić, na które testy ta zmiana będzie miała wpływ, czy wymagane jest przeprojektowanie i ponowne uruchomienie tych testów oraz jak dużo czasu to zajmie.

analiza wpływu (ang. impact analysis) – oszacowanie wpływu zmiany w dokumentacji lub systemie na przypadków testowych oraz ryzyka

pozostałe obszary

systemu,

postać

Rysunek 3.2. Przykład obustronnego śledzenia między artefaktami projektu Na rysunku 3.2 przedstawiono przykładowy schemat opisujący relacje między poszczególnymi elementami projektowymi, które umożliwiają śledzenie. Każdy przypadek testowy możemy odnieść do konkretnego elementu projektowego (linia kodu, funkcja, moduł, system itp.), który jest pokrywany tym testem. Z każdym elementem projektowym może być związane jakieś ryzyko, każde ryzyko można odnieść do konkretnego wymagania itd. Relacje między elementami mogą być jeden do jednego, jeden do wielu lub wiele do wielu. Strzałki na schemacie reprezentują śledzenie obustronne. Mając przypadek testowy, jesteśmy w stanie odnieść go do jednego lub kilku wymagań biznesowych i na odwrót – dla każdego wymagania biznesowego możemy znaleźć jeden lub kilka przypadków testowych odpowiadających temu wymaganiu. Jeśli w naszym projekcie schemat śledzenia wyglądałby tak, jak na rysunku 3.2, to np. na podstawie informacji o tym, które

testy zostały zaliczone, moglibyśmy wnioskować o tym, w jakim stopniu pokryliśmy ryzyko projektowe lub jaka część wymagań może być uznana za przetestowaną. śledzenie (ang. traceability) – zdolność identyfikowania elementów w dokumentacji i oprogramowaniu

powiązanych

Rozważmy przykład systemu ELROJ. Możemy odwzorować wymagania na poszczególne metody w następujący sposób: Tabela 3.2. Mapowanie wymagań na elementy projektowe systemu ELROJ

W ymaganie

Odpowiadająca metoda lub metody

R01

setInfo(), getInfo(), removeInfo()

R02

addLine(), removeLine()

R03

addBus()

R04

addBusInterval()

R05

removeBus()

R06

getDisplayString()

R07

setActualDate()

Możemy zdecydować, że wymaganie R01 będzie pokryte w 100% dopiero po pozytywnym przetestowaniu wszystkich trzech odpowiadających mu metod (setInfo, getInfo, removeInfo). Możemy też określić stopień pokrycia tego wymagania dla wszystkich trzech funkcji z osobna (np. każda może pokrywać wymaganie w jednej trzeciej). Zauważmy, że w tabeli brakuje metod getAllBusLines, getAllBusesTimes oraz getNextBusTime. Są one wywoływane w innych metodach w celu uzyskania określonych informacji i nie są bezpośrednio związane z żadnym z wymagań. Ale moglibyśmy zdecydować, że jeśli np. metoda getDisplayString wywołuje te trzy funkcje (i żadne inne), to wymaganie R06 może zostać pokryte w stu procentach tylko wtedy, gdy pozytywnie przetestowana będzie zarówno metoda getDisplayString, jak i wszystkie wywoływane przez nią

metody. Podejście takie jest szczególnie warte rozważenia w kontekście określania rezydualnego (pozostałego) poziomu ryzyka w testowanym systemie.

3.1.4. Projektowanie testów Efektem fazy analizy jest odpowiedź na pytanie – „co” testować. Faza projektowania odpowiada z kolei na pytanie – „jak” testować zidentyfikowane obiekty testów. Składa się z trzech głównych kroków: 1) wybór poziomu ogólności przypadków testowych dla poszczególnych obszarów testowania; 2) wybór technik projektowania, które zapewnią odpowiednie pokrycie; 3) stworzenie przypadków testowych weryfikujących zidentyfikowane warunki testowe. Projektowanie testów może się rozpocząć dopiero wtedy, gdy zidentyfikowano wszystkie warunki testowe. Projektowane przypadki testowe mogą mieć charakter przypadków ogólnych lub szczególnych. Te pierwsze są nazywane przypadkami testowymi wysokiego poziomu, drugie – przypadkami testowymi niskiego poziomu. przypadek testowy wysokiego poziomu, logiczny przypadek testowy, abstrakcyjny przypadek testowy (ang. high level test case, logical test case, abstract test case) – przypadek testowy bez konkretnych wartości wejściowych i oczekiwanych rezultatów przypadek testowy niskiego poziomu, konkretny przypadek testowy (ang. low level test case, concrete test case) – przypadek testowy z konkretnymi (na poziomie implementacji) wartościami wejściowymi i wynikami oczekiwanymi test (ang. test) – pojedynczy przypadek testowy lub zestaw przypadków testowych [5] Przypadki testowe wysokiego poziomu mogą być przydatne w sytuacji, gdy rzeczywiste wartości oczekiwane nie są jeszcze znane lub dostępne. Ten rodzaj przypadków stosuje się również w sytuacji, gdy chcemy dać więcej swobody

testerowi w stosowaniu konkretnych technik testowania. Zwykle mamy wtedy do czynienia z testerem doświadczonym, zarówno w zakresie technik testowania, jak i samego produktu. Ten poziom ogólności pozwala uzyskać większy stopień pokrycia, gdyż każde wykonanie przypadku testowego wysokiego poziomu może wyglądać nieco inaczej. Logiczne przypadki testowe znajdują również zastosowanie w projektach, w których wymagania są niedodefiniowane lub podane w sposób ogólny. Sprawdzają się dobrze tam, gdzie nie występuje duży narzut dokumentacyjny na czynności testerskie. Z przypadków ogólnych można wyprowadzać przypadki szczególne. Przypadki testowe niskiego poziomu są stosowane w sytuacji, w której tester jest niedoświadczony lub system jest testowany przez eksperta dziedzinowego, mającego wiedzę o systemie, ale niekoniecznie o testowaniu. Przypadki niskiego poziomu opisują szczegółowo całą procedurę testową, dzięki czemu nawet osoba niedoświadczona w testowaniu może takie przypadki z powodzeniem wykonać. Przypadki niskiego poziomu sprawdzają się dobrze w projektach, w których wymagania są dobrze zdefiniowane oraz w sytuacji, gdy jest wymagana zewnętrzna weryfikacja procesu testowego, np. w postaci audytu. Ze względu na precyzję ich konstrukcji, nadają się doskonale do wielokrotnego wykonania w tym sensie, że każda realizacja takiego przypadku zawsze będzie wyglądać tak samo i zawsze powinna dać ten sam (oczekiwany) wynik. Przypadki testowe tworzy się przy użyciu technik projektowania testów omówionych w rozdziałach 8–10, wykorzystując zidentyfikowane warunki testowe. Techniki projektowania mogą być narzucone lub zasugerowane przez dokumenty takie jak polityka, strategia czy plan testów. Dobry przypadek testowy powinien cechować się następującymi atrybutami: powtarzalność – każde wykonanie tego samego przypadku testowego powinno dać taki sam wynik (zakładając brak zmian w kodzie między kolejnymi wykonaniami); niezależność – kolejność wykonywania przypadków testowych nie powinna mieć wpływu na ich wyniki; weryfikowalność – po wykonaniu przypadku testowego da się jednoznacznie określić rezultat jego wykonania i jego zgodność z wynikiem spodziewanym; możliwość śledzenia – każdy przypadek powinno dać się odnieść do podstawy testów (wymagań, elementów projektowych, funkcjonalności,

ryzyka itp.) w sposób określony przez używaną strategię testową. powtarzalność testów (ang. test reproducibility) – atrybut testu wskazujący, czy przy każdym wykonaniu testu otrzymujemy te same wyniki Niezależność może być niespełniona w przypadku, gdy mamy do czynienia z sekwencją przypadków testowych ujętych w jeden duży scenariusz testowy, dlatego należy ją rozumieć jako cechę odnoszącą się do pojedynczego przypadku lub – jeśli to konieczne – do scenariusza testowego. Przypadek testowy może zawierać różne dane, takie jak imię i nazwisko autora, data utworzenia przypadku testowego, funkcjonalność, której dotyczy. W szczególności powinien jednak obowiązkowo zawierać następujące informacje: identyfikator – w celu jednoznacznej identyfikacji każdego przypadku; cel – opis tego, co chcemy sprawdzić przez wykonanie przypadku; warunki wstępne – opisujące stan środowiska i systemu tuż przed wykonaniem przypadku; dane testowe (nie dotyczy przypadków wysokiego poziomu); oczekiwany wynik; warunki wyjściowe – opisujące stan środowiska i systemu po wykonaniu przypadku. identyfikator (ang. identifier) – unikatowe oznaczenie testu, wymagania lub innego artefaktu testaliów, pozwalające na jednoznaczne określenie tego elementu cel testu (ang. test objective) – przyczyna lub powód zaprojektowania i przeprowadzenia testu dane testowe (ang. test data) – dane, które istnieją przed wykonaniem testu i które mają wpływ na testowany moduł lub system bądź na które wpływa testowany moduł lub system warunek wstępny (ang. precondition) – warunki środowiska i stanu oprogramowania, które muszą być spełnione zanim moduł lub system będzie mógł być uruchomiony przez określony test lub procedurę testową

warunek wyjściowy (ang. postcondition) – warunki środowiska lub stanu oprogramowania, które muszą być spełnione po wykonaniu testu lub procedury testowej Dostarczenie wspomnianych informacji, z wyjątkiem identyfikatora i celu, może być zadaniem trudnym. Na przykład, danymi testowymi może być zestaw kilkunastu plików o dużym rozmiarze i skomplikowanej strukturze. Podczas testowania programów wykorzystujących sieci telekomunikacyjne ważne jest dokładne określenie wszystkich parametrów takiej sieci jako warunków wstępnych oraz wyjściowych. Oczekiwany wynik nie musi zawsze być widocznym na zewnątrz zachowaniem aplikacji. Może to być wewnętrzny stan programu, bazy danych, pamięci itd. Porównanie rezultatu aktualnego ze spodziewanym czasami jest trudne, żmudne lub czasochłonne, zwłaszcza, gdy musi być dokonywane dynamicznie. Dlatego często wymaga zastosowania specjalnych narzędzi (np. komparatorów). komparator, komparator testowy (ang. comparator, test comparator) – narzędzie testowe do przeprowadzania rezultatów rzeczywistych z oczekiwanymi

automatycznego

porównania

Przypadek tes towy PT008 Opis : s prawdzenie funkcjonalnoś ci ekranu Warunki ws tępne: baza danych o rozkładach jes t pus ta Krok Czynnoś ć 008.1

S prawdź, czy s ys tem poprawnie dodaje linię autobus ową do rozkładu

008.2 S prawdź, czy s ys tem poprawnie dodaje kurs y do rozkładu 008.3 S prawdź, czy s ys tem poprawnie wyś wietla kurs y na ekranie Warunki wyjś ciowe: baza zawiera poprawnie dodaną linię wraz z jej kurs ami, a informacja na ekranie jes t wyś wietlona poprawnie

Rysunek 3.3. Przypadek testowy wysokiego poziomu

Przypadek tes towy PT004 Opis : weryfikacja poprawneg o dodawania kurs u do rozkładu i wyś wietlania g o na ekranie Warunki ws tępne: baza danych o rozkładach jes t pus ta Krok Czynnoś ć

Oczekiwany wynik

004.1 Dodaj do rozkładu linię 104 W bazie s tworzono nową linię 104 004.2

Dodaj kurs w 45. minucie dla linii 104

W bazie dodano kurs 45 dla linii 104

004.3

Dodaj ponownie ten s am kurs dla linii 104

S tan bazy nie uleg ł zmianie

004.4

Dodaj kurs w 8. minucie dla W bazie dodano kurs 08 dla linii linii 104 104

004.5 Us tal dzień na 11.12.2013

Data zos tała us tawiona na 11.12.2013

Wyś wietl informację na 004.6 ekranie zawierającym 2 linie dla liczby minut = 40

Ekran zawiera nas tępujące informacje: 11.12.2013 Linia Czas 104 5 min 104 28 min Co odpowiada łańcuchowi „ 104 5 104 28” zwróconemu przez metodę g etDis playS tring (40, 2)

Warunki wyjś ciowe: baza zawiera dwa kurs y dla linii 104 (o minutach 45 i 08), a s tan ekranu wyg ląda tak, jak oczekiwany wynik dla kroku 004.6.

Rysunek 3.4. Przypadek testowy niskiego poziomu dynamiczne porównanie (ang. dynamic comparison) – porównywanie rzeczywistych i oczekiwanych rezultatów podczas wykonywania testów, na przykład za pomocą narzędzia do automatyzacji testów Rozważmy ponownie system ELROJ z Dodatku A. Przykłady przypadków testowych wysokiego i niskiego poziomu pokazane są odpowiednio na rysunkach 3.3 oraz 3.4. Dla przypadku wysokiego poziomu nie są podawane żadne dane wejściowe ani oczekiwane wyjścia, natomiast dla przypadku niskiego poziomu każdy krok jest opisany dokładnie, wraz z konkretnymi wartościami wejściowymi oraz oczekiwanymi rezultatami.

3.1.5. Implementacja testów Implementacja testów jest inżynierską realizacją produktów koncepcyjnej pracy wykonanej w fazie projektowania. Ten etap procesu dotyczy trzech aspektów testowania: przypadków testowych, danych testowych oraz środowiska testowego. Czynności związane z przypadkami testowymi to: implementacja przypadków testowych – realizowana na przykład w postaci fragmentu kodu wywołującego konkretną metodę z określonymi parametrami (np. testy jednostkowe), skryptu wywołującego szereg metod (implementacja scenariusza testowego), nagrywanie wykonywanych czynności (zwykle przy testowaniu GUI, przy użyciu takich narzędzi jak np. Selenium, opisanych w rozdz. 30); pisanie skryptów testowych – dotyczy głównie automatyzacji testów. Skrypty mogą zawierać zestaw często wykonywanych testów (np. testy regresji), mogą też reprezentować przypadki testowe wysokiego poziomu i być sparametryzowane, tzn. mogą umożliwiać wykonywanie tego samego testu dla różnych wartości wejściowych; ustalanie kolejności wykonywania przypadków testowych – kolejność ta może być podyktowana przeprowadzoną wcześniej analizą ryzyka lub specyficznymi zależnościami między przypadkami. Może również wynikać z tego, że niektórych przypadków nie można jeszcze wykonać

ze względu na

brak

odpowiedniej infrastruktury, która

będzie

dostarczona w późniejszym czasie, lub z braku odpowiednich ludzi; organizacja testów w tzw. suity testowe – suita testowa może być złożona z przypadków testowych, które muszą być wykonane w ściśle określonej kolejności lub też być zbiorem przypadków testujących np. tę samą funkcjonalność. W tym drugim znaczeniu suity testowe stosuje się w celu lepszej, czytelniejszej organizacji testów. implementacja testów (ang. test implementation) – proces projektowania i nadawania priorytetów procedurom testowym, tworzenia danych testowych i, opcjonalnie, przygotowywania uprzęży testowych, pisania automatycznych skryptów testowych, infrastruktury testowej: organizacyjnych artefaktów potrzebnych do wykonania testów, składających się ze środowisk testowych, narzędzi testowych, wyposażenia biurowego i procedur postępowania; dane testowe mogą być pozyskane z zewnętrznych źródeł przez obiekty testowe podczas wykonywania testów; zewnętrznym źródłem może być sprzęt, oprogramowanie lub człowiek skrypt testowy, specyfikacja procedury testowej, scenariusz testowy, procedura testowa (ang. test script, test procedure specification, test scenario) – dokument określający ciąg akcji umożliwiający wykonanie testu [5] automatyzacja testowania (ang. test automation) – użycie oprogramowania do wykonania lub wspierania czynności testowych, np. zarządzania testami, projektowania testów, wykonywania testów oraz sprawdzania i porównywania wyników automatyzacja wykonania testu (ang. test execution automation) – użycie oprogramowania, np. urządzenia rejestrująco-odtwarzającego, w celu kontrolowania wykonania testu, porównania rezultatów rzeczywistych z oczekiwanymi, ustawienia warunków wstępnych testu i innych funkcji kontroli i raportowania testu suita testowa, zestaw testowy, zestaw przypadków testowych, zbiór testów (ang. test suite, test case suite, test set) – 1) ciąg przypadków testowych,

w którym warunki wyjściowe z jednego testu używane są jako warunki wejściowe do następnego testu; 2) zestaw logicznie powiązanych ze sobą przypadków testowych, testujących np. tą samą funkcjonalność bądź obszar modułu lub systemu infrastruktura testu (ang. test infrastructure) – organizacyjne artefakty niezbędne do przeprowadzenia testu, składające się ze środowisk testowych, narzędzi testowych, wyposażenia biurowego i procedur Czynności związane z danymi testowymi to: implementacja konkretnych danych w przypadkach testowych – jeśli przypadki są opisane na szczegółowym poziomie i wymagają konkretnych wartości wejściowych; przygotowanie zbiorów danych, które mogą w przypadkach wysokiego poziomu;

być wykorzystane

implementacja generatorów danych – przydatna, gdy dane wejściowe są duże, mają skomplikowaną strukturę lub gdy przypadek testowy wymaga podania ciągu losowych danych symulujących określony profil użytkowania systemu; implementacja komparatorów – czyli narzędzi, które są w stanie porównać wyniki oczekiwane z rzeczywistymi. Jest to element automatyzacji testów, gdyż ułatwia pracę testerom, którzy nie muszą porównywać wyników testów ręcznie. dane wejściowe do testów (ang. test input) – dane otrzymywane z zewnętrznego źródła dostarczane do obiektu testów podczas wykonywania testu. Źródłem zewnętrznym może być sprzęt, oprogramowanie lub człowiek Czynności dotyczące środowiska testowego obejmują m.in.: tworzenie zaślepek i sterowników, niezbędnych do wykonania przypadków testowych, które używają niezaimplementowanych jeszcze elementów systemu; konfigurację sprzętu i narzędzi, które będą wykorzystywane w fazie uruchamiania testów, np. stacje bazowe w testowaniu

oprogramowania telekomunikacyjnego; instalację i konfigurację baz danych, systemów operacyjnych oraz innego niezbędnego oprogramowania – jest to czynność niezwykle ważna w przypadku testowania tzw. produktów z półki (ang. Commercial Off-The-Shelf, COTS), które należy przetestować w różnych konfiguracjach sprzętowych i software’owych (np. pod różnymi przeglądarkami, pod różnymi wersjami systemu operacyjnego). oprogramowanie z półki, COTS, oprogramowanie standardowe, komercyjne oprogramowanie z półki (ang. off-the-shelf software, standard software, commercial off-the-shelf software) – oprogramowanie stanowiące produkt wytworzony na szeroki rynek, to znaczy dla dużej liczby klientów, które jest dostarczane wielu klientom w identycznej postaci oprogramowanie na zamówienie, niestandardowe oprogramowanie (ang. bespoke software, custom software) – oprogramowanie projektowane dla grupy ściśle określonych użytkowników lub klientów; przeciwieństwo oprogramowania z półki sterownik, sterownik testowy (ang. driver, test driver) – moduł oprogramowania lub narzędzie testowe, które zastępuje (symuluje) moduł kontrolujący lub wywołujący funkcje testowanego modułu lub systemu [28] zaślepka (ang. stub) – szkieletowa albo specjalna implementacja modułu używana podczas produkcji lub testów innego modułu, który tę zaślepkę wywołuje albo jest w inny sposób od niej zależny; zaślepka zastępuje wywoływany moduł [7] Rozważmy projekty przypadków testowych wysokiego i niskiego poziomu przedstawionych na rysunkach 3.3 i 3.4. Ich implementacje mogłyby wyglądać tak, jak na listingach 3.1 i 3.2.

1 void TestCase008(int line, int[] courses, int displayLines, int mm) { 2 TimetableFacade facade = new TimetableFacade();

3 4

facade.addLine(line); for (i=0; i=3, gdzie x jest zmienną typu int), to elementami pokrycia dla tego warunku może być sama wartość 3 oraz wartość brzegowa leżąca po drugiej stronie granicy przedziału [3, ∞), czyli 2. Jeśli warunkiem testowym są klasyfikacje i klasy drzewa decyzyjnego, to elementami pokrycia mogą być kombinacje tych klasyfikacji. Jeśli warunkami testowymi są klasy równoważności, to są one jednocześnie elementami pokrycia, itd. Elementy pokrycia należy opisać w specyfikacji przypadku testowego. Etap czwarty to wyprowadzenie przypadków testowych w celu pokrycia elementów pokrycia. Dokonuje się tego przez wybór wartości wejściowych. Może się zdarzyć, że jeden przypadek testowy pokryje więcej niż jeden element pokrycia. Pozwala to zredukować liczbę przypadków testowych, ale może wydłużyć czas debugowania. Mamy zatem następujące dwa, skrajne podejścia do projektowania przypadków testowych ze względu na sposób traktowania elementów pokrycia ([36], [10]): podejście „jeden do jednego” (ang. one-to-one), w którym jeden przypadek testowy pokrywa dokładnie jeden element pokrycia; podejście minimalizujące, w którym jeden przypadek pokrywa wiele (możliwie jak najwięcej) elementów pokrycia.

testowy

Wady i zalety obu podejść, a także rozwiązania pośrednie przedyskutujemy przy omawianiu technik projektowania przypadków testowych w rozdziale 8. Etap piąty to stworzenie suit testowych, które norma ISO/IEC/IEEE 29119 nazywa zbiorami testów. Jeden zbiór testów może np. zawierać przypadki testowe, które należy wykonać ręcznie, a inny przypadki, które można zautomatyzować. Innym przykładem może być wydzielenie zbioru testów, które wymagają specjalnej konfiguracji środowiska. Zbiory testów powinny być opisane w specyfikacji procedury testowej.

Ostatnim etapem jest stworzenie procedur testowych. Polega on na uporządkowaniu przypadków testowych w obrębie zbiorów testowych na podstawie zależności opisywanych warunkami wejściowymi i wyjściowymi, a także na podstawie innych wymagań testowych. Na każdym etapie należy zapewnić śledzenie między

poszczególnymi

artefaktami. Na samym końcu procesu powinniśmy mieć obustronne śledzenie między podstawą testów, zbiorami cech, warunkami testowymi, elementami pokrycia, przypadkami testowymi, zbiorami testów oraz procedurami testów. Wszystkie artefakty (np. specyfikacje, projekty, zaakceptowane przez udziałowców projektu.

plany)

powinny

zostać

1 Dokładny opis dokumentów wykorzystywanych w procesie testowym oraz ich zawartości jest zawarty w rozdziale 21. 2 Strategie testowania są omówione w rozdziałach 19 oraz 20. 3 W słowniku ISTQB [6] termin test bed jest przetłumaczony jako... łoże testowe (sic!). Angielskie słowo bed ma kilka znaczeń i w tym kontekście nie oznacza łóżka, ale podstawę, podłoże czegoś. 4 Projekt produktu (systemu) może składać się z takich elementów jak: specyfikacja wymagań, projekt funkcjonalny, projekt techniczny, diagramy UML, projekt bazy danych, specyfikacje interfejsów, atrybuty jakościowe oprogramowania, kryteria akceptacji przy przechodzeniu do poszczególnych faz projektu, przypadki i scenariusze użycia, normy, standardy, regulacje prawne, które produkt musi spełniać.

4. Testowanie w cyklu życia oprogramowania

Testowanie oprogramowania nigdy nie odbywa się w próżni – zawsze dotyczy konkretnego projektu, który jest wytwarzany według swojego własnego cyklu życia. Dlatego proces testowy musi być dopasowany do procesu wytwarzania oprogramowania. Na każdym etapie produkcji oprogramowania przedmiotem testów mogą być zupełnie inne artefakty. Może to mieć wpływ na to, jakie typy testów zostaną przeprowadzone. W tym rozdziale przedstawimy najczęściej spotykane modele wytwórcze i przeanalizujemy ich wady i zalety pod kątem czynności testowych. Następnie opiszemy typowe poziomy oraz typy testów. cykl życia oprogramowania (ang. software life cycle) – okres czasu rozpoczynający się, gdy pojawi się pomysł na oprogramowanie i kończący się, gdy oprogramowanie nie jest już dostępne do użytku; cykl życia oprogramowania zawiera zazwyczaj fazę koncepcji, fazę wymagań, fazę projektowania, fazę implementacji, fazę testów, fazę instalacji i zastępowania, fazę wykorzystania produkcyjnego i pielęgnowania oraz – czasami – fazę wycofania; fazy te mogą na siebie nachodzić, być wykonywane równolegle lub iteracyjnie model cyklu życia (ang. lifecycle model) – podział życia produktu lub projektu na fazy faza wymagań (ang. requirements phase) – przedział czasu w cyklu życia oprogramowania, podczas którego są zbierane i dokumentowane wymagania na oprogramowanie [7]

4.1. Modele wytwarzania oprogramowania Model cyklu życia (lub model wytwarzania oprogramowania) to wysokopoziomowy, abstrakcyjny opis procesu produkcji oprogramowania. Model taki zwykle dzieli proces tworzenia oprogramowania na fazy i określa relacje między nimi. Wybór modelu cyklu życia ma wpływ na podstawowy proces testowy, który musi być dopasowany do modelu wytwórczego. Modele cyklu życia można podzielić na trzy główne grupy: modele sekwencyjne, w których fazy następują po sobie; modele iteracyjne, w których cykl składa się z szeregu strukturalnie podobnych do siebie iteracji, kończących się w momencie osiągnięcia założonego poziomu jakości; w modelach tych duży nacisk kładzie się na testy regresji, a więc i na zagadnienia automatyzacji testów; modele przyrostowe, stanowiące kombinację modeli sekwencyjnych i iteracyjnych; w modelach przyrostowych tworzone są prototypy produktu, z których każdy jest w pełni działającym programem, ale z ograniczoną funkcjonalnością, dodawaną w kolejnych fazach prototypowania. Niektóre podejścia łączą w sobie cechy dwóch ostatnich grup, dlatego zwykle mówi się o modelach iteracyjno-przyrostowych. iteracyjny model wytwarzania (ang. iterative development model) – metoda wytwarzania oprogramowania, w której projekt jest podzielony na dużą liczbę iteracji; iteracja jest zamkniętym cyklem wytwórczym dającym działającą wersję produktu (wewnętrzną lub zewnętrzną) będącą podzbiorem finalnego produktu, który rozrasta się z iteracji na iterację aż do powstania produktu końcowego przyrostowy model wytwarzania (ang. incremental development model) – model wytwarzania oprogramowania, w którym przedsięwzięcie jest realizowane przyrostowo, w cyklach, z których każdy dostarcza część funkcjonalności z całego zbioru wymagań; wymagania są porządkowane według priorytetów i realizowane w tej kolejności w odpowiednich przyrostach; w niektórych

wersjach tego modelu każdy podprojekt jest realizowany zgodnie z „mini” modelem V z fazami projektowania, kodowania i testowania priorytet (ang. priority) – poziom (biznesowej) ważności określony dla elementu, np. wymagania, defektu, testu

4.1.1. Model kaskadowy Model kaskadowy (nazwany przez Winstona Royce’a modelem wodospadowym) jest klasycznym modelem sekwencyjnym. Składa się z liniowo uporządkowanych, następujących po sobie faz. W ścisłym modelu kaskadowym po zakończeniu danej fazy nie da się już do niej powrócić. Został on przedstawiony na rysunku 4.1. Istnieją odmiany tego modelu, które pozwalają na powrót do faz wcześniejszych. Zauważmy, że niezależnie od tego, czy mamy do czynienia z modelem ścisłym, czy dopuszczającym powroty, faza testowania zawsze rozpoczyna się dopiero po ukończeniu implementacji. Model nie uwzględnia bezpośrednio możliwości wykorzystania faz wcześniejszych do planowania i projektowania testów. Błędy będą znajdowane dopiero po fazie implementacji, co zwiększa koszt ich usunięcia. Model sprawdza się w projektach, w których wymagania są bardzo dobrze określone i nie będą się zmieniać w czasie trwania prac deweloperskich, aczkolwiek coraz rzadziej można spotkać organizację, która wprost przyznawałaby się do stosowania tego modelu. Obecnie nie przystaje on do większości technik wytwarzania oprogramowania.

Rysunek 4.1. Model kaskadowy (wodospadowy)

4.1.2. Model V Model V jest sekwencyjnym modelem starającym się przezwyciężyć wady modelu kaskadowego. Jego schemat przedstawiono na rysunku 4.2. Szare strzałki pokazują sekwencyjność faz, a ich graficzny układ tworzy literę V – stąd nazwa modelu. Jego lewe ramię pokazuje następstwo faz wytwórczych, a prawe – następstwo faz testowych. Testowanie wciąż odbywa się po fazie kodowania, ale poziome strzałki biegnące od faz testowych do faz projektowych sugerują bardzo istotną rzecz, stanowiącą główną ideę modelu V. Chodzi o to, że czynności testowe powinny rozpocząć się jak najwcześniej. Podczas zbierania wymagań mogą powstawać plany testów akceptacyjnych; w fazie specyfikacji powstają plany testów systemowych itd. Ponadto, w każdej fazie wytwórczej (lewe ramię modelu) przeprowadza się przeglądy i inspekcje produktów faz poprzednich, co reprezentują czarne strzałki idące w górę ramienia. Zarówno inspekcje, jak i plany testów pozwalają już na początkowych etapach wytwórczych znaleźć wiele potencjalnych problemów i umożliwiają ich wczesne usunięcie.

Zalety modelu V to: klarowne wyróżnienie i odróżnienie od siebie faz cyklu życia oraz zależności między nimi; wymóg jak najwcześniejszego tworzenia dokumentacji testowej; równomiernie rozłożony nacisk na fazy tworzenia i fazy testowania.

Rysunek 4.2. Model V Model V jest równocześnie krytykowany z kilku powodów. Przede wszystkim jest modelem sekwencyjnym – w rzeczywistych warunkach wymagania zawsze się zmieniają; model tego nie uwzględnia. Brian Marick [37] zarzuca modelowi V rozdzielenie różnych poziomów testów (np. osobne wykonywanie testów jednostkowych i osobne testów integracyjnych). To rozdzielenie powoduje np. w przypadku testów jednostkowych konieczność tworzenia wielu namiastek bądź sterowników. Marick uważa, że lepszym rozwiązaniem jest testowanie modułów podczas integracji, gdy wszystkie otaczające testowany moduł systemy i inne moduły są już zaimplementowane. Jednocześnie sugeruje, że wszystkie elementy na prawej gałęzi modelu powinny po prostu nazywać się „wykonaj odpowiednie testy”. Krytyka dotyczy również zbytniej prostoty samego modelu – projekty

informatyczne często są bardzo skomplikowane i trudno opisać ich złożoność za pomocą prostych modeli. Może to bezpieczeństwa.

dawać menedżerom

złudne poczucie

model V (ang. V model) – opis czynności cyklu wytwarzania oprogramowania od specyfikacji wymagań do pielęgnacji. Model V ilustruje, jak czynności testowe mogą być integrowane z każdym etapem cyklu życia wytwarzania oprogramowania

4.1.3. Model W Model W jest modyfikacją modelu V, w której wprost opisuje się tworzenie planów testów podczas kolejnych faz wytwórczych. Model jest przedstawiony na rysunku 4.3. W zasadzie cechy modelu W można opisać, rozszerzając nieco opis modelu V. Wszystkie argumenty

Rysunek 4.3. Model W za i przeciw modelowi V można powtórzyć pod adresem modelu W, dlatego nie będziemy go szczegółowo omawiać.

4.1.4. Rational Unified Process (RUP)

Rational Unified Process należy do rodziny modeli iteracyjnych, choć w zasadzie jest „produktem w postaci procesu”, czyli adaptacyjnym frameworkiem, który można konfigurować i dopasowywać do swoich potrzeb. RUP został stworzony w 1996 roku przez Rational Software. RUP definiuje 9 tzw. dyscyplin (wymiar zawartości) podzielonych na 4 etapy (wymiar czasu) [38], [39]. Architektura RUP jest przedstawiona na rysunku 4.4. Wykresy po prawej stronie pokazują intensywność prac w ramach poszczególnych dyscyplin w funkcji czasu. W każdej dyscyplinie zdefiniowanych jest wiele ról i artefaktów. Jeśli chodzi o dyscyplinę testowania, to RUP wymienia cztery główne role: kierownik testowania – związanych z testowaniem;

odpowiadający

za

powodzenie

prac

analityk testowania – wybiera metody testowania, definiuje testy, monitoruje ich postępy; projektant testowania – wybiera właściwe techniki, narzędzia i zasady implementowania testów, zgłasza zapotrzebowanie na zasoby niezbędne do realizacji zadań testowania; tester – wykonuje testy, porównuje wyniki, ocenia przebieg testowania, rejestruje żądania zmian. Jeśli chodzi o artefakty dla procesu testowego, to RUP wymienia ich bardzo wiele, wraz z opisem zależności między nimi. W większości są to klasycznie stosowane dokumenty, takie jak: plan testów, strategia testowania, przypadki testowe, konfiguracja środowiska

Rysunek 4.4. Model RUP testowego, suity testowe, scenariusze testowe, dziennik testów, żądania zmiany. Niestandardowym dokumentem jest „spis pomysłów testowania”, który tworzy się w wyniku zaistnienia żądania zmiany. RUP zawiera także diagram przebiegu prac testowania ze szczegółami i zależnościami między nimi. RUP wymaga testowania poszczególnych funkcjonalności systemu w miarę ich implementowania. Zastosowanie podejścia iteracyjnego w RUP wymusza częste testowanie oraz wczesne rozpoczęcie tej czynności. Duży nacisk jest położony na testy regresji. RUP można dostosować do większości projektów, ale należy uważać, by nie ulec fałszywemu przekonaniu, że samo wykorzystanie wszystkich proponowanych przez model artefaktów sprawi, że proces będzie przebiegał bez problemów. Zadaniem kierownika testów jest to, aby proces oferowany przez RUP skroić na miarę dla konkretnego projektu. Rational Unified Process, RUP – komercyjna, adaptowalna struktura iteracyjnego wytwarzania oprogramowania składająca się z czterech faz życia projektu: rozpoczęcie, opracowanie, tworzenie, przekazanie

4.1.5. Rapid Application Development (RAD) Model RAD, czyli model szybkiego prototypowania jest prekursorem metodyk

zwinnych. Stawia on nacisk na szybkie dostarczanie prototypów. Cadle i Yeats [40] wskazują na zastosowanie modelu RAD w celu uzyskania przewagi konkurencyjnej, gdzie akceptujemy fakt, że wdrażany system może być niedoskonały. Schemat modelu RAD (przebieg jednej iteracji) jest przedstawiony na rysunku 4.5.

Rysunek 4.5. Model szybkiego prototypowania RAD Cechą odróżniającą RAD od bardziej klasycznych, sekwencyjnych modeli wytwarzania oprogramowania jest to, że iteracje i udoskonalania są traktowane jako integralna część procesu, a nie jako niepożądane zdarzenia, których należy unikać. Istnieje kilka szkół myślenia na temat RAD, ale jedną z cech wspólnych dla nich wszystkich jest to, że testowanie traktuje się jako integralną część cyklu iteracyjnego. Model jest stosowany w środowisku, w którym wytwarzanie kolejnych iteracyjnych bądź przyrostowych wersji oprogramowania następuje bardzo szybko. Menedżer testów nie może więc pozwolić sobie na precyzyjne planowanie procesu testowego. Podejście analityczne może się tu nie sprawdzić. Lepsza będzie strategia reaktywna testowania. Jednocześnie, model RAD kładzie nacisk na stosowanie technik komponentowych [41], co może ułatwić proces testowy, gdyż powtórnie używane komponenty są już zwykle przetestowane.

4.1.6. Model spiralny Boehma Model spiralny został zaproponowany przez Boehma w 1986 roku [42]. Jest to model z rodziny iteracyjno-przyrostowej. Jego schemat jest przedstawiony na rysunku 4.6. Cykl życia jest tu opisany jako ewolucja następujących po sobie faz zakończonych dostarczeniem kolejnych wersji prototypów. Ostatni cykl spirali

można traktować jak model kaskadowy. Podejście ewolucyjne dobrze sprawdza się w projektach, w których wymagania podlegają częstym zmianom. Kluczową cechą modelu spiralnego jest analiza ryzyka, występująca przed początkiem każdej fazy. Podejście oparte na ryzyku umożliwia łączenie w ramach modelu spiralnego elementów podejść opartych na specyfikacji, prototypach, symulacji itd. Boehm w swojej późniejszej pracy [43] opisał wręcz model spiralny jako „generator modeli procesów”, w którym wybory oparte na analizie ryzyka projektowego generują właściwy model procesu dla tego projektu. W takim ujęciu modele: kaskadowy, V, RUP, zwinne, inkrementacyjne i inne mogą być traktowane jako szczególne postaci modelu spiralnego. W modelu spiralnym proces testowy rozpoczyna się w początkowych fazach projektu. Jeśli chodzi o strategię testowania, to sugeruje on wybór strategii analitycznej opartej na ryzyku. Model uwzględnia zmieniające się wymagania, co może stanowić element analizy ryzyka. W takim przypadku kierownik testów, w porozumieniu z analitykiem testów, może odpowiednio zaplanować czynności testowe oraz rozłożyć wysiłek zespołu w procesie tworzenia i wykonywania testów.

Rysunek 4.6. Model spiralny Boehma

4.1.7. Metodyki zwinne Metodyki zwinne to grupa iteracyjnych metod wytwarzania oprogramowania opartych na tzw. manifeście Agile zdefiniowanym w 2001 roku. manifest Agile (ang. Agile Manifesto) – manifest określający zasady stanowiące zwinne wytwarzanie oprogramowania: – ludzie i współpraca ponad procesy i narzędzia; – działające oprogramowanie ponad obszerną dokumentację; – współpraca z klientem ponad formalne ustalenia; – reagowanie na zmiany ponad podążanie za planem [44] Do grupy metodyk zwinnych zalicza się m.in. Scrum, Dynamic Systems Development Method (powstały na podstawie metodyki RAD), Test Driven Development, Behavioral Driven Development, Feature Driven Development, z których najpopularniejszą obecnie jest Scrum. Często do grupy metodyk zwinnych zalicza się tzw. programowanie ekstremalne (eXtreme Programming, XP), jednak jest to raczej paradygmat wytwarzania oprogramowania podany w formie tzw. dobrych praktyk niż dobrze zdefiniowana metodyka. wytwarzanie ukierunkowane na cechy (ang. Feature-Driven Development, FDD) – iteracyjny i przyrostowy proces wytwarzania oprogramowania ukierunkowany na punkt oceny funkcjonalności (cechy) przez jej wartość dla klienta. Wytwarzanie ukierunkowane na cechy jest wykorzystywane głównie w zwinnym wytwarzaniu oprogramowania Z punktu widzenia testera zasadniczą wadą metodyk zwinnych, która prawdopodobnie wynika z ich wciąż dość młodego wieku, jest brak spójnej koncepcji zarządzania jakością. O ile metodyki zwinne bardzo dobrze opisują sam proces wytwórczy, o tyle wciąż nie ma zgody co do właściwego opisu procesów dotyczących zapewniania jakości, a w szczególności testowania. Niektórzy twierdzą, że zwinne testowanie jest lub powinno być czymś zupełnie innym niż w starszych modelach, inni uważają, że ma ono dokładnie taką samą formę jak testowanie w metodykach klasycznych. Powstaje dużo publikacji poświęconych tej tematyce (np. [45]). Zwraca się coraz większą uwagę na testy niefunkcjonalne

(zwykle metodyki zwinne kojarzone są wyłącznie z podejściem TDD – Test Driven Development, wykorzystywanym głównie w testach jednostkowych), na rolę testów w dokumentowaniu z klientem.

wymagań,

automatyzację,

ścisłą

współpracę

zwinne wytwarzanie oprogramowania (ang. agile software development) – grupa metodyk wytwarzania oprogramowania oparta na iteracyjnym, przyrostowym modelu wytwarzania oprogramowania, w którym wymagania i rozwiązania ewoluują przez współpracę w ramach samoorganizujących się, realizujących wiele funkcji zespołów Niezależnie od wciąż istniejącego zamieszania co do zarządzania jakością w metodykach zwinnych, da się w nich wyodrębnić pewne techniki czy praktyki związane z testowaniem. Omawiamy je dalej w kolejnych podrozdziałach. testowanie zwinne (ang. agile testing) – metoda testowania stosowana w projektach korzystających z metodologii zwinnych stosująca techniki i metody takie jak programowanie ekstremalne (XP), traktująca wytwarzanie jako klienta testowania i kładąca nacisk na podejście „najpierw test” (ang. test driven) 4.1.7.1. XP Programowanie ekstremalne [46] (ang. eXtreme Programming) proponuje bardzo ciekawą z punktu widzenia testowania technikę, mianowicie tzw. programowanie w parach. Polega ona na tym, że proces tworzenia kodu odbywa się przy udziale dwóch osób, z których jedna koduje, a druga kontroluje to, co jest kodowane. Technika ta jest bardzo skuteczna w błyskawicznym wykrywaniu wielu błędów dzięki niezależnej „drugiej parze oczu”. Osoba niepisząca nie musi koncentrować się na czynności kodowania, dzięki czemu może w pełni skupić się na samym kodzie. Część błędów mogłaby zostać wyłapana przez kompilator, ale są one poprawiane na bieżąco, dlatego oszczędzamy czas na nieudane kompilacje i wyszukiwanie błędów w gotowym, kompilowalnym kodzie. Jeśli w projekcie możemy pozwolić sobie na przeznaczenie dwóch programistów do pisania jednego kodu, to zdecydowanie powinniśmy to zrobić.

programowanie ekstremalne (ang. eXtreme Programming, XP) – zbiór praktyk inżynierii oprogramowania wykorzystywanych w ramach zwinnego wytwarzania oprogramowania. Podstawowe praktyki to: programowanie w parach, wykonywanie dokładnych przeglądów kodu, testowanie modułowe całego kodu, jasność i przejrzystość kodu programowanie parami (ang. pair programming) – metoda wytwarzania oprogramowania, w której linie kodu (produkcyjne i/lub testowe) modułu są pisane przez dwóch programistów siedzących przy jednym komputerze. Domyślnie oznacza to odbywający się w czasie rzeczywistym przegląd kodu Można nieco zmodyfikować zasadę programowania w parach i jeszcze bardziej zwiększyć skuteczność jej stosowania, gdy programiście towarzyszyć będzie nie drugi deweloper, ale tester. Poza tym, że na bieżąco będzie wskazywać popełniane przez programistę błędy, tester może sobie notować na boku uwagi dotyczące tego, co należy przetestować w dalszych etapach tworzenia oprogramowania, w szczególności podczas testów integracyjnych. Uwagi te mogą mu przychodzić do głowy na skutek obserwacji pracy programisty oraz rozmawiania z nim podczas tworzenia kodu. Ponadto, tester w trakcie prac deweloperskich może sugerować programiście rozwiązania projektowe zwiększające testowalność kodu. To podejście będzie skuteczne, gdy tester ma również jakieś doświadczenie w tworzeniu oprogramowania (musi co najmniej znać wykorzystywany w kodowaniu język programowania). skuteczność (ang. effectiveness) – zdolność do osiągania zamierzonego celu 4.1.7.2. Scrum Podstawy metodyki Scrum zostały opisane w [47]. Metodyka ta zakłada istnienie samoorganizujących się zespołów w projekcie, w którym występują częste zmiany wymagań. Jest metodyką iteracyjną. Proces wytwarzania składa się z przebiegów (tzw. sprintów) trwających zwykle od 2 tygodni do 1 miesiąca. Każdy przebieg jest złożony z przebiegów dziennych. Cechami charakterystycznymi Scruma są: przebiegi (sprinty, iteracje), czyli ściśle określone ramami czasowymi okresy stanowiące podstawowe jednostki wysiłku zespołu, w ramach

których realizowane są poszczególne funkcjonalności systemu; codzienne spotkania (ang. stand-up meeting), zwykle odbywane na stojąco, aby wymusić szybki i sprawny ich przebieg; na spotkaniach tych omawia się zakres prac wykonanych w poprzednim dniu, podział prac w nadchodzącej iteracji, problemy, z jakimi członkowie zespołu, metody ich rozwiązywania itp.;

spotkali

się

Rysunek 4.7. Model Scrum backlog sprintu (lista zaległości), czyli spis wszystkich prac, które pozostały do wykonania w ramach danego przebiegu. Elementy do backlogu sprintu są wybierane z backlogu produktu przez zespół scrumowy, na podstawie rozmów z klientem oraz własnej oceny. Metodyka Scrum nie skupia się na zagadnieniach jakości. W niektórych wersjach modelu można spotkać się z dokumentem „Definition of Done” zawierającym kryteria wyjścia dla zakończenia prac nad danym produktem z backlogu. Zwykle kryteria te zawierają wymóg zaliczenia wszystkich testów regresji. SCRUM (ang. SCRUM) – iteracyjna, przyrostowa metodologia zarządzania projektem, powszechnie stosowana w zwinnym wytwarzaniu oprogramowania W Scrum, jako metodyce iteracyjnej, uwzględniającej częste zmiany wymagań, należy zwrócić szczególną uwagę na projektowanie i utrzymywanie zestawów testów regresyjnych. Istnieje pewna sprzeczność między celami jakościowymi

a filozofią metodyk zwinnych: metodyki te z jednej strony nie przykładają dużej wagi do dokumentacji, ale z drugiej zaś, ze względu na częste zmiany wymagań jest niezwykle istotne, aby móc śledzić testy wstecz do tych wymagań. Nie da się tego zorganizować dobrze bez odpowiedniego narzutu dokumentacyjnego. Więcej informacji o Scrum Czytelnik może znaleźć w książkach [48] i [49]. Pozycja [50] omawia dokładnie testowanie w tej metodyce. 4.1.7.3. TDD Test Driven Development (TDD), czyli technika wytwarzania oparta na testach, polega na tworzeniu testów dla danego modułu zanim rozpocznie się właściwe tworzenie tego modułu. TDD obecna była w eXtreme Programming jako jedna z dobrych praktyk testowania, ale obecnie wydzieliła się z XP i rozwinęła w osobną, samodzielną metodykę. Nie obejmuje ona całości procesu testowego. Skupia się głównie na niższych poziomach testowania (testowanie komponentów) i jest związana raczej z pracą programisty niż testera.

Rysunek 4.8. TDD Filozofia TDD [51] jest w swym założeniu bardzo prosta. Schematycznie jest pokazana na rysunku 4.8. Opisuje styl programowania składający się z trzech kroków: pierwszą czynnością jest zaprojektowanie i napisanie testu. Test ten oczywiście nie będzie działał, bo nie ma kodu do testowania. Drugi krok polega na napisaniu tego kodu. Po jego napisaniu test jest ponownie uruchamiany i powinien być zaliczony. Wtedy kod poddawany jest refaktoryzacji i można przystąpić do pisania kolejnego fragmentu, znów rozpoczynając od napisania testu.

wytwarzanie sterowane testami (ang. Test Deiven Development, TTD) – sposób wytwarzania oprogramowania, w którym przypadki testowe są przygotowywane i często automatyzowane zanim powstanie oprogramowanie, które będzie testowane za ich pomocą Takie podejście ma kilka zalet. Po pierwsze, zaczynamy od zaprojektowania testu, dlatego już na tym etapie możemy odkryć pewne pułapki i niebezpieczeństwa związane np. z niejasnymi bądź sprzecznymi wymaganiami. Skupiając się na teście, a nie na kodzie programista ukierunkowuje swoje myślenie na to, co może pójść źle. Wiedza ta ma charakter prewencyjny – podczas pisania kodu programista jest już świadomy różnych zagrożeń i dlatego tworzony kod będzie miał lepszą jakość, niż w przypadku rozpoczęcia kodowania bez uprzedniego tworzenia testu. Po drugie, jeśli uda nam się w ten sposób zmniejszyć gęstość defektów, to zapewnianie jakości może stać się czynnością proaktywną, a nie reaktywną. Po trzecie, niższa gęstość defektów i lepsza jakość kodu pozwalają menedżerom w lepszym szacowaniu parametrów projektu, ponieważ wystąpi mniej niespodziewanych zdarzeń zaburzających planowe czynności wytwórcze. Po czwarte, element refaktoryzacji sprawi, że tworzony kod będzie lepiej poddawał się testowaniu. 4.1.7.4. BDD Filozofia podejścia BDD jest oparta na TDD. W BDD, tak jak w TDD, zanim napisze się kod, tworzy się test. Różnica jest taka, że w TDD jest to zwykle test jednostkowy. W BDD są to raczej testy akceptacyjne, związane z logiką biznesową programu. Tworzy się je w postaci historyjki użytkownika (zwanej scenariuszem), opisującej pożądane zachowanie fragmentu kodu, który chcemy stworzyć. Historyjka składa się z trzech części: given, when oraz then („mając zadane…”, „gdy…”, „wtedy…”). Testy są pisane w języku naturalnym, a odpowiednie narzędzie (patrz p. 30.6.10) przypasowuje odpowiednie fragmenty zdań języka naturalnego do wzorców transformowanych na wykonywalny kod, dzięki czemu wykonywanie testów jest automatyzowane. Rozważmy prosty przykład oparty na [52]. Chcemy stworzyć kalkulator, w którym operator jest poprzedzany operandami (czyli np. działanie 3+4 polega najpierw na wpisaniu liczb 3 i 4, a potem naciśnięciu klawisza +). Oto przykładowy scenariusz, opisujący jedno z wymagań wobec tego kalkulatora:

Cecha: dzielenie Kalkulator mus i mieć możliwoś ć wykonania dzielenia dwóch liczb S cenarius z: dzielenie pros tych liczb całkowitych * Wpis ałem 3 do kalkulatora * Wpis ałem 2 do kalkulatora * Nacis nąłem / * Kalkulator powinien wyś wietlić 1.5 Scenariusz ten jest napisany w języku naturalnym. Aby obsłużyć taką historyjkę, należy napisać kod transformujący wyrażenia z języka naturalnego na konkretne operacje w systemie. Przykład takiego kodu jest pokazany na listingu 4.1.

Before do @calc = Calculator.new end After do end Given /Wpisałem (\d+) do kalkulatora/ do |n| @calc.push n.to_i end When /Nacisnąłem (\w+)/ do |op| @result = @calc.send op end Then /Kalkulator powinien wyświetlić (.*)/ do |result| @result.should == result.to_f

end Listing 4.1. Historyjka użytkownika w podejściu BDD Wyrażenia w nawiasach /.../ oznaczają frazy wyszukiwane w tekście. W nawiasach okrągłych znajdują się wartości, które są przypisywane do zmiennych o nazwach ujętych w znaki |...|. Na przykład tekst „Nacisnąłem /” zostanie przypisany do wzorca /Nacisnąłem (\w+)/, przy czym zmienna op przyjmie wartość „/”. Składnia wyrażeń w nawiasach okrągłych jest związana z obsługą wyrażeń regularnych1, np. „.*” oznacza dowolny ciąg znaków, „\d+” – dowolny niepusty ciąg cyfr itd. Dla każdego przypasowanego fragmentu kod wykonuje odpowiednią operację, np. dla tekstu w sekcji Given jest wykonywana instrukcja calc.push n.to_i oznaczająca wpisanie do kalkulatora wartości zmiennej n zrzutowanej na typ całkowity. Sekcje Before oraz After w skrypcie zawierają instrukcje służące odpowiednio do montowania i rozmontowania środowiska testowego, potrzebnego do przeprowadzenia danego testu. W naszym przypadku sekcja Before składa się z jednej instrukcji, tworzącej instancję kalkulatora, na której będą wykonywane zadane operacje. Sekcja After jest pusta – po zakończeniu testu nic nie musimy rozmontowywać. Osoba tworząca wymagania w postaci scenariuszy nie musi znać języka programowania. Wystarczy, że będzie posługiwała się odpowiednimi frazami języka naturalnego. Skrypt przetransformuje to na konkretne operacje w programie i przetestuje taką historyjkę, porównując wynik wykonania z tym, co zostało zapisane w sekcji Then. W podejściu BDD konstruujemy najpierw scenariusz testowy zachowania systemu, a następnie tworzymy kod, który zaliczy ten test.

4.1.8. Metodologia Cleanroom Metodologia Cleanroom postrzega proces wytwarzania oprogramowania jako czynność stricte inżynierską, opartą na podstawach naukowych, głównie na metodach statystycznych i matematycznych [53]. Aspekt tworzenia produktu jako proces programistycznych prób i błędów ma tu drugorzędne znaczenie. Innymi przykładami tego typu formalnych podejść są Wiedeńska Metoda Produkcji (ang. Vienna Development Model, VDM) oraz notacja Z [54], [55].

W podejściu Cleanroom dąży się do osiągnięcia przez produkt określonego poziomu niezawodności przez rygorystyczny proces kontroli nastawiony na zapobieganie

defektom

niż

na

ich

późniejsze

wykrywanie

i

usuwanie.

W osiągnięciu tego celu pomaga wykorzystanie metod formalnych podczas projektowania oprogramowania, metod statystycznej kontroli jakości podczas inkrementacyjnych testowania.

faz

wytwórczych

oraz

statystycznego

podejścia

do

kontrola jakości (ang. quality control) – operacyjne techniki i działania, część zarządzania jakością, koncentrująca się na spełnieniu wymagań jakościowych [56] Testowanie (w Cleanroom nazywane certyfikacją) oparte jest na formalnej specyfikacji, wykorzystywanej do tworzenia profili operacyjnych programu, czyli oczekiwanych sposobów jego użycia. Na ich podstawie projektuje się testy tak, aby skupić się głównie na najczęściej wykorzystywanych obszarach funkcjonalności testowanego programu. Poziom jakości nie jest obliczany jako gęstość defektów, ale wyrażany w terminach odchyleń standardowych, co przypomina podejście stosowane w Six Sigma. Chodzi tu o to, aby tak zminimalizować zmienność procesu, aby mierząca go wartość, odchylając się od wartości przeciętnej o określoną liczbę odchyleń standardowych, wciąż mieściła się w założonych granicach normy. Inną stosowaną w Cleanroom metryką jest metryka średniego czasu między awariami, która służy do oceny jakości produktu.

Rysunek 4.9. Metodologia Cleanroom certyfikacja (ang. certification) – proces potwierdzający, że moduł, system lub osoba spełnia określone wymagania. W stosunku do osób przyznanie certyfikatu może być uzależnione od zdania egzaminu Stosowanie metodologii Cleanroom wymaga od członków zespołu znajomości metod formalnych i statystyki. Jest to często trudne do osiągnięcia. Zwykle decyzja o stosowaniu tej metodologii wiąże się z zaplanowaniem szkoleń dla wszystkich członków zespołu. Koszt ten jest jednak wart rozważenia, gdyż źródła (np. [57]) podają, że średnia gęstość defektów w wykonywanym po raz pierwszy kodzie wynosiła ok. 2,9 defektów/KLOC, co było znacznie lepszym rezultatem niż przeciętna wartość przemysłowa.

4.2. Weryfikacja i walidacja

Pojęcia weryfikacji i walidacji są używane w testowaniu oprogramowania bardzo często, jednak chyba do dziś nie ma pełnej zgody co do ich precyzyjnej definicji oraz rozróżnienia. Poniżej podajemy definicje tych pojęć za normą ISO 9000 [1]. weryfikacja (ang. verification) – egzaminowanie poprawności i dostarczenie obiektywnego dowodu, że produkt procesu wytwarzania oprogramowania spełnia zdefiniowane wymagania walidacja (ang. validation) – sprawdzenie poprawności i dostarczenie obiektywnego dowodu, że produkt procesu wytwarzania oprogramowania spełnia potrzeby i wymagania użytkownika Gdy porównamy te definicje, zobaczymy, że brzmią bardzo podobnie, a różnią się jedynie tym, że walidacja dotyczy spełnienia potrzeb użytkownika, podczas gdy weryfikacja odwołuje się do wymagań niekoniecznie dotyczących wprost użytkownika końcowego. Jest to główne kryterium rozróżnienia tych pojęć. Ostatecznym i nadrzędnym celem procesu produkcji oprogramowania jest wytworzenie i dostarczenie klientowi (użytkownikowi) produktu, który będzie spełniał jego wymagania. Na przykład użytkownicy końcowi systemu ELROJ oczekują, że program ten pomoże im w procesie definiowania i wyświetlania na przystankach rozkładów jazdy autobusów. Sprawdzenie, że gotowy produkt rzeczywiście spełnia te wymagania użytkowe to walidacja. W przeprowadzeniu walidacji można stosować symulacje bądź ewaluacje, np. za pomocą ankiet badających satysfakcję użytkownika z dostarczonego produktu. Aby jednak zbudować takie oprogramowanie, projekt musi przejść przez wiele różnych faz wynikających z przyjętego modelu cyklu życia. Przejście z jednego etapu do drugiego powinno następować dopiero wtedy, kiedy wszystkie czynności fazy poprzedzającej zostaną ukończone i kiedy zespół jest gotowy do przeprowadzenia fazy następującej. Innymi słowy, muszą być spełnione zarówno kryteria wyjścia fazy wcześniejszej, jak i kryteria wejścia fazy kolejnej. Kryteria te zwykle dotyczą czynności technicznych, które w ogóle nie interesują użytkownika końcowego. Na przykład kryterium wyjścia z fazy testów modułowych dla systemu ELROJ może być przetestowanie funkcjonalności metody setInfo zgodnie ze specyfikacją wymagań dla tej metody. Takie sprawdzanie „wewnętrznych” wymagań, przyjętych przez członków zespołu projektowego, to weryfikacja.

Weryfikacja dotyczy więc czynności inżynierskich, „technicznych”. Jest zawsze związana z jakimś fragmentem oprogramowania, nie z gotowym produktem. Walidacja następuje w końcowych fazach i dotyczy gotowego produktu. Weryfikacja bada zgodność z wymaganiami danej fazy, walidacja – zgodność z wymaganiami biznesowymi. Sommerville definiuje weryfikację jako odpowiedź na pytanie „are we building the product right” (czy budujemy produkt dobrze?), a walidację jako odpowiedź na pytanie „are we building the right product?” (czy budujemy dobry produkt?). Techniki testowania można uznać za formę weryfikacji i walidacji. Istnieją różne taksonomie metod walidacji i weryfikacji. Na przykład Balci [58] wyróżnia następujące: nieformalne (m.in.: audyty, inspekcje, kontrola przy biurku, przeglądy, test Turinga); statyczne (m.in.: analiza przepływu danych, analiza semantyczna, analiza strukturalna, analiza składniowa); dynamiczne (m.in.: testowanie czarnoskrzynkowe, testowanie bottomup, debugowanie, śledzenie wykonania, testowanie polowe, testy regresji, techniki statystyczne, testy obciążeniowe, symboliczne, testowanie białoskrzynkowe);

debugowanie

symboliczne (m.in.: grafy przyczynowo-skutkowe, analiza ścieżek, wykonanie symboliczne); formalne (m.in.: indukcja, inferencja, rachunek lambda, dedukcja logiczna, rachunek predykatów, dowodzenie poprawności). W następnych rozdziałach omówimy dokładnie większość z tych technik. Przykładem weryfikacji jest kontrola jakości przez stosowanie tzw. bramek kontroli jakości. bramka kontroli jakości (ang. quality gate) – specjalny kamień milowy w projekcie umieszczany między tymi fazami projektu, które silnie zależą od wyników poprzedzających je faz; bramka kontroli jakości zawiera formalną kontrolę dokumentów z poprzedzającej fazy

Rysunek 4.10. Hierarchia czynności weryfikacji i walidacji wg ISO/IEC/IEEE 29119 W kontekście normy ISO/IEC/IEEE 29119 weryfikacja i walidacja jest z kolei nazywana „analizą weryfikacji i walidacji”, która wraz z dwiema innymi kategoriami: metodami formalnymi oraz testowaniem wchodzi w skład ogółu czynności związanych z zapewnianiem jakości oprogramowania, zwanych „weryfikacją i walidacją” (rys. 4.10).

4.3. Poziomy testów Testowanie zawsze odbywa się w konkretnym modelu cyklu życia, dlatego musi uwzględniać fazy tego cyklu. Poziomy testów odnoszą się właśnie do faz wytwarzania oprogramowania. Opisane typowe poziomy testów są najściślej związane z fazami modeli sekwencyjnych, ale mogą być stosowane w dowolnym modelu wytwórczym. Należy również pamiętać, że wymienione w kolejnych podrozdziałach poziomy nie są jedynymi możliwymi – w jednym projekcie mogą występować dokładnie w takiej formie, w innym może być tylko jeden poziom

testów, w jeszcze innym poziomy mogą wyglądać i nazywać się zupełnie inaczej. Niezależnie od rodzaju i liczby poziomów przyjętych w konkretnym projekcie, istotą rozróżniania poziomów testów jest to, że każdy poziom ma inne cele testowania, ma zwykle inną podstawę testów, a także inny obiekt testowania. Ze względu na odmienną podstawę i cele testowania, na różnych poziomach testów są zwykle wykrywane różne typy defektów. Typowe poziomy to: testy jednostkowe (zwane też komponentów czy unit-testami);

testami

modułowymi,

testami

testy integracyjne; testy systemowe; testy akceptacyjne. Poza tymi czterema klasycznymi poziomami często spotyka się również takie poziomy jak: testy produkcyjne, testy zgodności z umową, testy zgodności legislacyjnej, testy alfa, testy beta. Niektórzy autorzy klasyfikują je jako odmiany testów akceptacyjnych. poziom testów, etap testów (ang. test level, test stage) – grupa czynności testowych, które są razem zorganizowane i zarządzane; poziom testów jest powiązany z poziomami odpowiedzialności w projekcie; przykładami poziomów testów są testy modułowe, integracyjne, systemowe i akceptacyjne [28] faza testów (ang. test phase) – wyróżniony zbiór aktywności testowych zebrany w podlegającą zarządzaniu fazę projektu, np. wykonanie testów na jakimś poziomie testów [59]

4.3.1. Testy jednostkowe

Testy modułowe polegają na wyszukiwaniu usterek w logicznie wydzielonych jednostkach oprogramowania (moduły, klasy, funkcje), które można testować w izolacji od innych jednostek. Jest to typ testów najczęściej wykonywany przez programistów, zwykle z wykorzystaniem narzędzi typu xUnit (np. JUnit). Niektórzy nie uważają tego typu testów za „prawdziwe” testowanie, właśnie ze względu na fakt, iż jest to faza, w której biorą udział zwykle deweloperzy, a nie testerzy. Testy modułowe idealnie nadają się do stosowania podejścia Test Driven Development. testowanie modułowe, testowanie komponentów, testowanie jednostkowe (ang. module testing, component testing, unit testing) – testowanie pojedynczych modułów oprogramowania [7] Testy modułowe wykonuje się w izolacji od innych komponentów, ale często działanie testowanego modułu zależy w jakiś sposób od innych części oprogramowania, które w chwili testów mogą nie być jeszcze gotowe. Są dwa typy takich sytuacji: 1) testowany moduł wywołuje inny komponent (np. funkcję); 2) testowany moduł lub jego część jest wywoływany przez inny komponent. W obu przypadkach zewnętrzne wobec testowanego modułu jednostki mogą jeszcze nie być gotowe i trzeba na czas testowania stworzyć ich „zastępcze” wersje. W pierwszym przypadku zastępczą wersją jest zaślepka, w drugim – sterownik. Niektórzy autorzy rozróżniają dwie2 wersje zaślepek (ang. stub oraz mock). Pierwsza (stub) jest „statycznym” komponentem mającym zawsze ten sam stan, zwracającym zwykle tą samą wartość, a druga (mock) jest bardziej skomplikowaną wersją zaślepki, potrafiącą symulować zachowanie komponentu. Zaślepki i sterowniki składają się na tzw. uprząż testową, zwaną również jarzmem testowym. uprząż testowa, jarzmo testowe (ang. test harness) – środowisko testowe, składające się z zaślepek i sterowników potrzebnych do wykonania testu Podstawą testów są wymagania dla modułów, projekt szczegółowy oraz kod źródłowy. Typowymi obiektami testów modułowych są najmniejsze dające się wydzielić logicznie części oprogramowania, takie jak pojedyncze funkcje, pojedyncze klasy, moduły czy programy. Tester na poziomie testów modułowych ma zwykle dostęp do kodu źródłowego. Proces testowania na tym poziomie cechuje się bardzo małą

formalnością



testowanie

oraz

usuwanie

błędów

(debugowanie)

jest

wykonywane bez stosowania formalnego procesu zarządzania usterkami. Wymóg większej formalizacji poziomu testów jednostkowych może mieć miejsce np. w przypadku systemów krytycznych. Podczas testowania jednostkowego często wykorzystuje się odpowiednie środowisko (tzw. strukturę do testów jednostkowych), w którym moduł może być przetestowany w izolacji od pozostałych części systemu. Środowisko takie umożliwia również automatyczne wykonanie testów i w graficzny sposób pokazuje odsetek zdanych testów oraz stopień pokrycia kodu. struktura do testów jednostkowych (ang. unit test framework) – narzędzie dostarczające środowisko do testów jednostkowych. W takim środowisku moduł może być testowany niezależnie (w izolacji) lub z użyciem odpowiednich zaślepek i sterowników. Dostarcza również innego rodzaju wsparcia dla programistów, np. możliwość debugowania [60]

4.3.2. Testy integracyjne Testy integracyjne sprawdzają poprawność współdziałania (interakcji) oddzielnych komponentów systemu oraz poprawność interfejsów między nimi. Przykładem interakcji może być współpraca między programem a systemem plików, ale może ona dotyczyć również komunikacji między dwiema klasami lub nawet dwiema metodami tej samej klasy. Rozważmy system ELROJ z Dodatku A. Występuje w nim między innymi metoda addBusInterval, która do poprawnego działania wymaga wywołań metod addLine oraz addBus. Test poprawności współdziałania tych metod jest przykładem testu integracji. testowanie integracyjne (ang. integration testing) – testowanie wykonywane w celu wykrycia defektów podczas interakcji między komponentami lub systemami W zależności od typu obiektów, których współdziałanie chcemy testować, możemy mówić o testach małej lub dużej skali integracji. Integracja małej skali, jak sama nazwa wskazuje, dotyczy łączenia ze sobą „małych” elementów oprogramowania, takich jak metody czy klasy. Integracja dużej skali polega na łączeniu ze sobą większych elementów, takich jak całe programy lub systemy

w tzw. system systemów. Czasami używa się również rozróżnienia na testy integracji modułów oraz testy integracji systemów. system systemów (ang. system of systems) – mnogie, heterogeniczne rozproszone systemy, które mogą być zagnieżdżone w sieciach na wielu poziomach

i

w

wielu

połączonych

domenach,

ukierunkowane

na

rozwiązywanie interdyscyplinarnych, wspólnych problemów wielkiej skali i służeniu takim celom integracja (ang. integration) – proces łączenia komponentów w większe zespoły integracja podstawowych funkcjonalności systemu (ang. functional integration) – metoda integracji, w której moduły lub systemy łączy się jak najwcześniej w celu uzyskania działającej podstawowej funkcjonalności testowanie integracji modułów, testowanie integracyjne małej skali (ang. component integration testing, integration testing in the small) – testowanie wykonywane w celu wykrycia usterek w interfejsach i interakcjach między integrowanymi komponentami oprogramowania testowanie integracji systemów, testowanie integracyjne zewnętrzne (dużej skali) (ang. system integration testing, integration testing in the large) – testowanie integracji systemów i pakietów oraz interfejsów z obiektami zewnętrznymi wobec systemu (np. zewnętrzne bazy danych, systemy plików, internet, elektroniczna wymiana danych) W testach integracyjnych wyróżnia się również testowanie interfejsów oraz testowanie integracji sprzęt–oprogramowanie. Specjalnym typem testowania interfejsów jest testowanie interfejsu programowania aplikacji API (ang. Application Programming Interface), które bardzo silnie jest oparte na tzw. testowaniu negatywnym w celu sprawdzenia odporności na błędy. Przykładem testowania integracji sprzęt–oprogramowanie w przypadku systemu ELROJ byłoby przetestowanie komunikacji między oprogramowaniem a fizycznym ekranem zainstalowanym na przystanku i wyświetlającym czasy przyjazdu autobusów.

testowanie interfejsu (ang. interface testing) – testowanie wykonywane w celu wykrycia usterek w interfejsach między modułami testowanie interfejsu programowania aplikacji API (ang. API testing) – testowanie kodu, który umożliwia komunikację między różnymi procesami, programami i/lub systemami; testowanie interfejsu programowania aplikacji często zawiera testowanie negatywne, np. aby sprawdzić odporność na syntaktycznie niepoprawne wejścia oraz poprawność obsługi błędów testowanie

integracji

sprzęt–oprogramowanie

(ang.

hardware-software

integration testing) – testowanie wykonywane, by odnaleźć w interfejsach i współdziałaniu między sprzętem i oprogramowaniem

defekty

Podstawą dla testów integracji jest przede wszystkim projekt architektury systemu, a także opis przepływu procesów. W przypadku dużej integracji podstawą testów mogą być także przypadki użycia. Typowymi obiektami testów są interfejsy zapewniające komunikację między modułami. Ważnym obiektem testów, o którym często się zapomina, są dane konfiguracyjne systemu. Konfiguracja różnych ustawień systemowych może mieć wpływ na działanie wszystkich modułów wchodzących w skład aplikacji. Na przykład w systemie ELROJ daną konfiguracyjną może być liczba linii ekranu. Od tej wartości zależy działanie metody przygotowującej dane do wyświetlenia. Innym przykładem danej konfiguracyjnej w systemie ELROJ może być maksymalna długość łańcucha znaków przechowującego komunikat. Liczba ta wpływa na sposób obsługi metod getInfo, setInfo oraz getDisplayString. element konfiguracji (ang. configuration item) – zbiór zawierający sprzęt, oprogramowanie lub te dwie rzeczy, które poddawane są zarządzaniu konfiguracją i traktowane jako pojedyncza składowa w procesie zarządzania konfiguracją [7]) zarządzanie konfiguracją (ang. configuration management) – dyscyplina stosująca techniczne i administracyjne metody kierowania i nadzoru w celu: określenia i udokumentowania charakterystyk funkcjonalnych i fizycznych konfiguracji, kontroli zmiany tych charakterystyk, zapisywania

i

raportowania

wykonywanych zmian i

statusów implementacji

oraz

weryfikacji zgodności z wyspecyfikowanymi wymaganiami [7] informacja o statusie (ang. status accounting)



element

zarządzania

konfiguracją składający się z rejestrowania i raportowania informacji potrzebnych do efektywnego zarządzania konfiguracją; informacje te zawierają zestawienie

zaakceptowanych

zaproponowanych zmian zaakceptowanych zmian [7]

elementów

konfiguracji

oraz

konfiguracji, status

status

implementacji

kontrola konfiguracji, kontrola wersji, kontrola zmiany (ang. configuration control, version control, change control) – element zarządzania konfiguracją składający się z oceny, koordynacji oraz udzielenia lub nieudzielenia zgody na zmianę elementów konfiguracji oraz implementacji zmian po formalnej identyfikacji elementu konfiguracji Podczas testowania integracyjnego testerzy powinni skupiać się wyłącznie na aspekcie współdziałania testowanych komponentów, np. na testowaniu interfejsu. Na tym etapie testowania nie interesuje nas poprawność funkcjonalności każdego z modułów z osobna. W przypadku większych projektów, gdy liczba elementów poddawanych testom integracji jest duża, pojawia się następujący problem: w jakiej kolejności powinny być tworzone komponenty, aby jak najefektywniej przeprowadzić testy modułowe oraz integracyjne? Efektywność ta może dotyczyć np. jak najwcześniejszego minimalizowania jak największej liczby ryzyk, ograniczania liczby zaślepek i sterowników koniecznych do przeprowadzenia testów (to zabiera zwykle dużo czasu), chęci wykonania jak najwcześniej testów w celu integracji modułów krytycznych itd. Kolejność tworzenia modułów nie zawsze jest podyktowana wolą testera – zwykle wynika ona z planu projektu. Niemniej jednak testerzy powinni mieć wpływ na podejmowanie decyzji w tym względzie. Bardzo często spotyka się sytuacje, w których etap testowania integracyjnego sprowadza się do tzw. metody wielkiego wybuchu (ang. big bang). Polega ona na testowaniu całkowicie zintegrowanego systemu, który nie był wcześniej testowany na poziomie testów integracyjnych w sposób inkrementacyjny ze względu na brak czasu, zasobów itd. Wadą tej metody jest to, że zwykle podczas jej stosowania testerzy napotykają wiele błędów, które trudno zlokalizować

w postaci usterki, gdyż nie wiadomo który interfejs bądź która interakcja jest za nią odpowiedzialna. Dlatego – z punktu widzenia testowania – rozsądnym podejściem do integracji jest podejście inkrementacyjne. Istnieją dwie podstawowe techniki integracji oparte na architekturze systemu: integracja zstępująca i wstępująca. Systematyczne strategie integracji mogą również być oparte na funkcjonalności (integrowanie w kolejności wyznaczonej funkcjonalnością modułów) czy też na ryzyku. Strategie te omówimy na przykładzie programu przedstawionego na rysunku 4.11. Program ten składa się z dziewięciu modułów, a wywołania (interakcje) między nimi są oznaczone kreskami. Na przykład moduł A wywołuje moduły B, F oraz G, moduł I jest wywoływany zarówno przez moduł D, jak i przez moduł H itd. Strzałka wchodząca do modułu E oznacza, że moduł ten pobiera dane z zewnętrznego źródła danych. Strzałka wychodząca z modułu H symbolizuje wypisywanie przez ten moduł danych na wyjście (w przypadku systemu ELROJ moduł E mógłby zawierać metodę getInfo, a moduł H – metodę getDisplayString). Szare tło modułów D, G oraz I oznacza, że moduły te są krytyczne dla działania systemu i jako takie przypisane mają większą część zidentyfikowanych ryzyk jakościowych (produktowych).

Rysunek 4.11. Projekt architektury przykładowego programu W przypadku integracji metodą zstępującą (ang. top-down) najpierw tworzony jest moduł A, następnie moduły B, F i G, w dalszej kolejności moduły C, D oraz H, a na samym końcu E oraz I. W przypadku integracji metodą wstępującą (ang. bottom-up) najpierw tworzy się moduły położone na najniższych poziomach diagramu zależności, czyli E oraz I. W następnej kolejności tworzy się moduły C, D oraz H, potem B, F, G, a na końcu moduł główny A. testowanie zstępujące (ang. top-down testing) – podejście przyrostowe do testowania integracyjnego, w którym jako pierwszy jest testowany moduł na górze hierarchii, a moduły niższych rzędów są symulowane przez zaślepki; przetestowane moduły są później używane do testowania modułów niższych rzędów; proces ten jest powtarzany, aż zostaną przetestowane moduły leżące najniżej w hierarchii testowanie wstępujące (ang. bottom-up testing) – podejście przyrostowe do testowania integracyjnego polegające na testowaniu modułów najniższego poziomu jako pierwszych, co ułatwia testowanie modułów wyższych poziomów; proces ten jest powtarzany dopóty, dopóki moduł na szczycie hierarchii nie zostanie przetestowany Strategia integracji funkcjonalnej wyznacza kolejność tworzenia modułów tak, aby jak najwcześniej przetestować wybrane obszary funkcjonalne. Załóżmy, że chcemy przetestować funkcjonalność pobierania i wypisywania danych na ekran. Moduł wypisujący H korzysta z modułu pobierającego E, dlatego w pierwszej kolejności należy stworzyć moduły umożliwiające przepływ danych z E do H. Jest to np. ścieżka E, D, B, A, G, H. Moduł G może być wymieniony na moduł F, ale oba wywołują H, a G jest modułem krytycznym, dlatego w pierwszym przypadku oprócz uzyskania ścieżki zapewniającej żądaną funkcjonalność otrzymujemy „gratis” możliwość wczesnego przetestowania modułu o znaczeniu krytycznym. Niektóre z tych modułów mogą być zaślepkami lub sterownikami (np. zamiast tworzyć duży moduł główny A, można stworzyć jego sterownik, który będzie symulował wywoływanie modułów B i G, potrzebnych do zrealizowania naszej ścieżki).

Strategia integracji opartej na ryzyku zakłada, że najwcześniej są tworzone moduły krytyczne, a w testach integracji najwcześniej testuje się integrację modułów krytycznych. W przypadku naszego programu mamy trzy moduły krytyczne: D, G, I, które powinny być stworzone najwcześniej. Po stworzeniu D oraz I od razu możemy przeprowadzić test integracji D–I. W dalszej kolejności można tworzyć moduły wchodzące w interakcję z modułami krytycznymi, a więc moduły B, E (dla interakcji z D), H (dla interakcji z G oraz I) i A dla interakcji z G. Źródłem ryzyka może być też fakt, że część modułów (w integracji małej) lub systemów (w integracji dużej) jest tworzona przez stronę trzecią, niezależnie od organizacji rozwijającej system. Na przykład moduł G może być dostarczany przez firmę zewnętrzną. W takim przypadku można uznać, że testy integracji A–G oraz G–H powinny być wykonane szczególnie starannie. Zasadniczo, po każdym stworzeniu komunikujących się ze sobą modułów X i Y można przeprowadzić testy integracji X–Y. Kolejność tworzenia modułów ma również wpływ na efektywność testów modułowych. Jeśli np. moduły są tworzone sekwencyjnie, a testowanie modułowe chcemy rozpocząć jak najwcześniej, to w przypadku strategii zstępującej po dostarczeniu modułu A programista musi stworzyć zaślepki dla B, F i G, aby w pełni przetestować działanie A. Musi również stworzyć zaślepki: C i D do przetestowania B, H do przetestowania F i G, E do przetestowania D oraz I do przetestowania D i H. W tym przypadku programista musi w sumie stworzyć 8 zaślepek. Oczywiście chcąc przetestować integrację modułu A z B, F i G, możemy po prostu poczekać, aż te trzy ostatnie moduły zostaną stworzone. W takim przypadku proces testowania rozpoczyna się później (następuje przesunięcie go w czasie).

4.3.3. Testy systemowe Z testami systemowymi mamy do czynienia, gdy poszczególne elementy systemu zostały z sobą zintegrowane w spójny, działający system. Jest to poziom, na którym można sprawdzać wysokopoziomową funkcjonalność systemu oraz przeprowadzać pełne scenariusze testowe z punktu widzenia użytkownika (nazywane czasem end-to-end testing), od momentu zalogowania się do systemu po jego opuszczenie. Na tym etapie najpełniej wykorzystuje się czarnoskrzynkowe metody testowania, a także najczęściej przeprowadza się testy niefunkcjonalne, na przykład testy wydajności, niezawodności czy bezpieczeństwa. Testowanie systemowe uważa się za jeden z najtrudniejszych do przeprowadzenia poziomów

testów [61]. Programiści rzadko w nim uczestniczą – testy systemowe są zwykle wykonywane przez niezależny zespół testerski. testowanie systemowe (ang. system zintegrowanego systemu w celu z wyspecyfikowanymi wymaganiami [11]

testing) – proces sprawdzenia jego

testowania zgodności

Zadaniem testowania systemowego jest porównanie stworzonego programu z jego pierwotnymi celami projektowymi. Podstawę testów stanowią więc wymagania systemowe, przypadki użycia, specyfikacja funkcjonalna, a także specyfikacja procesów biznesowych realizowanych przez system. Obiektami testów są: sam system, podręczniki systemowe, dokumentacja użytkowa, a także konfiguracja systemu. Testy systemowe dotyczą gotowego systemu, który działa w konkretnym środowisku. Dlatego bardzo ważne jest, aby środowisko testowe przypominało w jak największym stopniu docelowe środowisko produkcyjne. Czasami kwestia środowiska nie ma żadnego lub prawie żadnego znaczenia, czasami wymaga stosowania technik kombinacyjnych (zwłaszcza w przypadku produktów z półki, dla których nieznane jest docelowe środowisko), a czasem przygotowanie środowiska testowego jest zajęciem niezwykle skomplikowanym (np. dla systemów rozproszonych lub oprogramowania telekomunikacyjnego, które jest bardzo silnie związane z infrastrukturą telekomunikacyjną). Na przykład środowisko testowe dla systemu ELROJ powinno zawierać w szczególności egzemplarz modelu wyświetlacza, jaki będzie montowany na przystankach autobusowych. środowisko

produkcyjne

(ang.

operational

environment)



sprzęt

i oprogramowanie zainstalowane w siedzibie użytkownika lub klienta, w którym moduł lub system będzie używany; w skład oprogramowania mogą wchodzić systemy operacyjne, bazy danych i inne aplikacje

4.3.4. Testy akceptacyjne Testy akceptacyjne są zwykle wykonywane przez klienta lub użytkowników końcowych na gotowym, w pełni działającym produkcie, choć można również przeprowadzać testy akceptacyjne dla oprogramowania z niepełną

funkcjonalnością (np. test akceptacyjny jednego systemu wchodzącego w skład systemu systemów). Oprogramowanie z półki może być poddane testom akceptacyjnym w momencie instalacji lub integracji z innymi systemami; testy akceptacyjne mogą dotyczyć nawet pojedynczych modułów, itd. testowanie

akceptacyjne

(przez użytkownika), akceptacja

(ang.

(user)

acceptance testing, acceptance) – testowanie formalne przeprowadzane w celu umożliwienia użytkownikowi, klientowi lub innemu uprawnionemu podmiotowi ustalenia, czy zaakceptować system lub moduł [7] testowanie akceptacyjne w środowisku użytkownika (ang. site acceptance testing) – testowanie akceptacyjne wykonywane przez użytkowników/klientów w ich środowisku pracy w celu określenia, czy moduł lub system spełnia ich potrzeby oraz czy realizuje procesy biznesowe; standardowo zawierają zarówno testy sprzętu, jak i oprogramowania kryteria akceptacji (ang. acceptance criteria) – kryteria wyjścia, które moduł lub system musi spełniać, aby został zaakceptowany przez klienta, użytkownika lub inny uprawniony podmiot [7] Głównym celem testowania akceptacyjnego Większość z nich powinna zostać znaleziona wykonywanych poziomach testów. Testowanie przede wszystkim do nabrania zaufania do niefunkcjonalnych. Testowanie akceptacyjne

nie jest znajdowanie usterek. na innych, zwykle wcześniej akceptacyjne powinno służyć systemu lub jego atrybutów jest formą walidacji, czyli

potwierdzenia, że produkt spełnia oczekiwania klienta. Często testy akceptacyjne przybierają formę testów niefunkcjonalnych (np. testowanie użyteczności – patrz podrozdz. 15.3). Wyniki testów akceptacyjnych mogą mieć formę ankiet/kwestionariuszy dotyczących zadowolenia klienta z systemu. Ocenie podlegać mogą zarówno aspekty funkcjonalne systemu (co system robi), jak i niefunkcjonalne (jak system pracuje). Nie można oczywiście wykluczyć, że podczas przeprowadzania testów akceptacyjnych, zwłaszcza w siedzibie klienta, w docelowym środowisku produkcyjnym, wykryte zostaną jakieś błędy. Ważne jest, aby zapewnić sobie możliwie najlepszą jakość informacji zwrotnej od klienta dotyczącej zgłaszanej

usterki. Użytkownik nie jest testerem i w szczególności nie zna się na procedurach śledzenia i zgłaszania defektów. Można zastosować różne podejścia do tego problemu, na przykład: przed

przeprowadzeniem

testów

użytkownik

powinien

być

poinformowany w jaki sposób rejestrować bądź zgłaszać błędy; użytkownik powinien przeprowadzać testy w obecności testera lub programisty; działania użytkownika podczas testów powinny być nagrywane, np. za pomocą kamery lub programu rejestrującego akcje użytkownika (np. tzw. keylogger). Dzięki temu zespół testerów i programistów łatwiej zreprodukuje błąd i zlokalizuje miejsce odpowiedzialnej za niego usterki. Należy pamiętać, że koszty usuwania usterek w późniejszych fazach cyklu życia są o wiele wyższe niż koszty usuwania usterek w fazach wcześniejszych. Dlatego trzeba łagodzić ryzyko trudności naprawy błędu znalezionego przez użytkownika, np. przez stosowanie opisanych metod.

4.3.5. Pozostałe poziomy testów Wśród poziomów testów wyróżnia się również testy: produkcyjne, zgodności z umową, zgodności legislacyjnej, alfa oraz beta. Testy produkcyjne lub akceptacyjne testy produkcyjne mają za zadanie akceptację systemu przez administratorów. Typowe czynności podczas tego typu testów to sprawdzanie: poprawności tworzenia i odtwarzania kopii zapasowych systemu (w tym automatycznego tworzenia kopii zapasowych); odtwarzania systemu po awarii; funkcjonalności związanej z zarządzaniem użytkownikami. Pod pojęciem testów produkcyjnych rozumie się także testowanie mające za zadanie ocenić system w jego środowisku produkcyjnym. Testy zgodności z umową to testy sprawdzające, czy wszystkie zawarte w kontrakcie warunki akceptacji zostały spełnione. Testy te występują głównie w przypadku oprogramowania tworzonego na zamówienie, gdzie zawsze mamy

do czynienia z podpisaniem umowy określającej wymagania na system. Może je przeprowadzać

zespół

produkcyjny

na

etapie

testów

systemowych

lub

użytkownik końcowy na etapie testów akceptacyjnych. Raport z testów zgodności z umową powinien być podpisany i zaakceptowany przez obie strony umowy. Stanowi on bowiem formalne potwierdzenie wykonania warunków umowy. Testy zgodności legislacyjnej dotyczą oprogramowania realizującego zadania w obszarze, w którym istnieją regulacje prawne. Na przykład program zarządzający procesem fakturowania w firmie musi spełniać odpowiednie wymagania ustawy o rachunkowości; programy dla instytucji finansowych muszą spełniać wymagania ustawy o bankowości itd. Najczęściej testy te przeprowadza się na etapie testów systemowych lub akceptacyjnych, ale są one najczęściej wykonywane przez zespół testerów lub audytora. Testy alfa i beta (testy polowe) to testy akceptacyjne przeprowadzane przez klienta lub użytkownika końcowego. Testy alfa przeprowadza się u producenta, w środowisku testowym. Oczywiście środowisko to powinno być jak najbardziej zbliżone do docelowego. Testy beta (polowe) przeprowadzane są przez klientów w ich własnych lokalizacjach. Testy alfa i beta są popularne w przypadku produkcji oprogramowania z półki, tworzonego na szeroki rynek. Celem tych testów może być: uzyskanie zaufania docelowego testowanego programu; obliczenie profilów oprogramowania;

operacyjnych,

lub

potencjalnego

czyli

sposobów

klienta

dla

użytkowania

uzyskanie oceny klienta dotyczącej użyteczności bądź innych cech niefunkcjonalnych testowanego programu; zebranie od klientów-testerów uwag dotyczących testowanego oprogramowania, które pomogą zwiększyć jakość produktu. Wyniki testów beta można wykorzystać w pomiarze jakości procesu wytwórczego (np. w mierzeniu efektywności usuwania defektów – patrz rozdz. 45), gdyż błędy (defekty) znalezione przez użytkownika można traktować jak błędy (defekty) polowe (zwane również operacyjnymi), wykryte po wydaniu oprogramowania.

testowanie produkcyjne (ang. operational testing) – 1) testowanie mające na celu ocenę modułu lub systemu w jego środowisku produkcyjnym [7]; 2) forma testowania akceptacyjnego przez administratorów systemu produkcyjne testy akceptacyjne, akceptacyjne testowanie produkcyjne (ang. Operational Acceptance Testing, Production Acceptance Testing, OAT) – testowanie produkcyjne w fazie testów akceptacyjnych, zwykle przeprowadzane w środowisku produkcyjnym będącym symulacją rzeczywistego środowiska docelowego; wykonywane zazwyczaj przez operatora lub administratora, zorientowane na takie charakterystyki jakościowe jak: odtwarzalność, zarządzanie zasobami, łatwość instalacji i zgodność techniczna; patrz także: testowanie produkcyjne testowanie alfa (ang. alpha testing) – symulowane lub rzeczywiste testy produkcyjne przeprowadzane przez potencjalnych użytkowników lub niezależny zespół testowy, przeprowadzane u producenta, ale bez udziału wytwórców oprogramowania; testowanie alfa jest często wykorzystywane jako forma wewnętrznych testów akceptacyjnych dla oprogramowania z półki testowanie beta, testowanie polowe (ang. beta testing, field testing) – testowanie produkcyjne wykonywane przez potencjalnego i/lub istniejącego użytkownika/klienta w zewnętrznym miejscu niezwiązanym z programistami/twórcami – poza organizacją wytwórczą, w celu podjęcia decyzji, czy moduł albo system zaspokaja potrzeby użytkownika/klienta i współgra z procesami biznesowymi; testowanie beta jest często traktowane jako forma zewnętrznych testów akceptacyjnych oprogramowania z półki w celu uzyskania informacji zwrotnej z rynku akceptacyjne testy przemysłowe (ang. factory acceptance testing) – testy akceptacyjne wykonywane w środowisku, w którym produkt jest wytwarzany, wykonywane przez pracowników dostawcy, w celu sprawdzenia czy moduł lub system spełnia wymagania, zwykle włączając w to zarówno sprzęt, jak i oprogramowanie; patrz również: testowanie alfa

zgodność (ang. compliance) – zdolność oprogramowania standardom, konwencjom rozporządzeniom [3]

lub

regulacjom

prawnym

do podlegania i

podobnym

testowanie zgodności (ang. compliance testing) – proces testowania określający zgodność modułu lub systemu (np. z umową lub z obowiązującymi przepisami prawa)

4.4. Typy testów Poziomy testów wyznaczają podział testów ze względu na ich umiejscowienie w procesie wytwórczym. Testy można jednak sklasyfikować również ze względu na ich cel. Taki podział wyznacza typy testów. Cztery główne typy to: testy funkcjonalne; testy niefunkcjonalne; testy strukturalne; testy związane ze zmianą. typ testów (ang. test type) – grupa czynności testowych nakierowanych na testowanie modułu, komponentu lub systemu, skoncentrowanych na szczegółowych, konkretnych celach, takich jak testy funkcjonalności, testy użyteczności, testy regresyjne itp.; typ testów może być użyty na jednym lub na wielu poziomach testów [28]

4.4.1. Testy funkcjonalne Testy funkcjonalne sprawdzają funkcje systemu, przy czym słowo „funkcje” rozumiemy tu jako funkcjonalności, czyli czynności, do których wykonywania program jest przeznaczony (w odróżnieniu od funkcji jako elementów kodu źródłowego w paradygmacie programowania strukturalnego). Testy te sprawdzają więc, „co” program robi, weryfikują spełnienie wymagań funkcjonalnych. Cel testowania funkcjonalnego jest zwykle jasno określony, gdyż wynika wprost lub pośrednio ze specyfikacji wymagań, przypadków użycia czy specyfikacji funkcjonalnej. Ten typ testowania jest domeną analityka testów.

testowanie funkcjonalne (ang. functional testing) – testowanie oparte na analizie specyfikacji funkcjonalnej modułu lub systemu wymaganie

funkcjonalne

(ang.

functional

requirement)



wymaganie

specyfikujące funkcję, którą moduł lub system musi realizować [7] Przy tworzeniu testów funkcjonalnych ma zastosowanie wiele technik czarnoskrzynkowych, gdyż są to techniki oparte na specyfikacji. Techniki czarnoskrzynkowe zostały opisane w rozdziale 8. Testowanie funkcjonalne obejmuje swoim zakresem także niektóre spośród atrybutów jakościowych, takie jak dokładność, odpowiedniość, współdziałanie, bezpieczeństwo funkcjonalne czy użyteczność. Omówimy je dokładnie w rozdziale 15.

4.4.2. Testy niefunkcjonalne Testowanie niefunkcjonalne sprawdza, „jak” system działa. Nie skupiamy się tu na aspektach funkcjonalnych, ale na „parametrach technicznych” oprogramowania opisywanych wymaganiami niefunkcjonalnymi. Typowe atrybuty techniczne podlegające testowaniu niefunkcjonalnemu to: niezawodność, efektywność, bezpieczeństwo techniczne, pielęgnowalność czy przenaszalność. Metody testowania – choć głównie czarnoskrzynkowe – są zupełnie odmienne niż w przypadku testowania funkcjonalnego. Często wymagają specjalnie skonfigurowanego środowiska lub dedykowanego oprogramowania pozwalającego na symulację różnego rodzaju obciążeń systemu. Metody te są opisane dokładniej w rozdziale 16. Testowanie niefunkcjonalne jest domeną technicznego analityka testów. testowanie niefunkcjonalne (ang. non-functional testing) – testowanie atrybutów modułu lub systemu, które nie odnoszą się do jego funkcjonalności, np. niezawodność, efektywność, pielęgnowalność czy przenaszalność testowanie czarnoskrzynkowe (ang. black box testing) – testowanie funkcjonalne lub niefunkcjonalne, bez odniesienia do wewnętrznej struktury modułu lub systemu

wymaganie niefunkcjonalne (ang. non-functional requirement) – wymaganie, które nie dotyczy funkcjonalności, ale cech oprogramowania takich jak niezawodność, efektywność, użyteczność, pielęgnowalność czy przenaszalność Z

testowaniem

niefunkcjonalnym

wiąże

się

pewna

trudność.

Zwykle

dokumenty wymagań skupiają się na wymaganiach funkcjonalnych, a te niefunkcjonalne specyfikowane są bardzo rzadko. Często zakłada się domyślne wymagania niefunkcjonalne (np. odpowiedni poziom bezpieczeństwa lub szybkość działania programu). Tester musi zwracać na to uwagę. Dobre testowanie nigdy nie ogranicza się do testowania funkcjonalności – jeśli wymagania niefunkcjonalne nie są podane wprost w specyfikacji, to należy je uzyskać od klienta lub po prostu założyć domyślnie. Katalog atrybutów jakościowych dla testów niefunkcjonalnych można znaleźć np. w normie ISO 9126 [3] lub w zastępującej ją normie ISO/IEC 25010 [2]. Używając tego typu standardów, można projektować testy tak, aby dało się je odnieść do poszczególnych elementów modelu jakości oprogramowania, dzięki czemu testy niefunkcjonalne mogą pokrywać wymagania jakościowe. technika projektowania testów niefunkcjonalnych (ang. non-functional test design technique) – procedura otrzymywania i/lub wyboru przypadków testowych dla testów niefunkcjonalnych oparta na analizie specyfikacji modułu lub systemu bez odniesienia do jego wewnętrznej struktury

4.4.3. Testy strukturalne Testowanie strukturalne, zwane też testowaniem

architektury, polega

na

upewnieniu się, że wszystkie strukturalne elementy oprogramowania zostały przetestowane (to znaczy, zostały pokryte przez testy). Aby zweryfikować stopień pokrycia, musimy mieć wgląd w wewnętrzną strukturę programu. Dlatego testy strukturalne stosują zawsze techniki białoskrzynkowe. testowanie strukturalne, testowanie białoskrzynkowe, testowanie szklanoskrzynkowe, testowanie na podstawie kodu (ang. structural testing, white box testing, glass box testing, code-based testing) – testowanie oparte na analizie wewnętrznej struktury modułu lub systemu

Strukturalnym elementem oprogramowania mogą być: linie kodu, rozgałęzienia w instrukcjach warunkowych, warunki logiczne w predykatach, wywoływane funkcje itp. Techniki białoskrzynkowe są dokładnie omówione w rozdziale 9. Badanie stopnia pokrycia elementów strukturalnych wymaga zwykle użycia narzędzi, które wykonują to zadanie automatycznie. Testowanie strukturalne może być również przeprowadzone na podstawie analizy architektury systemu (np. hierarchii wywołań metod, funkcji lub innych obiektów). Dobrą praktyką jest rozpoczęcie testowania przy użyciu technik opartych na specyfikacji, a następnie użycie technik opartych na strukturze, aby przetestować elementy strukturalne testowanego programu, nie pokryte podczas testów czarnoskrzynkowych.

4.4.4. Testy związane ze zmianami Testy związane ze zmianami dotyczą powtórnego wykonania testu lub zestawu testów w związku ze zmianą w produkcie lub środowisku. Przykładowe zmiany to: poprawiony kod w związku z wykryciem i naprawieniem defektu; wydanie nowej wersji oprogramowania; opublikowanie „łatki” na oprogramowanie; zmiana wersji systemu operacyjnego, bazy danych lub oprogramowania współpracującego z tworzonym produktem.

innego

W zależności od typu zmiany mówimy o testowaniu potwierdzającym (tzw. retestowaniu) lub o testowaniu regresywnym. Jeśli jakiś test wykrył usterkę i została ona naprawiona, to należy ponownie wykonać ten sam test, aby upewnić się, że naprawa dokonana została prawidłowo. Nierzadko okazuje się, że naprawa nie tylko nie usunęła defektu, lecz także w jej wyniku pojawiło się jeszcze więcej błędów. W przypadku wprowadzania planowanych zmian w oprogramowaniu, niezwiązanych z żadnymi usterkami czy błędami, należy nową wersję przetestować przy użyciu tych samym przypadków testowych, za pomocą których testowaliśmy wersję wcześniejszą. Takie testowanie nazywamy testowaniem regresywnym, a same testy – testami regresji. Testowanie

regresywne ma na celu sprawdzenie, czy wprowadzone do oprogramowania zmiany nie spowodowały powstania jakichś usterek. Zasadnicza różnica między retestami a testami regresji jest taka, że wykonanie retestów związane jest z konkretną, znaną nam usterką, a wykonanie testów regresji nie dotyczy istniejących błędów, lecz jest ukierunkowane na wykrywanie nowych, nieznanych nam dotąd usterek. Testy regresji są pierwszymi i naturalnymi kandydatami do zautomatyzowania. Suita testów regresji często osiąga bardzo duże rozmiary, a w przypadku tzw. codziennych wydań (daily buids) należy je wykonywać bardzo często. Ręczne przeprowadzanie tego procesu często jest bardzo trudne lub wręcz niemożliwe. Organizacje zwykle przeprowadzają testy regresji w nocy, poza godzinami pracy, na sprzęcie deweloperów lub na specjalnie dedykowanych do tego celu serwerach testowych. testowanie potwierdzające, retestowanie (ang. confirmation testing, re-testing) – testowanie polegające na uruchomieniu przypadków testowych, które podczas ostatniego uruchomienia wykryły błędy, w celu sprawdzenia poprawności naprawy testowanie regresywne (ang. regression testing) – ponowne przetestowanie uprzednio testowanego programu po dokonaniu w nim modyfikacji, w celu upewnienia się, że w wyniku zmian nie powstały nowe defekty lub nie ujawniły się defekty w niezmienionej części

oprogramowania; takie testy



przeprowadzane po

zmianach

oprogramowania lub jego środowiska W przypadku stosowania techniki ciągłej integracji czy tzw. codziennego budowania wersji (np. w metodykach zwinnych) spotkać można również stosowanie tzw. testów dymnych. Test dymny to niewielki podzbiór testów sprawdzających główne funkcjonalności systemu. Wykonuje się go przed „właściwym” testowaniem kolejnej wersji systemu, aby sprawdzić, czy system ten jest stabilny i czy nie wystąpią jakieś poważne awarie uniemożliwiające bądź opóźniające czynności testowe.

codzienne budowanie wersji (ang. daily build) – aktywność programistyczna, której celem jest kompletna kompilacja i integracja systemu każdej doby (zwykle nocą) tak, aby zintegrowany system wraz z ostatnimi zmianami był dostępny w dowolnym czasie test dymny, test kondycji, test potwierdzający3 (ang. smoke test, sanity test, confidence test) – podzbiór wszystkich zdefiniowanych/zaplanowanych przypadków testowych, które pokrywają główne funkcjonalności modułu lub systemu, mający na celu potwierdzenie, że kluczowe funkcjonalności programu działają, bez zagłębiania się w szczegóły; codzienne wydania (ang. daily builds) i testy dymne stanowią dobre praktyki wytwarzania oprogramowania; patrz także: test wstępny test wstępny, pretest (ang. intake test, pretest) – szczególny rodzaj testu dymnego mający na celu podjęcie decyzji, czy moduł lub system jest gotowy do dalszego szczegółowego testowania; najczęściej przeprowadzany na początku fazy wykonywania testów

4.5. Poziomy a typy testów Poziomy i typy testów są dwoma zupełnie niezależnymi od siebie podziałami testów. Oznacza to, że w praktyce każdy typ testów może być wykonywany na każdym poziomie testowania. Na przykład: testy modułowe oraz systemowe mogą dotyczyć zarówno funkcjonalnych, jak i niefunkcjonalnych cech systemu; testy integracji mogą być wykonywane na więcej niż jednym poziomie i dotyczyć zarówno modułów, jak i systemów; testowanie funkcjonalne może być przeprowadzane zarówno podczas testów modułowych, jak i akceptacyjnych, itd. Oczywiście, w praktyce pewne połączenia poziomów z typami występują częściej niż inne, np. testy niefunkcjonalne zwykle wykonuje się podczas testów systemowych, ale należy pamiętać, że tego typu połączenia nie są regułą i każdy kierownik testów powinien swobodnie operować poziomami i typami, zgodnie z planem testów, tak, aby proces testowy przebiegał jak najefektywniej.

1 Więcej informacji o wyrażeniach regularnych Czytelnik znajdzie w Dodatku C. 2 Część autorów, np. [50] wyróżnia jeszcze więcej rodzajów komponentów zastępczych: stub, spy, mock, fake, dummy. 3 Nazwa „test potwierdzający” występująca w słowniku ISTQB [6] nie jest zbyt szczęśliwa, ponieważ kojarzyć się może z testowaniem potwierdzającym, które oznacza coś zupełnie odmiennego. Lepiej używać pojęcia „test dymny”.

Część II Techniki projektowania testów

Be as simple as possible, but not simpler Albert Einstein

Część

ta

jest

poświęcona

technikom

projektowania

testów.

Zdolność

do

projektowania i tworzenia efektywnych przypadków testowych jest kluczową umiejętnością dobrego testera. W rozdziałach 8 i 9 poznamy bardzo wiele modeli działania oprogramowania, będących podstawą różnych technik tworzenia testów. Zanim jednak je omówimy, w rozdziale 5 zdefiniujemy dwa podstawowe modele: przepływu sterowania oraz przepływu danych. Opiszemy je wcześniej, ponieważ będą nam potrzebne również w rozdziale 6, dotyczącym technik testowania statycznego. Ponadto, większość technik w rozdziale 9 jest bezpośrednio opartych na kodzie, który modeluje się właśnie grafem przepływu sterowania lub przepływu danych. W rozdziale 7 opiszemy przykłady technik analizy dynamicznej, której podstawową cechą jest analiza programu w trakcie jego działania. Rozdziały 8 i 9 zawierają obszerny i szczegółowy przegląd technik testowania opartych na specyfikacji oraz na strukturze, czyli tzw. testowanie biało- i czarnoskrzynkowe. Każda technika jest zilustrowana przykładem opisującym proces tworzenia testów zgodny z normą ISO/IEC/IEEE 29119. W szczególności dla każdej z nich omówimy cztery pierwsze kroki procesu. W rozdziale 10 opiszemy dwie ostatnie techniki: bazującą na defektach oraz najmniej

sformalizowaną,

ale

zaskakująco

skuteczną

technikę

opartą

na

doświadczeniu. Rozdział

11

zawiera

rozważania

projektowania testów. W rozdziale

dotyczące 12

wyboru

omówimy

odpowiednich

natomiast

techniki

technik oceny

priorytetyzacji przypadków testowych. technika projektowania testów, technika testowa, technika specyfikacji testowej, technika projektowania przypadków testowych (ang. test design technique, test technique, test specification technique, test case design technique) – procedura wywodzenia i/lub wybierania przypadków testowych technika wykonywania testu (ang. test execution technique) – metoda użyta do wykonania konkretnego testu, zarówno ręcznie, jak i automatycznie

5. Testowanie oparte na modelu

Oprogramowanie ma zazwyczaj dosyć skomplikowaną strukturę i przetestowanie wszystkich możliwych aspektów jego działania nie jest możliwe. Dlatego do projektowania testów wykorzystuje się modele. Większość technik projektowania testów omówionych w rozdziałach 8 i 9 to techniki oparte na modelu. Model może opisywać sam program, wymagania lub logikę biznesową realizowaną przez aplikację. Nawet w mniej sformalizowanych podejściach, takich jak techniki oparte na doświadczeniu opisane w rozdziale 10, da się odnaleźć elementy podejścia bazującego na modelu. testowanie oparte na modelu (ang. model-based testing) – testowanie oparte na modelu działania modułu lub systemu podlegającego testom, np. na modelu wzrostu niezawodności, profilu operacyjnym lub modelach zachowania takich jak tablica decyzyjna czy diagram stanów

5.1. Cechy dobrego modelu Model obiektu (np. programu) to formalny opis tego obiektu na pewnym ustalonym poziomie abstrakcji. Przykłady takich modeli poznaliśmy w podrozdziale 4.1, w odniesieniu do cyklu życia oprogramowania. Abstrakcja pozwala nam opuścić w opisie nieistotne w danej chwili szczegóły obiektu i skupić się tylko na tych cechach, które nas z różnych względów interesują. Weźmy przykład programu komputerowego. Najbardziej dokładnym modelem tego programu będzie on sam – skompilowany (lub interpretowany) zbiór linii kodu. Załóżmy, że program ten składa się z wielu modułów, które komunikują się ze sobą, np. przez wywoływanie jednego modułu przez inny. Może się zdarzyć, że

w działaniu programu nie będzie nas interesował przepływ sterowania między poszczególnymi liniami kodu, ale to, w jaki sposób moduły wzajemnie się wywołują. Abstrahujemy od programu jako zbioru linii kodu i wychodzimy na wyższy, bardziej ogólny poziom: patrzymy na program jak na zbiór modułów. Wewnętrzna struktura modułu nas nie interesuje. Model tak postrzeganego programu może składać się np. z modułów i opisu relacji między nimi (przykładem może być diagram klas w UML). Jeśli interesuje nas sekwencja wywołań funkcji w zbiorze klas, to wtedy modelem działania programu może być diagram sekwencji; jeśli skupiamy się na definiowaniu i używaniu danych w programie, to modelem może być diagram przepływu danych, i tak dalej. Dobry model systemu musi charakteryzować się kilkoma cechami: formalizm – model musi być opisany w ścisły, formalny sposób, precyzyjnym językiem; niesprzeczność – z modelu nie mogą wynikać dwa sprzeczne ze sobą zachowania systemu; abstrakcja – model powinien skupiać się tylko na tych aspektach i charakterystykach systemu, które nas w danej chwili interesują; jednoznaczność – dwie osoby używające modelu w taki sam sposób powinny otrzymać dokładnie takie same wyniki (chyba że model jest niedeterministyczny). Po co w ogóle tworzy się modele? Czasami wynika to po prostu z powodów finansowych lub związanych z bezpieczeństwem. Jeśli NASA chce wystrzelić rakietę na orbitę okołoziemską, to nie będzie tworzyła setek prototypów rakiet, wystrzeliwała ich z różnymi parametrami lotu i patrzyła, co się stanie (wyleci na orbitę? poleci w kosmos? a może spadnie na ziemię, powodując przy tym śmierć i zniszczenie?). Raczej użyty zostanie matematyczny model lotu rakiety umożliwiający szybkie, wielokrotne, tanie i bezpieczne uruchamianie (w komputerze), pozwalające na dokładne dostrojenie wszystkich niezbędnych parametrów. W testowaniu oprogramowania powód stosowania modeli jest nieco inny. Każda technika projektowania testów jest nakierowana na odkrywanie w oprogramowaniu innego rodzaju błędów. Wybór techniki oparty jest na czymś, co Boris Beizer nazywa „hipotezą błędu” ([62], [14]). Hipoteza błędu wyraża nasze przekonanie

o

rodzajach

błędów,

jakie

mogą

istnieć

w

testowanym

oprogramowaniu. Dysponując taką hipotezą, możemy wybrać odpowiednią technikę projektowania testów. Jeśli hipoteza jest słuszna, to nasze podejście będzie efektywne. Technika projektowania jest oparta na modelu błędu, czyli na ścisłym, formalnym opisie niezgodności rzeczywistego działania programu z oczekiwanym. Wykorzystując odpowiedni model działania programu, uwzględniający określony model błędu, możemy efektywnie generować przypadki testowe pozwalające wykrywać błędy danego typu. Przypadki są tak generowane, aby pokryć zadane wymagania testowe. Wymagania testowe mogą być opisane na poziomie modelu. Jeśli na przykład wykorzystujemy technikę projektowania opartą na kryterium pokrycia gałęzi, to naszym modelem jest graf przepływu sterowania, a wymaganiem testowym będzie zbiór wszystkich krawędzi tego grafu. Kwestie związane z pokrywaniem wymagań testowych omówimy dokładnie w dalszych rozdziałach. Ważną korzyścią ze stosowania modeli jest to, że generacja przypadków testowych na podstawie modelu jest zwykle szybka i automatyczna. Nieco bardziej problematyczne i czasochłonne może być dostarczenie konkretnych danych testowych dla wygenerowanych przypadków testowych. Na przykład znalezienie wartości wejściowych, które pozwolą na uzyskanie określonej ścieżki przepływu sterowania może być bardzo trudne. Istnieją wyspecjalizowane narzędzia, które są w stanie, przez symboliczne wykonywanie kodu, dostarczyć odpowiednie przypadki testowe (np. [63]). Inną zaletą stosowania podejścia opartego na modelu jest możliwość wczesnego wykrywania błędów oraz rozpoczęcia czynności testowych przed fazą kodowania. Zanim nastąpi implementacja kodu, jest tworzony projekt programu, który możemy traktować jak model (np. diagramy UML). Model może zostać poddany procesowi przeglądu (patrz podrozdział 6.1), dzięki czemu jest możliwe wykrycie i naprawa usterek. Takie podejście chroni nas przed popełnieniem tych błędów na etapie implementacji. Część modeli ma właściwość wyroczni. Oznacza to, że potrafią na podstawie określonego wejścia opisać pożądane zachowanie czy odpowiedź systemu. Ta właściwość pozwala na automatyzację nie tylko projektowania i wykonania testów, lecz także na automatyzację weryfikacji tego, czy test jest zdany, czy nie. Podsumowując, zalety testowania opartego na modelu są następujące [64]: formalizm modelu pozwala na systematyczność w tworzeniu przypadków testowych, przez co zmniejsza się ryzyko pominięcia

w testowaniu niektórych aspektów systemu; można ustalać odpowiedni poziom precyzji testów, co pozwala na stosowanie różnych kryteriów pokrycia; są niezależne od psychologii, tzn. osobowość użytkownika modelu nie wpływa na jego działanie podczas testowania; testy weryfikują poprawność modeli; testy są generowane automatyczne; często oczekiwane odpowiedzi można wygenerować automatycznie z modelu. Testowanie oparte na modelu ma również pewne wady: testy nie będą bardziej dokładne niż sam model; testy dotyczą tylko jednego aspektu działania systemu, opisanego przez model; wiele modeli jest bezużytecznych, skomplikowanych, trudnych w użyciu lub wymagających dużej pracochłonności; niewłaściwe podejście do takiego testowania daje fałszywe poczucie bezpieczeństwa; tak stworzone testy nie uwzględniają doświadczenia i intuicji ludzi.

5.2. Taksonomia modeli Modele można charakteryzować pod wieloma różnymi kątami. Na rysunku 5.1 przedstawiono taksonomię zaproponowaną w [65]. Taksonomia ta wyróżnia siedem wymiarów modeli, zgrupowanych w trzy kategorie: opis modelu, generacja testów oraz wykonanie testów. Przedmiot modelu – modelować można zarówno testowany system (ang. System Under Test, SUT), jak i środowisko. Zwykle modeluje się jedno i drugie. W przypadku SUT model działa jak wyrocznia i jest w stanie dostarczyć, dla zadanego wejścia, oczekiwane wyjście (zachowanie) systemu. Ponadto, jego struktura może zostać wykorzystana do generacji testów. Model środowiska narzuca z kolei ograniczenia na możliwe wejścia.

Redundancja – model, poza generacją przypadków testowych może również generować kod źródłowy tych przypadków. Charakterystyki – model może dopuszczać niedeterministyczne zachowanie, uwzględniać następstwo zdarzeń (modele uwarunkowane czasowo) oraz pracować na danych dyskretnych (np. poszczególne chwile czasu jako kroki, skończona liczba stanów), ciągłych (czas bądź zbiór możliwych wartości jest ciągły), jak i hybrydowych (ciągłodyskretnych).

Rysunek 5.1. Taksonomia modeli

Paradygmat – opisuje paradygmat i notację zapisu modelu. Można wyróżnić pięć głównych paradygmatów: pre–post (wykorzystywane np. w językach modelowania takich, jak notacja Z [66], notacja B [67] czy język OCL [68]), oparty na przejściach (wykorzystujący np. sieci Petriego, diagramy stanów UML czy maszyny stanowe omówione w podrozdz. 8.5), oparty na historii, funkcjonalny oraz operacyjny (symulujący oczekiwane użycie systemu na podstawie rozkładu prawdopodobieństwa). Kryteria wyboru przypadków testowych – opisują sposób, w jaki będą wybierane testy. Wszystkie te kryteria omówione zostaną w rozdziałach 8, 9 i 10. Technologia – opisuje sposób generowania przypadków testowych. Wykonywanie testów – w podejściu online generacja testów następuje na podstawie historii zachowania się systemu. Podejście to wykorzystuje się zwłaszcza w modelach niedeterministycznych, gdzie model musi podążać za nieznaną z góry ścieżką wykonania programu. W podejściu offline wszystkie testy są generowane przed ich uruchomieniem.

5.3. Przykład wykorzystania modelu Skoro omówiliśmy teoretyczne podstawy modelowania, czas na praktykę. Przedstawimy konkretny przykład rzeczywistego systemu i na jego przykładzie pokażemy, jak opisane w poprzednim podrozdziale korzyści ze stosowania modelu uwidaczniają się w kontekście testowania. Przykład dotyczy pewnej transformacji słów kodu Graya. Kod Graya długości n to uporządkowana sekwencja wszystkich 2n słów binarnych o długości n, w której każde dwa kolejne słowa różnią się tylko na jednym bicie. Rozszerzenie kodu Graya o jeden bit odbywa się według dwóch prostych zasad: 1) mając kod o długości n, dopisz jego odbicie lustrzane; 2) do oryginalnego kodu dopisz na początku zero, a do elementów w odbiciu lustrzanym jedynkę. Na rysunku 5.2 pokazano przykład tworzenia kodu Graya o długości 3 z kodu o długości 2. Z prawej strony rysunku widzimy numerację (idącą od zera) kolejnych elementów ośmioelementowego kodu Graya o długości 3. Na przykład

element 000 jest pierwszy (pozycja nr 0), element 001 drugi (pozycja nr 1), element 011 trzeci (pozycja nr 2) itd. Numeracja jest podana w systemie dwójkowym.

Rysunek 5.2. Generacja kodu Graya o długości 3 z kodu o długości 2 System Gray2Order to układ kombinacyjny1 zamieniający element kodu Graya w binarną sekwencję, opisującą pozycję tego elementu w kodzie. Testowany przez nas system będzie działał na kodzie Graya o długości 8, złożonym z 28 = 256 elementów. System ma postać układu scalonego, z 8 wejściami i 8 wyjściami. Nie znamy jego wewnętrznej struktury, ale dysponujemy modelem w postaci schematu elektronicznego. Jest on przedstawiony na rys. 5.3. Wejścia są oznaczone symbolami od I1 do I8, przy czym I8 opisuje bit najbardziej znaczący, I1 – najmniej znaczący. Wyjścia są oznaczone przez O1 do O8, z taką samą interpretacją bitów znaczących. Model składa się z siedmiu bramek logicznych XOR (alternatywa rozłączna, ang. XOR, od exclusive OR), połączonych w odpowiedni sposób. Każda bramka XOR ma dwa wejścia i jedno wyjście. Jeśli wejścia są różne (0 i 1 lub 1 i 0), to na wyjściu pojawia się sygnał 1. Jeśli takie same (dwa zera lub dwie jedynki), to na wyjściu bramki pojawi się 0. Wyjście z bramki jest zarazem jednym z wejść do bramki następnej.

Rysunek 5.3. Model 8-bitowego konwertera Gray2Order Nasz model ma wszystkie cechy dobrego modelu opisane w podrozdziale 5.1. Jest formalny, bo używa precyzyjnej notacji opisu układów elektronicznych. Jest niesprzeczny, bo bramki działają w sposób deterministyczny i nie da się uzyskać dwóch różnych wyjść na podstawie tego samego wejścia. Jest jednoznaczny, bo reguły działania bramek logicznych są ściśle określone i każdy użytkownik modelu dostanie, dla zadanego wejścia, taki sam wynik jak pozostali użytkownicy. Jest wreszcie abstrakcyjny – fizyczna budowa układu (ścieżki, tranzystory itp.) została zamodelowana za pomocą abstrakcyjnych elementów, takich jak bramki logiczne. Nie interesuje nas wewnętrzna budowa bramek logicznych, ale wyłącznie ich działanie oraz sposób połączenia elementów. Zauważmy ponadto, że model może podać oczekiwaną odpowiedź dla dowolnego zadanego wejścia. Można również wykorzystać ten model do automatycznego projektowania testów dla różnych kryteriów pokrycia, np.: dla każdej błędnie liczącej bramki powinien istnieć przynajmniej jeden test, w którym wystąpi to złe obliczenie; albo: dla każdej bramki powinny istnieć testy, które przetestują wszystkie cztery możliwe wejścia do tej bramki, itd. Jak przetestować system Gray2Order? Załóżmy, że dysponujemy środowiskiem testowym, w którym możemy zainstalować nasz układ, pobudzać go odpowiednimi sygnałami na wejściu i analizować otrzymane wyjście. Każde podanie 8 sygnałów wejściowych oczywiście zajmuje czas i chcielibyśmy przetestować system za pomocą możliwie najmniejszej liczby przypadków

testowych, ale jednocześnie możliwie jak najpełniej. Pojęcie „jak najpełniej” jest trudne do dokładnego zdefiniowania. Oczywiście, w trywialnym przypadku możemy przetestować układ, podając mu wszystkie możliwe kombinacje sygnałów wejściowych. Dla systemu 8-bitowego takich kombinacji jest 256, co jeszcze nie jest dużą liczbą. Ale w przypadku układu 64-bitowego musielibyśmy wykonać 264 = 18,446,744,073,709,551,616 testów, co jest już niewykonalne. Jak zatem sensownie przetestować nasz układ? Zastanówmy się najpierw, jak mogłaby wyglądać hipoteza błędu. Innymi słowy – co może spowodować nieprawidłowy wynik działania układu? Nasz model wykorzystuje bramki logiczne, które fizycznie są realizowane przez odpowiednio połączone tranzystory. Na skutek różnych defektów (uszkodzenia mechaniczne, błąd przy drukowaniu płytki itp.) któraś z bramek logicznych może źle działać, tzn. dawać błędny wynik działania XOR. Pomysł polega więc na tym, aby skonstruować takie przypadki testowe, które wymuszą – dla każdej bramki – przyjęcie przynajmniej raz każdej możliwej kombinacji wejść (00, 01, 10, 11). Jeśli bowiem jedna z bramek działa nieprawidłowo dla jakiejś kombinacji wejść, to przynajmniej jeden z testów powinien to wykryć. Wymaganiem testowym jest więc zbiór wszystkich możliwych kombinacji wejść dla każdej bramki logicznej. Test pokryje wymaganie „wejścia 01 dla bramki U2”, jeśli podane na wejściu sygnały spowodują podanie bramce U2 wejść 0 i 1. Kryterium pokrycia zostanie spełnione, jeśli każda bramka przynajmniej raz otrzyma na wejściu każdą możliwą kombinację sygnałów 0 i 1. Proponowane przypadki testowe (PT) pokazano w tabeli 5.1. Pierwsza kolumna zawiera numer przypadku testowego. W kolejnych 8 kolumnach umieszczone są sygnały podawane na 8 wejść układu. Kolumny U1 do U7 zawierają opis kombinacji wejść do wszystkich siedmiu bramek logicznych pod wpływem podania sygnałów wejściowych I8 I7 … I1. Ostatnia kolumna zawiera oczekiwane wyjście układu, dzięki czemu możemy porównać wynik oczekiwany z rzeczywistym. Tabela 5.1. Przypadki testowe dla konwertera Gray2Order

test

W ejścia do bramek logicznych

Oczekiwane wyjście I8 I7 I6 I5 I4 I3 I2 I1 U1 U2 U3 U4 U5 U6 U7 (O8...O1) Sygnały wejściowe

PT1 0 0 0 0 0 0 0 0 00 00 00 00 00 00 00 00000000 PT2 1 1 1 1 1 1 1 1 11 01 11 01 11 01 11 10101010 PT3 0 1 1 1 1 1 1 1 01 11 01 11 01 11 01 01010101 PT4 1 0 0 0 0 0 0 0 10 10 10 10 10 10 10 11111111 Zauważmy, że zarówno dla naszego 8-bitowego układu, jak i w ogólnym przypadku, dla dowolnej liczby bitów wejściowych, wystarczą tylko 4 testy do spełnienia naszego kryterium pokrycia. W każdej kolumnie od U1 do U7 występują wszystkie cztery kombinacje wejść do danej bramki. Na przykład realizacja wejść 01 do bramki U5 nastąpi przy wykonaniu testu nr 3. Dzięki odpowiednio sformułowanej hipotezie błędu oraz modelowi układu byliśmy w stanie stworzyć rozsądny zestaw przypadków testowych. Gdyby któraś z bramek była zepsuta, to istniałaby duża szansa, że któryś z testów to wykryje. Nie możemy jednak być tego pewni w stu procentach. Na przykład na skutek błędów w drukowaniu płytki jedna z bramek może dać złe wyjście przy pewnej szczególnej kombinacji dwóch lub więcej wejść. Nasze testy nie wykryją tego, jeśli taka kombinacja wejść nie wystąpi w żadnym teście. Może też wystąpić tzw. zjawisko maskowania błędów: błędny sygnał, powstały w jednym miejscu, może zostać zamieniony ponownie, na skutek innego błędu, na poprawny w innym miejscu. maskowanie usterek, maskowanie defektów, maskowanie błędów (ang. fault masking, defect masking, failure masking) – sytuacja, w której występowanie jednego defektu lub błędu uniemożliwia wykrycie innego [7] Widać więc, że żadna metoda testowania (za wyjątkiem testowania gruntownego które, jak już wspomnieliśmy, zwykle jest niewykonalne) nie da nigdy stuprocentowego przekonania o poprawności systemu. Zastosowanie modelu pozwala jednak zredukować liczbę przypadków do rozsądnej liczby. Ponadto, przypadki te są dosyć silne, tzn. z dużym prawdopodobieństwem wykryją istniejący w układzie błąd. Zauważmy też, że nasz zestaw przypadków testowych spełnia również inne, ciekawe kryteria pokrycia, na przykład pokrycie wszystkich możliwych wartości dla każdego bitu wejściowego czy pokrycie wszystkich możliwych wartości dla każdego bitu wyjściowego. Gdyby na

przykład żaden test nie powodował wystąpienia sygnału 1 na bicie O5, nigdy nie wykrylibyśmy błędu powodowanego tym zdarzeniem.

5.4. Modele działania oprogramowania Pokazaliśmy przykład zastosowania modelu w testowaniu układu elektronicznego. Jak jednak mogą wyglądać modele opisujące działanie oprogramowania? W ciągu ostatnich kilkudziesięciu lat powstało bardzo wiele takich metod formalnego opisu. Niektóre z nich są wymienione w tabeli 5.2. Większość z tych modeli będzie nam służyła w następnych rozdziałach do projektowania efektywnych przypadków testowych. Przedstawimy teraz dwa podstawowe modele działania programu, należące do grupy modeli grafowych: graf przepływu sterowania oraz graf przepływu danych. Omawiamy je wcześniej niż pozostałe modele, ponieważ będziemy je wykorzystywać nie tylko w rozdziałach 8 i 9 dotyczących technik białoi czarnoskrzynkowych, lecz także podczas opisywania analizy statycznej w podrozdziale 6.2. Tabela 5.2. Modele działania oprogramowania

Aspekt systemu

Przykładowe modele

Zastosowanie w testowaniu

przepływ s terowania, złożonoś ć kodu

tes towanie g raf przepływu s terowania białos krzynkowe, analiza s tatyczna

przepływ danych, złożonoś ć komunikacji

g raf przepływu danych, g raf wywołań

tes towanie białos krzynkowe, analiza s tatyczna tes towanie mutacyjne, tes towanie interfejs ów,

s kładnia

model s kładniowy

tes towanie s ekwencji zdarzeń akcje, s tany prog ramu

model mas zyny s tanowej

tes towanie czarnos krzynkowe

ws półbieżnoś ć

s ieci Petrieg o, alg ebra proces ów

tes towanie czarnos krzynkowe

kombinacje warunków

tablica decyzyjna, g rafy przyczynowo-s kutkowe, drzewa klas yfikacji, tes towanie par

tes towanie czarnos krzynkowe

poprawnoś ć

model aks jomatyczny, alg ebra proces ów, log iki temporalne, notacja Z, język B, OCL

dowodzenie poprawnoś ci prog ramu

proces y biznes owe

diag ram przypadków użycia, model przepływu zdarzeń

tes towanie czarnos krzynkowe, tes towanie eks ploracyjne

przepływ sterowania (ang. control flow) – sekwencja zdarzeń, w postaci ścieżki, występująca podczas pracy modułu lub systemu przepływ danych (ang. data flow) – abstrakcyjna reprezentacja sekwencji i możliwych zmian stanu obiektu danych, gdzie dostępne stany obiektu to utworzenie, użycie lub usunięcie [14] punkt startu (ang. entry point) – pierwsze wyrażenie wykonywane wewnątrz modułu

punkt wyjścia (ang. exit point) – ostatnie wyrażenie wykonywane wewnątrz modułu

5.4.1. Graf przepływu sterowania Graf przepływu sterowania (ang. Control Flow Graph, CFG) jest abstrakcyjnym modelem opisującym wszystkie możliwe sekwencje wykonań instrukcji programu. Składa się z wierzchołków połączonych skierowanymi krawędziami. Każdy wierzchołek opisuje jedną instrukcję programu lub sekwencję instrukcji I1, I2, …, In takich, że instrukcje te wykonają się zawsze albo wszystkie po kolei, albo nie wykona się żadna z nich. Takie sekwencje nazywamy blokami podstawowymi. Formalnie, sekwencja I1, I2, …, In jest blokiem podstawowym, kiedy są spełnione dwa warunki: gdy sterowanie przejdzie do początku sekwencji, wtedy zawsze nastąpi wykonanie wszystkich instrukcji I1, I2, I3 …, In dokładnie w takiej kolejności (czyli w obrębie sekwencji nie ma rozgałęzień),

każdy skok spoza tej sekwencji w jej obręb może nastąpić tylko do instrukcji I1, tzn. do początku bloku. instrukcja, instrukcja kodu źródłowego (ang. statement, source statement) – element kodu zapisanego w określonym języku programowania, stanowiący najmniejszą niepodzielną jednostkę wykonania lub traktowany w ten sposób instrukcja wykonywalna (ang. executable statement) – wyrażenie, które w trakcie kompilacji jest tłumaczone na kod binarny i które będzie wykonywane proceduralnie podczas działania programu; może ono wykonywać akcje na danych programu blok podstawowy (ang. basic block) – ciąg składający się z jednej lub wielu następujących po sobie instrukcji bez rozgałęzień, przy czym każdy skok spoza bloku w jego obręb – jeśli istnieje – to musi nastąpić do pierwszej instrukcji bloku

DD-ścieżka (ang. DD-path od decision-to-decision) – ścieżka w CFG będąca w jednej z poniższych postaci: – pojedynczy wierzchołek o

stopniu2

wchodzącym

=

0

(wierzchołek

początkowy); – pojedynczy wierzchołek o stopniu wychodzącym = 0 (wierzchołek końcowy); – pojedynczy wierzchołek o stopniu wchodzącym ≥ 2 lub stopniu wychodzącym ≥ 2 (wierzchołek decyzyjny lub tzw. punkt łączenia); – maksymalny łańcuch, tzn. ciąg wierzchołków (v1, v2, …, vn) taki, że stopnie wchodzące oraz wychodzące wszystkich (v1, v2, …, vn) są równe 1 i nie da się tej ścieżki rozszerzyć tak, aby właściwość ta nadal zachodziła W grafach przepływu sterowania pojedyncze wierzchołki często reprezentują bloki podstawowe. Dzięki tej reprezentacji rozmiar grafu (w sensie liczby jego wierzchołków) jest mniejszy niż gdybyśmy chcieli reprezentować każdą pojedynczą instrukcję osobnym węzłem. Grupowanie instrukcji w jeden blok nie ma żadnego wpływu na analizę przepływu sterowania programu, ze względu na właściwość bloku: jego instrukcje zawsze wykonają się w ten sam sposób. Bloki podstawowe reprezentują liniowe sekwencje instrukcji. Czasami zamiast bloków podstawowych wykorzystuje się tzw. DD-ścieżki3 . Na rysunku 5.4 przedstawiono podstawowe elementy modelu przepływu sterowania.

Rysunek 5.4. Podstawowe elementy grafu przepływu sterowania Wśród wierzchołków wyróżniamy początkowy, czyli ten, od którego rozpoczyna się przepływ sterowania oraz końcowy, w którym następuje koniec działania programu. Krawędź od wierzchołka X do wierzchołka Y reprezentuje

przepływ sterowania z bloku X do bloku Y. Instrukcje, w których jest podejmowana decyzja co do wyboru dalszego sterowania są reprezentowane przez wierzchołki z więcej niż jedną krawędzią wychodzącą. W przypadku instrukcji if-then lub if-then-else będą to dwie krawędzie wychodzące. W przypadku instrukcji switch-case może ich być więcej. Wierzchołek może też mieć więcej niż jedną krawędź wchodzącą, jeśli może do niego nastąpić skok z kilku miejsc w programie. Formalnie, graf przepływu sterowania to czwórka G = (V, E, v0, VT), gdzie: V – jest niepustym, skończonym zbiorem wierzchołków; E ⊂ V × V – jest zbiorem krawędzi skierowanych łączących pary wierzchołków; v0 ∈ V – jest wierzchołkiem początkowym; VT ⊂ V – jest niepustym zbiorem wierzchołków (terminalnych). Zauważmy, że zgodnie z powyższą

definicją

wierzchołek

końcowych

początkowy

w szczególności może być jednocześnie wierzchołkiem końcowym. Graf przepływu sterowania jest budowany na podstawie kodu źródłowego. Istnieją narzędzia do automatycznej budowy takich grafów. Proces ten jest bardzo prosty i polega na podziale kodu na bloki podstawowe, a następnie opisaniu relacji następstw tych bloków. Na rysunku 5.5 przedstawiono metodę transformacji dla podstawowych konstrukcji algorytmicznych. diagram przepływu sterowania (ang. control flow graph) – abstrakcyjna prezentacja możliwych sekwencji zdarzeń (w postaci ścieżek) występujących podczas uruchomienia modułu lub systemu Sekwencja

następujących

po

sobie

instrukcji

(blok

podstawowy)

jest

reprezentowana pojedynczym wierzchołkiem. Instrukcja warunkowa if-then składa się z wierzchołka zawierającego warunek (B1) oraz wierzchołka zawierającego ciało instrukcji warunkowej (B2). Jeżeli warunek jest spełniony, to sterowanie przechodzi lewą krawędzią z B1 do B2, a następnie, po wykonaniu instrukcji w B2 przechodzi do pierwszej instrukcji następującej po końcu instrukcji warunkowej (B3). Jeżeli warunek nie jest spełniony, to sterowanie przechodzi od razu prawą krawędzią do B1. Na podobnej zasadzie działa transformacja do grafu w przypadku instrukcji if-then-else, przy czym niespełnienie

Rysunek 5.5. Transformacja konstrukcji algorytmicznych na grafy przepływu sterowania warunku oznacza przejście prawą krawędzią do początku bloku następującego po konstrukcji if-then-else (B4). Transformacja dla instrukcji switch-case jest analogiczna, gdyż może być rozumiana jako wielopoziomowa instrukcja if-thenelse.

Instrukcja for składa się z inicjalizacji, warunku, inkrementacji oraz instrukcji wewnątrz pętli. Dlatego wejściowym wierzchołkiem dla konstrukcji for jest B1 zawierający instrukcję inicjalizacji. Na przykład, dla instrukcji for (i:=0; i0) mogą być oznaczone odpowiednio i > 0 oraz i -1 && pat[k+1] != pat[i]) k = pi[k]; if (pat[i] == pat[k+1])

11 12 13 14

k++; pi[i] = k; } return pi; }

Listing 5.1. Kod w C obliczający tablicę prefiksów dla algorytmu Knutha–Morrisa– Pratta Pierwszy krok to identyfikacja bloków podstawowych. Bloki te są pokazane z lewej strony na rysunku 5.6. Instrukcja i++ w pętli for przynależy do bloku B9 i jest wykonywana jako jego ostatnia instrukcja. Drugi krok polega na określeniu następstw między blokami, których graficzną reprezentacją jest graf przepływu sterowania pokazany na rysunku 5.6 z prawej strony.

Rysunek 5.6. Podział kodu na bloki podstawowe i odpowiadający mu graf przepływu sterowania

5.4.2. Ograniczenia w stosowaniu grafu przepływu sterowania Rozwój technik programowania powoduje, że ścisły zapis przepływu sterowania niektórych programów jest trudny lub wręcz niemożliwy. Rozważmy trzy następujące przykłady: obsługa wyjątków – różne typy wyjątków mogą wystąpić w wielu miejscach programu. W programie na rysunku 5.7a wyjątki mogą nastąpić podczas zarówno otwierania pliku, czytania z pliku, jak i wykonywania operacji arytmetycznej. W sytuacji wystąpienia wyjątku możemy je rozróżniać tak, jak to zostało pokazane na rysunku, ale wciąż nie możemy reprezentować poprawnie sterowania, gdyż B1 tak naprawdę nie jest blokiem – pod wpływem rzucenia wyjątku może

nastąpić skok ze środka bloku do B2 lub B3. Częściowym rozwiązaniem jest reprezentowanie każdej linii programu osobnym wierzchołkiem, jednak to znacznie komplikuje postać grafu;

Rysunek 5.7. Problematyczne programy dla modelowania przepływu sterowania rekurencja modelować

– w przypadku stosowania rekurencji trudno jest działanie programu za pomocą grafów przepływu

sterowania. Na rysunku 5.7b przerywanymi strzałkami zaznaczono możliwe skoki w przypadku stosowania rekurencji. Jeśli wywołano rekurencyjnie funkcję f, to następuje skok B3 → B1. Jeśli zakończono rekurencyjne wywołanie funkcji w B2 i to nastąpił powrót do funkcji wywołującej, mamy skok B2 → B3, przy czym skok może nastąpić do „środka” instrukcji return, między jednym a drugim wywołaniem f; dynamiczne wiązanie – w przykładzie z listingu 5.2 w momencie wykonywania metody dodaj na obiekcie s klasy Student nie zawsze możemy przewidzieć, która metoda dodaj zostanie użyta – może to zależeć od dynamicznego typu obiektu b, np. książka może być niedostępna dla studentów spoza danej uczelni. Grafy przepływu sterowania są zbyt słabym narzędziem do modelowania takich sytuacji.

... wypożycz(Book b, Student s) { if (b.dostępna()) { s.dodaj(b) }

} ... Listing 5.2. Fragment kodu z dynamicznym wiązaniem

5.4.3. Graf przepływu danych Graf przepływu danych (ang. data flow graph) to graf przepływu sterowania wzbogacony o informacje dotyczące użycia i definicji zmiennych. Graf ten umożliwia śledzenie przepływu danych od momentu ich inicjalizacji do momentu ich użycia. Model ten pozwala na testowanie (zarówno statyczne, jak i dynamiczne), które skupia się na przepływach wartości danych. analiza przepływu danych (ang. data flow analysis) – analiza statyczna na podstawie definiowania i użycia zmiennych Definicja zmiennej to miejsce, w którym wartość zmiennej jest zapisywana do pamięci (przypisanie, wejście itp.). Użycie zmiennej to miejsce, w którym wartość zmiennej jest odczytywana z pamięci w celu wykorzystania (prawa strona instrukcji przypisania, warunek logiczny itp.).

Rysunek 5.8. Graf przepływu danych dla funkcji compute_prefix Rozważmy przykład opisanej wcześniej funkcji compute_prefix. W bloku B3 następuje tam definicja zmiennej pi (w instrukcji pi[0]=k), a po przejściu B3 → B4 → B5 → B6 w bloku B6 następuje jej użycie (w instrukcji k=pi[k]). Graf przepływu danych dla funkcji compute_prefix jest przedstawiony na rysunku 5.8. Jest to graf przepływu sterowania z rysunku 5.6 z dodanymi informacjami dotyczącymi definicji i użyć zmiennych. W celu uproszczenia pominęliśmy etykiety krawędzi opisujące warunki logiczne. Niektórzy autorzy (np. [27]) przypisują definicje i użycia także do krawędzi (właśnie w przypadku tych warunków), jednak my będziemy stosować konwencję odwołującą się bezpośrednio do bloków. Krawędzie reprezentują bowiem abstrakcyjne pojęcie przepływu sterowania, natomiast zmienne są umieszczane w konkretnych miejscach (blokach) programu.

5.4.4. Ścieżki, ścieżki testowe i ścieżki nieosiągalne

Należy odróżnić od siebie dwa pojęcia związane z grafowymi modelami działania programu: ścieżkę oraz ścieżkę testową. Ścieżką w grafie G = (V, E, v0, VT)

o długości n nazywamy dowolny ciąg wierzchołków v1, v2, …, vn+1 ∈ V takich, że (v1, vn+1) ∈ E dla każdego i = 1, … n. Ścieżka testowa jest natomiast ścieżką opisującą realizację działania programu dla określonych danych wejściowych. Musi zatem zawsze zaczynać się w wierzchołku początkowym, a kończyć w wierzchołku końcowym grafu przepływu sterowania. Rozróżnienie to jest istotne w przypadku grafowych kryteriów pokrycia omówionych w rozdziale 9: o ile warunkami testowymi i elementami pokrycia dla metod grafowych mogą być dowolne ścieżki (np. pojedyncze wierzchołki4 lub krawędzie), o tyle fizycznymi elementami pokrywającymi te elementy będą zawsze ścieżki testowe. ścieżka testowa, ścieżka przepływu sterowania (ang. test path, control flow path) – sekwencja wydarzeń, np. wykonywalnych wyrażeń, w ramach modułu lub systemu począwszy od punktu wejścia do punktu wyjścia Zgodnie z powyższą definicją pojęcie ścieżki testowej można rozszerzyć na sekwencje „wydarzeń”, którymi mogą być np. stany w maszynie stanowej, wywołane funkcje w grafie wywołań, wywołane podmoduły. Nie każda ścieżka występująca w grafie przepływu sterowania musi być osiągalna. Może nie istnieć zestaw danych, który spowodowałby odpowiadające jej rzeczywiste wykonanie programu. Takie ścieżki nazywa się nieosiągalnymi. Rozważmy przykład prostego kodu z rysunku 5.9.

Rysunek 5.9. Kod oraz odpowiadający mu CFG z nieosiągalną ścieżką Zauważmy, że gdy warunek w bloku B1 jest spełniony, nastąpi podstawienie 1 do zmiennej z, co automatycznie spowoduje spełnienie warunku B3. Dlatego ścieżka B1 → B2 → B3 → B5, choć syntaktycznie poprawna, w praktyce nigdy nie będzie mogła wystąpić. Jest więc ścieżką nieosiągalną. Jeśli powyższy przykład wydaje się nieco sztuczny, to Czytelnik może przeanalizować kod z listingu 9.9 ze s. 336. Jest to rzeczywisty nieosiągalnych.

program,

w

którym

występuje

wiele

ścieżek

Wiele grafowych kryteriów pokryć opisanych w rozdziale 9 wymaga pokrycia testami określonych zbiorów ścieżek. Jeśli okaże się, że występują wśród nich ścieżki nieosiągalne, to można przyjąć jedno z następujących podejść: zignorować ścieżki nieosiągalne (tzn. usunąć je ze zbioru wymaganych elementów pokrycia), osłabić kryterium (tzn. przyjąć inne, słabsze kryterium, które nie będzie generować ścieżek nieosiągalnych), pokryć ścieżki „podobne” do ścieżek nieosiągalnych, stosując objazdy (ang. detour) lub ścieżki poboczne (ang. sidetrip) omówione w podrozdziale 9.14. ścieżka nieosiągalna, ścieżka niewykonalna (ang. unreachable path, infeasible path) – ścieżka, dla której nie istnieje zestaw danych wejściowych, przy których przejście tej ścieżki jest możliwe

1 Układ kombinacyjny to elektroniczny układ cyfrowy, w którym stan wyjścia zależy wyłącznie od stanu jego wejścia. 2 W grafie skierowanym (takim jak CFG) stopień wchodzący wierzchołka v to liczba krawędzi wchodzących do v, a stopień wychodzący – liczba krawędzi wychodzących z v. Wierzchołek decyzyjny zawsze ma stopień wychodzący co najmniej 2. 3 Istnieje subtelna różnica między definicją bloku podstawowego a DD-ścieżką zdefiniowaną tak, jak w tym rozdziale (patrz p. 9.10.3). 4 Wierzchołek, w świetle definicji ścieżki, jest ścieżką o długości 0 (bez krawędzi).

6. Techniki testowania statycznego

Techniki testowania statycznego to zestaw narzędzi służących do sprawdzania ręcznie lub za pomocą analizy automatycznej kodu lub innych artefaktów procesu wytwórczego (dokumentacja, wymagania) bez uruchamiania kodu. Do grupy technik statycznych zalicza się przeglądy oraz analizę statyczną kodu. Proces testowania przy użyciu tych metod jest nazywany testowaniem statycznym. W podrozdziale 6.1 omówimy przeglądy, w kolejności zgodnej ze stopniem ich sformalizowania. W podrozdziale 6.2 opiszemy metody analizy statycznej kodu oraz architektury. Analiza statyczna zawiera w sobie m.in. analizę przepływu danych. Tę metodę wykorzystamy również w testach białoskrzynkowych (p. 9.12.6). analiza statyczna (ang. static analysis) – analiza artefaktów oprogramowania, np. wymagań lub kodu źródłowego przeprowadzona bez wykonywania tych artefaktów. Analiza statyczna jest na ogół przeprowadzana za pomocą narzędzi testowanie statyczne (ang. static testing) – testowanie artefaktów powstałych podczas tworzenia oprogramowania, np. wymagań, projektu lub kodu, bez wykonywania tych artefaktów, tzn. podczas przeglądów lub analizy statycznej analizator, analizator statyczny, narzędzie do analizy statycznej (ang. analyzer, static analyzer, static analysis tool) – narzędzie wykonujące analizę statyczną analizator kodu, analizator statyczny kodu (ang. code analyzer, static code analyzer) – narzędzie wykonujące analizę statyczną kodu; sprawdza ono kod

źródłowy pod względem pewnych właściwości, takich jak zgodność ze standardami kodowania, metryki jakości lub anomalie przepływu danych

6.1. Przeglądy Przeglądy są – paradoksalnie – najmniej docenianą lecz jedną z najbardziej efektywnych metod testowania. Capers Jones [70] w swoim raporcie na temat jakości oprogramowania zebrał dane z ponad 13 000 projektów i zbadał, m.in., efektywność usuwania defektów podczas przeglądów. Wyniki te zebrane są w tabeli 6.1. Tabela 6.1. Efektywność usuwania defektów dla wybranych statycznych metod testowania

Rodzaj przeglądu

Efektywność usuwania defektów najniższa mediana najwyższa

Przeg ląd nieformalny wymag ań

20%

30%

50%

Przeg ląd nieformalny projektu wys okieg o poziomu

30%

40%

60%

Ins pekcja s zczeg ółoweg o projektu funkcjonalnoś ci

30%

65%

85%

Ins pekcja s zczeg ółoweg o projektu log iczneg o

35%

65%

75%

Ins pekcja kodu lub analiza s tatyczna

35%

60%

90%

Na przykład, podczas inspekcji kodu w połowie projektów wykryto więcej niż 60% całkowitej liczby znalezionych defektów. W niektórych projektach efektywność ta dochodziła nawet do 90%! W porównaniu z przeglądami, klasyczne metody testowania nie są już tak efektywne. Mediana (w zależności od poziomu testu) waha się od 25% w przypadku testów jednostkowych do 50% w przypadku

testów systemowych. Wartości najwyższe dla testowania przyjmują wartości od 50% do 75%. Wyniki te nie oznaczają oczywiście, że w procesie testowym wystarczy ograniczyć się do przeglądów, aby uzyskać dobrą jakość oprogramowania. Po pierwsze, produktem końcowym jest działające oprogramowanie, a błędy w nim można znaleźć jedynie podczas testów dynamicznych. Po drugie, przeprowadzanie inspekcji nie jest tanie. Wymaga również czasu, przygotowania oraz odpowiednio przeszkolonych ludzi. Zwykle jednak inwestycja w przeprowadzenie przeglądów, zwłaszcza dla kluczowych dokumentów lub fragmentów kodu, zwraca się, co jasno pokazują przedstawione dane. Co więcej, przeglądy można przeprowadzać w bardzo wczesnych fazach projektu, dlatego błędy będą również wykrywane wcześniej. Systematyczne przeprowadzanie przeglądów podczas całego cyklu życia powoduje, że liczba błędów wykrywanych za pomocą przeglądów rozkłada się mniej więcej równomiernie w czasie. Bez stosowania przeglądów większość błędów ujawnia się w końcowych fazach projektu, powodując tym samym zwiększenie kosztów i nakładów pracy potrzebnych do ich usunięcia (patrz rys. 6.1 opracowany na podstawie [70]). W rezultacie, końcowe fazy projektu można nazwać strefą chaosu. Przeglądy, tak jak testowanie, mają dwa zasadnicze cele: znajdowanie defektów oraz nabranie zaufania do tworzonego oprogramowania. Jednak istnieją również cele właściwe tylko przeglądom. Pierwszym z nich jest upewnienie się, że wszyscy członkowie

Rysunek 6.1. Porównanie miejsc powstawania i wykrywania defektu bez stosowania przeglądów (u góry) i z przeglądami (na dole) zespołu projektowego rozumieją dany dokument w ten sam sposób, zarówno jeśli chodzi o samą jego treść, jak i znaczenie dla projektu. Drugim celem jest budowanie porozumienia co do treści zawartej w dokumencie poddawanym inspekcji.

przegląd (ang. review) – ocena produktu lub statusu projektu mająca na celu stwierdzenie rozbieżności od planowanych założeń i rekomendację usprawnień [71] Przeglądy formalne to rodzaj przeglądów wykorzystujących ściśle określone procedury i wymóg dokładnego raportowania. Zostały one wprowadzone w latach 70. przez Fagana [72]. W latach późniejszych wprowadzano różnego rodzaju usprawnienia do procesu inspekcji, np. Genuchten [73] zaproponował zwiększenie efektywności inspekcji przez wprowadzenie tzw. Elektronicznego systemu spotkań (ang. Electronic Meeting System). Przykładami przeglądów formalnych są np. inspekcja lub audyt. przegląd formalny (ang. formal review) – przegląd charakteryzujący się udokumentowanymi procedurami i wymaganiami Czasami można również spotkać się z pojęciem przeglądu koleżeńskiego. Jest to forma przeglądu, w której uczestnikami są koledzy autora, np. członkowie tego samego zespołu. Przykładem przeglądu koleżeńskiego może być przegląd nieformalny, przejrzenie a nawet inspekcja. Najczęstszą jednak formą tego przeglądu jest przegląd techniczny. przegląd koleżeński (ang. peer review) – przegląd produktów powstałych podczas wytwarzania oprogramowania przeprowadzany przez kolegów ich twórcy mający na celu wskazanie defektów i możliwości poprawek Jeszcze jedną odmianą przeglądu jest tzw. kontrola przy biurku. Polega ona na indywidualnym przeglądzie fragmentu kodu lub dokumentacji. kontrola przy biurku, sprawdzanie ręczne (ang. desk checking) – testowanie oprogramowania lub specyfikacji przez manualną symulację jego wykonania Więcej informacji o przeglądach można znaleźć w [30] oraz [74].

6.1.1. Proces dla testowania statycznego Do przeglądów można zastosować podstawowy proces testowy omówiony w rozdziale 3. W fazie planowania i kontroli w przypadku przeglądów powinny

nastąpić: zdefiniowanie kryteriów jakości, identyfikacja uczestników spotkań przeglądowych oraz zaplanowanie spotkania przeglądowego. W fazie analizy i projektowania uczestnikom powinny zostać rozesłane materiały, na podstawie których będzie prowadzony przegląd, takie jak obiekt testowania statycznego (np. fragment kodu źródłowego), a także specyfikacje, dokumenty wymagań, listy kontrolne (jeśli spotkanie ma być na nich oparte) itd. Chodzi

o

to,

aby

uczestnicy

spotkania

mieli

czas

na

zapoznanie się

z dokumentami i ich analizę. Dzięki temu dobrze przygotują się do samego spotkania, które będzie przeprowadzone efektywnie. Wraz z przesłaniem dokumentów uczestnicy są informowani o celach spotkania. Faza implementacji i wykonania to samo spotkanie (lub analiza dokumentów dokonywana przez uczestników przeglądu przy własnych biurkach), zebranie wyników spotkania, a także – w przypadku bardziej formalnych przeglądów – zebranie metryk dotyczących przebiegu spotkania oraz jego efektywności (np. liczba zgłoszonych uwag, liczba znalezionych błędów). W fazie ewaluacji kryteriów wyjścia i raportowania uczestnicy przeglądu (lub np. osoba prowadząca przegląd) oceniają wyniki przeglądu pod kątem spełnienia kryteriów wyjścia. Jest również tworzony raport z przeglądu. Faza czynności zamykających polega na upewnieniu się, że wszystko zostało przeprowadzone jak należy i że cała niezbędna dokumentacja została wytworzona i rozesłana odpowiednim adresatom. Jeśli podczas przeglądu zdecydowano o ponownym przeglądzie danego artefaktu, w fazie czynności zamykających, to należy upewnić się, że czas powtórnego przeglądu został już wyznaczony lub – jeśli ma to nastąpić później – że po prostu o tym nie zapomnimy.

6.1.2. Metody sprawdzania oraz możliwe wyniki przeglądu Istnieją trzy główne metody sprawdzania dokumentów poddanych przeglądowi [35]: sprawdzanie zgodności wstecz; sprawdzanie zgodności wewnętrznej; sprawdzanie zgodności w przód.

Sprawdzanie zgodności wstecz to po prostu weryfikacja, czyli upewnienie się, że artefakt poddawany przeglądowi jest zgodny z innym, źródłowym wobec artefaktu dokumentem. Jest to sprawdzanie opierające się na podstawie testów. Na przykład, jeśli przeprowadzamy przegląd kodu jakiegoś modułu, to możemy go sprawdzać pod kątem zgodności ze specyfikacją tego modułu. Sprawdzanie zgodności wewnętrznej można podzielić na dwa rodzaje. Pierwszy z nich polega na tym, że dokument jest sprawdzany pod kątem występowania określonych defektów lub innych kryteriów (np. jakości kodu). Defekty te najczęściej są opisane w postaci listy kontrolnej. Lista taka nie jest dokumentem źródłowym wobec artefaktu poddawanego przeglądowi, ale zewnętrznym, niezależnym dokumentem opisującym po prostu, pod jakim kątem należy sprawdzić dany artefakt. Z drugim rodzajem sprawdzania zgodności wewnętrznej mamy do czynienia wtedy, gdy nie istnieje podstawa testów i musimy sprawdzać dokument sam ze sobą (np. pod kątem występowania niejasności, sprzeczności lub wieloznaczności). Sprawdzanie zgodności w przód polega na upewnieniu się, że przeglądany artefakt będzie przydatny jako podstawa testów w późniejszych fazach cyklu wytwórczego. Wtedy sprawdzanie dokumentu w przyszłości pod kątem zgodności z tym artefaktem, będzie sprawdzaniem zgodności wstecz. Można wyróżnić trzy zasadnicze decyzje, jakie zespół przeglądowy podejmuje wobec sprawdzanego dokumentu: pełna akceptacja – w przypadku nieznalezienia żadnych usterek dokument może być zaakceptowany w takiej formie, w jakiej jest i nie są potrzebne żadne dodatkowe poprawki; akceptacja warunkowa – w przypadku, gdy kontrolowany dokument zawiera defekty, ale nie jest ich zbyt wiele lub nie są zbyt poważne i wiadomo, że da się je bezproblemowo naprawić, dokument może zostać zaakceptowany warunkowo (tzn. jest zaakceptowany pod warunkiem wprowadzenia odpowiednich zmian) i nie jest konieczny powtórny przegląd tego dokumentu; odrzucenie – w przypadku, gdy błędów jest zbyt dużo, jakość dokumentu jest bardzo niska lub znalezione usterki są bardzo poważne, może zostać podjęta decyzja o odrzuceniu. Dokument wraca wtedy do autora, który musi go poprawić. Konieczny jest powtórny przegląd poprawionego dokumentu.

6.1.3. Role Osoby biorące udział w przeglądach mogą odgrywać różne role. Przed przystąpieniem do przeglądu każdy uczestnik musi mieć jasno określoną rolę i wiedzieć, z jakimi obowiązkami się ona wiąże. Różne typy przeglądu wymagają uczestnictwa osób odgrywających różne role. Czasami jedna osoba może odgrywać więcej niż jedną rolę. Standardowe role występujące w przeglądach to: decydent; kierownik, moderator, prowadzący; autor (projektant, programista, architekt, tester, analityk, menedżer itp.); przeglądający; protokolant. Decydent to osoba, dla której jest przeprowadzany przegląd. Ma on uprawnienia do podjęcia decyzji, czy zostały spełnione kryteria wyjścia dla przeglądu, czyli czy cel został osiągnięty. Rola ta występuje w bardziej formalnych przeglądach. Kierownik (moderator, prowadzący) to osoba odpowiedzialna za przeprowadzenie całego procesu przeglądu, zarówno od strony administracyjnej (zebranie ludzi, rozsyłanie dokumentów, zwoływanie spotkania), jak i logistycznej (rezerwacja sali na spotkanie). W szczególności moderator prowadzi samo spotkanie przeglądowe i czuwa nad tym, aby przebiegało ono sprawnie. moderator, prowadzący inspekcję (ang. moderator, inspection leader) – lider i główna osoba odpowiedzialna za prowadzenie inspekcji lub przeglądu Autor to osoba odpowiedzialna za wytworzenie przeglądanego artefaktu. Zwykle jest też referentem, to znaczy osobą przedstawiającą pozostałym członkom zespołu przeglądowego dany dokument. Przeglądający (inspektor) to osoba mająca za zadanie odszukiwać problemy w referowanym dokumencie. Przeglądającym może być zarówno doświadczony tester, jak i niedoświadczony programista. Często na spotkanie przeglądowe zaprasza się osobę z niewielkim doświadczeniem lub spoza projektu, ze względu na jej „świeże spojrzenie”. Uwagi od takiej osoby często nakierowują bardziej doświadczonych członków zespołu na znajdowanie nowych defektów.

przeglądający, inspektor, kontroler (ang. reviewer, inspector, checker) – osoba zaangażowana w przegląd, która identyfikuje i opisuje odstępstwa w przeglądanym produkcie lub projekcie; przeglądający mogą być dobierani tak, aby reprezentować różne punkty widzenia i odgrywać różne role w procesie przeglądu Protokolant jest osobą zapisującą przebieg całego spotkania. Osoba pełniąca tę funkcję nie powinna odgrywać jednocześnie innych ról. Chodzi o to, aby zarówno autor, jak i przeglądający w pełni mogli skupić się na merytorycznej stronie przeglądu i nie rozpraszali swojej uwagi robieniem jakichkolwiek notatek. Przeglądy są spotkaniami bardzo wyczerpującymi, gdyż jego aktywni uczestnicy muszą przez cały czas zachować wysoki poziom koncentracji i skupienia. protokolant, skryba, rejestrator (ang. scribe, recorder) – osoba, która podczas spotkania przeglądowego rejestruje w dzienniku przeglądu każdy zgłoszony defekt lub sugestię odnośnie usprawnienia procesu; protokolant musi zapewnić, że dziennik przeglądu jest czytelny i zrozumiały Czasami można spotkać również role lektora oraz referenta. Zadaniem lektora jest odczytywanie treści analizowanego dokumentu. Praca referenta polega na parafrazowaniu fragmentów przeglądanego dokumentu podczas spotkania.

6.1.4. Aspekt psychologiczny przeglądów Przeglądy wymagają od uczestników nieustannego skupienia i nie pozwalają na analizę zbyt dużej ilości kodu na jednym spotkaniu. Dlatego muszą być przeprowadzane efektywnie, a nadzór nad ich sprawnym przebiegiem sprawuje moderator. Efektywność może być zaburzona w wyniku różnych mechanizmów psychologicznych. W takich sytuacjach moderator powinien reagować i sprowadzać dyskusję na właściwe tory. Oto przykłady niebezpieczeństw zaburzających poprawny przebieg przeglądu. Prawo Parkinsona, które mówi, że spotkanie zajmuje dokładnie tyle czasu, ile na nie przeznaczymy. Oznacza to, że jeśli prostą sprawę dającą się załatwić w kwadrans omawiać się będzie na spotkaniu trwającym godzinę, to załatwianie tej sprawy będzie trwało godzinę. Jeśli zaś

sprawa wymaga długich narad, ale czasu jest niewiele, to sprawa zwykle będzie załatwiona szybko. Dlatego, aby z jednej strony nie marnować czasu, a z drugiej nie zmuszać ludzi do pracy pod jego presją, należy dobrze wymierzyć czas spotkania przeglądowego. Często jest to zadanie trudne; dobra estymacja czasu spotkania wymaga od moderatora sporego doświadczenia. Rozwiązywanie

problemów

podczas

przeglądu



uczestnicy

przeglądów, w sytuacji wykrycia defektu w analizowanym kodzie lub innym dokumencie, często mają tendencję do natychmiastowego proponowania rozwiązania tego problemu. Moderator musi w takiej sytuacji szybko reagować i przypominać, że celem spotkania jest znajdowanie błędów, a nie debugowanie. Takie dyskusje są z punktu widzenia celów przeglądu stratą czasu i powodują zmniejszenie efektywności spotkania, gdyż na wykrycie kolejnych defektów pozostaje mniej czasu. Proces naprawy powinien następować po przeglądzie. Wyjątkiem są tu drobne, oczywiste błędy, które można poprawić „od ręki”. Napięcie na linii autor–przeglądający. Referowanie własnego kodu, zwłaszcza przed większą liczbą osób, na formalnym spotkaniu, często jest dużym stresem dla autora. Co więcej, uwagi przeglądających może on odbierać jako atak pod jego adresem. Moderator musi zadbać o to, aby wszyscy uczestnicy przeglądów mieli świadomość wspólnego celu spotkania, jakim jest wykrycie możliwie największej liczby defektów. Autor musi mieć poczucie, że proces ten nie ma nic wspólnego z ocenianiem jego umiejętności jako twórcy przeglądanego dokumentu. Powinien też traktować przegląd jako okazję do zwiększenia swoich umiejętności – przeglądającymi często są osoby z dużym doświadczeniem, których merytoryczne uwagi mogą być cennymi wskazówkami dla autora.

6.1.5. Typy przeglądów Istnieje kilka typów przeglądów. Różnią się one poziomem formalizacji oraz uczestnikami. Chociaż wszystkie mają na celu wykrywanie defektów, mają też inne cele, odmienne dla poszczególnych przeglądów.

Przegląd nieformalny to, jak sama nazwa wskazuje, najmniej sformalizowana forma testów statycznych. Przeglądy nieformalne przeprowadza się bardzo często, a ich uczestnicy mogą nawet nie być świadomi, że to, co robią jest przeglądem. Najczęściej polega on na tym, że np. jeden członek zespołu pyta drugiego o radę, prosi o pomoc w odszukaniu usterki w kodzie czy też o sprawdzenie poprawności gramatycznej jakiegoś tekstu. W przeglądach nieformalnych nie generuje się żadnych dodatkowych dokumentów ani raportów. Uczestnikami są zwykle autor oraz jeden lub kilku uczestników odgrywających rolę przeglądających. Przegląd nieformalny może dotyczyć dowolnego artefaktu, a o czasie jego przeprowadzenia oraz zakończenia decyduje autor. Zwykle przegląd ten odbywa się przy biurku autora. Przeglądy nieformalne są tanie i łatwe do przeprowadzenia. Powodzenie przeglądu zależy od doboru właściwych uczestników, ale trudno przecenić zyski płynące z tej formy testowania statycznego. przegląd nieformalny, przegląd ad hoc (ang. informal review, ad hoc review) – przegląd, który nie jest oparty na formalnej (udokumentowanej) procedurze Przejrzenie to forma przeglądu, w której prezentacja analizowanego dokumentu następuje krok po kroku. Taka forma prezentacji zmusza autora do określonego sposobu przedstawiania dokumentu i często skutkuje tym, że większość błędów jest wykrywanych przez samego autora podczas prezentacji. Głównym celem przejrzenia jest wykrywanie defektów, ale innym, nie mniej ważnym, celem jest ustanowienie wśród uczestników konsensusu co do sposobu rozumienia treści dokumentu. Po udanym przejrzeniu mamy pewność, że wszyscy rozumieją dany dokument w ten sam sposób. Przejrzenie może dotyczyć dowolnego dokumentu, jednak najczęściej jest nim kod lub projekt architektury systemu, gdyż wspólne, jednakowe rozumienie tych artefaktów przez wszystkich członków zespołu jest sprawą kluczową. Przejrzenia nie są zwykle bardzo formalne. Uczestnikami przejrzenia, oprócz autora, są wyłącznie przeglądający (zwykle do 6–7 osób). Przeglądający mogą przed spotkaniem otrzymać analizowany dokument, ale nie ma obowiązku przygotowania się przez zaznajomienie się z tym dokumentem przed spotkaniem. Po spotkaniu wszyscy uczestnicy powinni otrzymać nieformalny raport podsumowujący przejrzenie.

Jedną z form, jakie można przyjąć podczas przejrzenia jest tzw. zabawa w kompilator. Można ją stosować, gdy analizowanym dokumentem jest kod lub jego fragment. Uczestnicy „na sucho” wykonują kod programu, zapisując na tablicy stan wszystkich zmiennych. Takie przejrzenie można na przykład stosować, gdy autor jest świadomy istnienia usterki w kodzie, ale nie może jej znaleźć. Samo spotkanie nie powinno trwać dłużej niż 2 godziny. Jeśli zespół nie jest w stanie w takim czasie przejrzeć całego dokumentu, to należy wybrać jego najbardziej reprezentatywną część lub najistotniejszy fragment. przejrzenie, przejrzenie zorganizowane (ang. walkthrough, structured walkthrough) – przedstawienie przez autora, krok po kroku, dokumentu w celu zebrania informacji i ustalenia wspólnego rozumienia jego zawartości ([71], [75]) Przegląd techniczny jest przeglądem koleżeńskim i skupia się na osiągnięciu zgody, co do tego, jakie należy przedsięwziąć czynności techniczne wobec analizowanego dokumentu. Cechą charakterystyczną przeglądu technicznego jest etap podejmowania decyzji. Przedmiotem przeglądu technicznego może być każdy dokument techniczny (to znaczy niezwiązany bezpośrednio z kwestiami zarządczymi). W przeglądzie technicznym role są zwykle dość precyzyjnie określone. Osobą zarządzającą procesem przeglądu technicznego jest kierownik, którym nie może być autor. Samo spotkanie jest prowadzone przez moderatora. W spotkaniu uczestniczy również autor oraz przeglądający. Kierownik może odgrywać rolę moderatora. Skład osobowy nie powinien przekraczać 10 osób. Ważne jest, aby osoby biorące udział w przeglądzie technicznym zajmowały w organizacji stanowiska mniej więcej na tym samym poziomie. Zaleca się, aby w przeglądach technicznych nie brał udziału menedżer, gdyż ze względów psychologicznych oraz organizacyjnych dotyczących relacji menedżera z podwładnymi może to wpłynąć na niższą efektywność spotkania. Spotkanie jest zwoływane przez kierownika. Moderator przedstawia cel spotkania, po czym dokument jest przeglądany strona po stronie. Uczestnicy mogą zgłaszać uwagi, które powinny być notowane przez protokolanta lub przez samych przeglądających. Uczestnicy pod koniec spotkania powinni osiągnąć porozumienie dotyczące ewentualnych zmian w dokumencie.

O ile podczas przejrzenia autor referuje swój dokument, o tyle podczas przeglądu technicznego nie zabiera głosu. Może odpowiadać na pytania zadawane przez przeglądających, ale nie powinien przyjmować postawy obronnej – służy jako źródło dodatkowych informacji o dokumencie. Efektywność w znajdowaniu defektów w przypadku przeglądu technicznego jest zwykle wyższa niż w przypadku przeglądów nieformalnych czy przejrzeń. Spotkania przeglądowe są znakomitą okazją dla uczestników, aby uczyć się wzajemnie od siebie i dzielić doświadczeniem. przegląd techniczny (ang. technical review) – dyskusja w grupie współpracowników skupiająca się na osiągnięciu porozumienia w zakresie aspektów technicznych ([30], [71]) Przegląd kierowniczy (menedżerski) dotyczy Dokumenty poddawane przeglądowi kierowniczemu to:

kwestii

zarządczych.

plany związane z projektem (dotyczące harmonogramu, kosztorysu, zasobów, jakości, ryzyka itp.); plany odnoszące się do produktu (dotyczące bezpieczeństwa, utrzymywania systemu, instalacji, kopii zapasowych itp.); raporty (dotyczące postępu w pracach, incydentów, wyników innych przeglądów itp.); dokumenty prawne (plany umów, kontraktów, Servce Level Agreement itp.). Efektem przeglądu kierowniczego może być znajdowanie usterek w powyższych dokumentach, ale znacznie ważniejszym celem jest monitorowanie postępów projektu. Podczas spotkania można porównać plany z raportami i w ten sposób ocenić, czy projekt posuwa się naprzód w należytym tempie. Przeglądy menedżerskie zwykle odbywają się w okolicy kamieni milowych projektu. Wynikiem przeglądu kierowniczego są decyzje menedżerskie. Dotyczyć one mogą przydziału dodatkowych zasobów, realokacji środków, modyfikacji planów, dostosowania poziomu ryzyka itd. kamień milowy (ang. milestone) – punkt w czasie realizacji projektu, dla którego zostały określone (pośrednie) produkty oraz wyniki

W przeglądzie kierowniczym musi brać udział osoba uprawniona do podejmowania decyzji (np. menedżer), a także kierownik, przeglądający i

protokolant.

Przeglądającymi

powinni

być

udziałowcy

projektu

(ang.

stakeholders), a także osoby – zarówno z pionu menedżerskiego, jak i technicznego – odpowiedzialne za wprowadzanie podjętych na spotkaniu decyzji w życie. Organizacja przeglądu kierowniczego jest podobna do organizacji przeglądu technicznego i ma podobny stopień sformalizowania. Jedyna różnica polega na tym, że od uczestników przeglądu kierowniczego oczekuje się przygotowania przed spotkaniem tak, aby znali dobrze bieżący status projektu i orientowali się w postępach prac wytwórczych. Po spotkaniu tworzony jest raport opisujący listę rzeczy do zrobienia wraz ze wskazaniem osób odpowiedzialnych za wykonanie poszczególnych czynności. przegląd kierowniczy, przegląd menedżerski (ang. management review) – systematyczna ocena procesu zakupu, dostawy, wytworzenia, działania lub utrzymania oprogramowania wykonywana przez kierownictwo albo w jego imieniu, która monitoruje postępy, określa status planów i harmonogramów, zatwierdza wymagania oraz ich alokację, a także ocenia skuteczność metod zarządzania Inspekcja to bardzo formalny rodzaj przeglądu. Cały proces jej przeprowadzania (w tym określenie ról odgrywanych przez uczestników) musi być dokładnie zdefiniowany. Moderator oraz uczestnicy muszą być przeszkoleni w zakresie przeprowadzania inspekcji. Podstawą tego typu przeglądu jest zawsze konkretny materiał źródłowy, czyli tzw. dokumentacja bazowa. Inspektorzy szukają określonych typów problemów, często z wykorzystaniem tzw. listy kontrolnej na użytek inspekcji. W ciągu całego procesu zbiera się odpowiednie metryki, które są poddawane dalszej analizie. Tak dokładne i formalne zdefiniowanie ról, obowiązków i metryk ma głębokie uzasadnienie, które staje się jasne, gdy spojrzymy na dwa podstawowe cele inspekcji. Pierwszy z nich to znajdowanie defektów w produkcie, a drugi to znajdowanie defektów w procesie. Cele te można również zdefiniować bardziej ogólnie, jako ulepszanie produktu oraz ulepszanie procesu. Dzięki użyciu dobrze zdefiniowanych metryk jesteśmy w stanie ulepszać sam proces wytwarzania oprogramowania oraz kontrolować jego jakość. Lepszy proces wpływa

pozytywnie na jakość końcową produktu oraz w kolejnych projektach. Uczestnicy inspekcji odgrywają następujące role:

produktów

tworzonych

kierownik inspekcji; autor; inspektorzy; moderator; protokolant. Kierownik jest odpowiedzialny za przeprowadzenie całego procesu inspekcji. Do jego zadań należy uformowanie zespołu inspekcyjnego, zaplanowanie czynności, monitorowanie i nadzór procesu inspekcji, podejmowanie działań korygujących, jeśli zajdzie taka potrzeba, to weryfikacja spełnienia kryteriów wyjścia inspekcji, zamykanie inspekcji, informowanie udziałowców o wynikach inspekcji, branie aktywnego udziału w ulepszaniu procesu. Często kierownik odgrywa również rolę moderatora i bierze udział w spotkaniach inspekcyjnych. Kierownik musi być osobą przeszkoloną nie tylko w zakresie samego procesu inspekcji, lecz także w takich obszarach, jak: plan metryk w organizacji, metodyki ulepszania procesów, statystyczne metody kontroli procesu, czy też polityka przeprowadzania inspekcji w danej organizacji. W inspekcji muszą być dobrze zdefiniowane kryteria wejścia i wyjścia, to znaczy musi być wiadome, kiedy można rozpocząć przeprowadzanie inspekcji i kiedy można ten proces uznać za zakończony. Osobą odpowiedzialną za weryfikację spełnienia tych kryteriów jest kierownik inspekcji. Przykładowe kryteria wejścia: dostępność odpowiednich list kontrolnych; dokumenty bazowe przeszły inspekcję i mają dopuszczalny poziom pozostałych defektów; dokument poddawany gramatycznej.

inspekcji

przeszedł

kontrolę poprawności

Przykładowe kryteria wyjścia: wszystkie wykryte poważne defekty zostały usunięte; dokument poddany inspekcji ma dopuszczalny poziom defektów;

wszystkie wymagane raporty odpowiednich osób.

zostały

napisane i

rozesłane do

Inspekcja Fagana składa się z sześciu podstawowych faz przedstawionych na rysunku 6.2. W fazie planowania jest ustalany skład zespołu inspekcyjnego, są przydzielane role oraz jest estymowany czas wymagany na przegląd dokumentu. Dobór właściwych inspektorów jest kluczowy dla powodzenia całego procesu. Muszą oni mieć odpowiednie umiejętności, doświadczenie, znajomość biznesu i być dostępni w czasie trwania inspekcji.

Rysunek 6.2. Podstawowy model inspekcji Fagana W zależności od rozmiaru dokumentu i stopnia jego skomplikowania wylicza się, jak dużo elementów (linie kodu, strony dokumentacji, wymagania itp.) może być poddanych inspekcji na jednostkę czasu. Na podstawie tych obliczeń planuje się odpowiednią ilość czasu na inspekcję, w szczególności na spotkanie inspekcyjne. Można przeprowadzić również analizę ryzyka i zdecydować, czy inspekcji ma być poddawany dokument w całości, czy też tylko jakaś jego część. Jeśli dokument ma duży rozmiar, to można podzielić go na fragmenty i dla każdego z nich zaplanować osobną sesję inspekcyjną. Kierownik musi w fazie planowania określić, jakie metryki będą zbierane podczas inspekcji. W szczególności wśród tych metryk powinny się znaleźć: miary objętości dokumentów; liczba znalezionych defektów, sklasyfikowanych wg stopnia dotkliwości; koszt poprawy;

ich

czas poświęcony na poszczególne czynności. Faza przeglądu obejmuje szkolenie, przypisanie ról inspekcyjnych, dystrybucję materiałów do uczestników oraz ustalenie harmonogramu prac, w szczególności terminu spotkania inspekcyjnego. Kierownik musi mieć pewność, że wszyscy uczestnicy inspekcji będą w tym czasie dostępni oraz że każdy z nich rozumie dobrze swoją rolę w procesie. Szkolenie może dotyczyć samej inspekcji, ale również dokumentów poddawanych inspekcji. Jeśli uczestnicy inspekcji są doświadczeni, to nie ma konieczności organizowania spotkania w fazie przeglądu – przypisanie ról następuje indywidualnie, a materiał jest wysyłany bezpośrednio do inspektorów bez przeprowadzania żadnych szkoleń. Faza przygotowania uczestników jest kluczową fazą inspekcji. To w tej fazie następuje właściwe testowanie dokumentu poddawanego kontroli. Każdy inspektor przegląda indywidualnie dokument, pamiętając o wykonaniu standardowych czynności tej fazy, takich jak: zapisanie czasu rozpoczęcia fazy; zapisanie wszystkich uwag, pytań i wątpliwości co do przeglądanego dokumentu; wykorzystanie czasu przeznaczonego na przygotowanie (nie więcej i nie mniej), aby zachować tempo sprawdzania poszczególnych elementów dokumentu; zliczanie napotkanych problemów. Problemy znalezione w czasie fazy przygotowania powinny być przypisane do jednej z trzech kategorii: poważne; mniej istotne; niejasne. Problem o statusie „niejasny” oznacza, że inspektor nie może do końca przypisać konkretnej kategorii i wymaga to uzyskania dodatkowych informacji przez autora. Koncepcja roli inspektora bierze się z faktu, że ludzie szukają tego, co chcą znaleźć. Jeśli każemy inspektorom skupić się na wykrywaniu błędów określonego typu (stąd powszechne stosowanie list kontrolnych w inspekcjach), to inspektor

będzie analizował dokument tylko pod tym kątem i szanse na znalezienie defektów danego typu się zwiększą. Jeśli inspektor nie będzie miał konkretnych wytycznych, to jego efektywność może być o wiele mniejsza. Hass [35] wymienia następujące typy wytycznych dla roli inspektora: lista kontrolna – inspektor przegląda dokument pod kątem zadań lub problemów opisanych w liście kontrolnej; dokumenty – inspektor sprawdza zgodność między dokumentami; skupienie – inspektor szuka w dokumencie konkretnego problemu, np. związanego z użytecznością, aspektem finansowym, użyciem odpowiedniego słownictwa; perspektywa – inspektor analizuje dokument, wchodząc w rolę przyszłego użytkownika tego dokumentu, np. projektanta, programisty lub testera; ważne jest, aby inspektor w codziennej pracy odgrywał tę samą rolę lub dobrze ją rozumiał; procedura – inspektor sprawdza dokument, podążając za określoną procedurą; scenariusz – inspektor analizuje dokument pod kątem konkretnego procesu dla konkretnej roli; jest to rola bardziej szczegółowa od perspektywy; standard – inspektor sprawdza, czy dokument jest zgodny z danym standardem, np. ISO, IEEE, standardami dziedzinowymi lub wewnętrznymi stosowanymi w organizacji; punkt widzenia



inspektor

przegląda

dokument

jako

osoba

odgrywająca inną rolę, np. użytkownik, analityk, tester, projektant; jest to rola podobna do perspektywy, przy czym jako punkt widzenia są wykorzystywane odpowiednie dokumenty bazowe. Celem fazy spotkania inspekcyjnego jest zebranie wszystkich uwag od inspektorów i przedyskutowanie ich. Spotkanie takie może się wydawać niepotrzebne wobec faktu wykrycia defektów w fazie poprzedniej, ale praktyka pokazuje, że na spotkaniach inspekcyjnych wykrywa się dodatkowo 10–20% więcej usterek. Jeśli jednak inspektorzy znaleźli niewielką liczbę problemów, to spotkanie inspekcyjne może zostać pominięte. Rola moderatora na spotkaniu inspekcyjnym jest taka sama jak w każdej innej formie przeglądu. Ma za zadanie trzymać uczestników „w ryzach”, pilnować, aby

dyskusja była merytoryczna, skupiona na problemach, a nie sposobach ich rozwiązywania oraz zapobiegać konfliktom pojawiającym się na skutek obecności osób o tzw. niskich umiejętnościach miękkich. Moderator zbiera od inspektorów wyniki ich prac z fazy przygotowania, w szczególności ich nazwiska oraz sumaryczną liczbę znalezionych problemów. Następnie moderator odczytuje dokument element po elemencie. Inspektorzy opisują jakie znaleźli problemy w danym fragmencie dokumentu. Dyskusja powinna być skoncentrowana na problemach poważnych oraz niejasnych. Problemy mniej istotne mogą być przekazane protokolantowi później, gdyż nie wymagają zwykle tak dużej uwagi jak pozostałe. W przypadku, gdy podczas spotkania zostaną znalezione nowe problemy, muszą być one zaraportowane i opisane. Pozwoli to między innymi na obliczenie pewnego aspektu efektywności inspekcji, polegającego na zdolności do wykrywania nowych błędów. Faza zmiany to etap, w którym następuje naprawa defektów znalezionych w fazach przygotowania oraz spotkania inspekcyjnego. Naprawy dokonuje zwykle autor. Jeśli znalezione błędy dotyczą dokumentów, których zespół nie może zmieniać samodzielnie, to błędy te powinny zostać przekazane osobom mającym takie uprawnienia (np. błąd znaleziony w strategii testowej może być zmieniony tylko przez kierownika testów). Gdy wszystkie cykle inspekcyjne zostaną zakończone, a kryteria wyjścia spełnione, proces przechodzi do fazy kontynuacji (ang. follow-up). W fazie tej kierownik inspekcji zbiera i opracowuje wszystkie pomiary dokonane podczas całego procesu. Może też oszacować na ich podstawie, wykorzystując też dane historyczne, liczbę pozostałych, niewykrytych problemów w kontrolowanym dokumencie. Dokumentuje też wszystkie pomysły na poprawę procesu w przyszłości. Inspekcje są drogie, ale opłacalne. Na rysunku 6.3 przedstawiono porównanie typowych nakładów pracy w dwóch projektach, z których jeden wykorzystuje inspekcje, a drugi nie. Z wykresu widać, że w początkowej fazie nakłady pracy w projekcie z inspekcjami są wyższe. Jest to zrozumiałe, gdyż koszt inspekcji jest zwykle duży: inspekcje wymagają

Rysunek 6.3. Porównanie nakładu pracy w projekcie z inspekcjami i bez nich czasu na przygotowanie, spotkania, tworzenie dokumentacji, być może także szkolenia. Jednak inwestycja ta zwraca się w końcowych fazach projektu. Dzięki stosowaniu inspekcji już we wczesnych fazach następuje wykrycie dużej liczby usterek, przez co nie trzeba tracić czasu na wyszukiwanie i eliminowanie defektów w późniejszym etapach. Ponadto należy pamiętać, że dzięki inspekcji usprawniamy sam proces, co jest inwestycją także w jakość przyszłych projektów. Lista korzyści z przeprowadzania inspekcji jest długa. Sam Fagan [76] wymienia następujące: redukcja błędów (defektów) polowych użytkowników po wydaniu oprogramowania);

(zgłaszanych

przez

zwiększona satysfakcja klienta; poprawa produktywności, zmniejszenie całkowitych nakładów pracy (dostarczenie większej ilości funkcji w danym czasie lub redukcja czasu do wydania); ulepszenie harmonogramów spotkań; wymiana doświadczeń między deweloperami; proces ciągłej poprawy przez usuwanie defektów systemowych, które są źródłem defektów w produkcie;

szybka nauka unikania błędów podczas pisania kodu przez programistów, którzy uczestniczą w inspekcji swojego oraz cudzego kodu; budowanie zgranego zespołu; w niektórych przypadkach inspekcje pozwalają wyeliminować testy jednostkowe. inspekcja (ang. inspection) – rodzaj przeglądu koleżeńskiego polegający na wizualnej weryfikacji dokumentów w celu wykrycia defektów, np. niezgodności ze standardami projektowymi lub dokumentacją wyższego poziomu; jest to najbardziej formalna technika przeglądu, zawsze oparta na udokumentowanej procedurze ([7], [71]) Audyt to najbardziej formalna technika testowania statycznego. Jest przeprowadzany przez audytorów, przeszkolonych w zakresie przeprowadzania tego typu kontroli. Audyt może być zewnętrzny lub wewnętrzny. W tym drugim przypadku audytorem jest pracownik audytowanej organizacji, ale zewnętrzny wobec zespołu wytwórczego. Audyty przeprowadza się zwykle pod koniec cyklu wytwórczego oprogramowania. Audyt powinien stanowić wartość dodaną dla organizacji przez dostarczenie następujących informacji (stanowiących jednocześnie cele audytu) [77]: czy wykorzystywane w organizacji standardy, procesy, systemy i plany są zgodne z polityką firmy, jej wymaganiami oraz celami; czy pracownicy organizacji prawidłowo wykorzystują te dokumenty podczas wykonywania swojej pracy; jaka jest efektywność wykorzystania tych dokumentów przy osiąganiu założonych celów; czy zasoby (ludzie, wykorzystywane;

sprzęt,

infrastruktura

itp.)



efektywnie

czy wytwarzane produkty są zgodne z wymaganą specyfikacją i standardami. W audytach można wyróżnić kilka ról: klient – osoba lub organizacja zamawiająca audyt;

kierownictwo audytora – odpowiedzialne za przypisanie głównego audytora (ang. lead auditor) do audytu oraz współpraca z nim w zakresie tworzenia zespołu audytorów; główny audytor (kierownik audytu) – osoba odpowiedzialna za zaplanowanie i przeprowadzenie audytu, tworzenie planów audytu, zarządzanie zespołem audytorów, podejmowanie decyzji dotyczących prac

audytorów,

komunikację

z

klientem,

organizację

z pracownikami organizacji, przegląd wyników koordynację i kontrolę wykonania akcji naprawczych;

spotkań

audytu

oraz

audytorzy (ang. auditors) – osoby przygotowujące i przeprowadzające wspólnie z głównym audytorem audyt, zbierające informacje, oceniające zebrane informacje, uczestniczące w spotkaniach audytowych oraz raportujące do głównego audytora wyniki swojej pracy; kierownictwo organizacji audytowanej (ang. auditee management) – współpracuje z głównym audytorem, informuje pracowników organizacji o mającym być przeprowadzonym audycie, udostępnia audytorom niezbędne dokumenty oraz zasoby (w tym ludzkie), dostarcza informacji niezbędnych audytorom do wykonania swojej pracy, upewnia się, czy akcje naprawcze zostały poprawnie wykonane i dostarcza na to dowodów głównemu audytorowi; audytowani (ang. auditee) – pracownicy audytowanej organizacji, z którymi audytorzy przeprowadzają wywiady, zbierając różnego rodzaju informacje. Audytowani są zobowiązani do dostarczenia wszystkich niezbędnych dla audytorów danych. Audytor w swojej pracy często wykorzystuje tzw. ścieżkę audytu, czyli śledzi wstecz krok po kroku czynności, jakie powinny zostać wykonane w ramach danego procesu i ocenia ich poprawność na każdym etapie. Na przykład rozpoczynając od analizy raportu z testów, audytor może cofnąć się do etapu wykonywania testów, następnie przeanalizować dokumentację projektową testów, aby na samym końcu przejść do analizy wymagań, na podstawie których testy te zostały zaprojektowane. ścieżka audytu (ang. audit trial) – ścieżka wstecz, wzdłuż której śledzi się oryginalne wejście do procesu (np. dane), rozpoczynając od wyjścia procesu

jako punktu startu; ułatwia to analizę defektów i umożliwia przeprowadzenie audytu procesu Wynikiem audytu jest raport, który może zawierać obserwacje, uwagi, wnioski, sugestie poprawy co do audytowanego dokumentu lub procesu. Zawiera także ostateczną decyzję (tak/nie) dotyczącą tego, czy kontrolowany obiekt jest zgodny z odpowiednimi normami bądź przepisami. Wadą audytu jest jego koszt oraz fakt, że mimo najwyższego stopnia formalizacji jest to najmniej efektywna technika statyczna, jeśli chodzi o wykrywanie problemów. Czasami konieczność przeprowadzenia audytu wynika z zapisów kontraktu lub z przepisów prawa. audyt (ang. audit) – niezależna ocena oprogramowania lub procesów w celu ustalenia zgodności ze standardami, wytycznymi, specyfikacjami oraz/lub procedurami, oparta na obiektywnych kryteriach, wliczając dokumenty, które określają: 1) postać lub zawartość produkowanego produktu; 2) proces, według którego produkt powinien być produkowany; 3) sposób pomiaru stosowania się do standardu lub specyfikacji [71]

6.1.6. Biznesowa wartość przeglądów Zysk z przeprowadzenia przeglądów można wyrazić za pomocą konkretnych wartości liczbowych. W tabeli 6.2 jest przedstawiona przykładowa symulacja kosztów testowania i usuwania defektów dla projektu w dwóch wersjach: z inspekcją i bez inspekcji. Tabela 6.2. Prosta analiza kosztów i zysków z przeprowadzenia przeglądu

symulacja (ang. simulation) – odwzorowanie wybranych charakterystycznych zachowań jednego fizycznego lub abstrakcyjnego systemu przez inny system [78] Taka analiza bardziej przemawia do wyobraźni niż omówiony wykres z rysunku 6.3. Rozważmy dwie wersje tego samego projektu i w celu uproszczenia skupmy się na fazie wymagań, traktując pozostałe etapy projektu jako jedną fazę. Projekty różnią się tym, że w pierwszym nie przeprowadzono inspekcji wymagań, a w drugim to uczyniono. Druga kolumna tabeli przedstawia symulacje dla projektu nr 1. Zakładając, że w fazie wymagań wprowadzono łącznie 400 defektów i w tej samej fazie znaleziono ich 280, efektywność usuwania defektów w fazie wymagań wynosi 280/400 = 70%. Koszty inspekcji są zerowe, gdyż jej nie przeprowadzono. Wiedząc, że koszt usunięcia 280 defektów wynosił 15 000 USD, możemy obliczyć koszt usunięcia jednego błędu równy ok. 53,5 USD, dzieląc 15 000 przez 280. W pozostałych fazach znaleziono 100 spośród pozostałych 120 błędów, co daje efektywność usuwania defektów w fazach późniejszych równą 100/200 ≈ 83%. W drugim projekcie przeprowadzono inspekcję wymagań, której koszt wyniósł 400 USD. Efektywność usuwania defektów w inspekcji szacujemy, stosując dane

historyczne lub przemysłowe (np. raport Capersa Jonesa [70]) na 85%. Oznacza to, że w wyniku inspekcji znajdziemy 0,85 ⋅ 400 = 340 błędów. Używając wskaźnika kosztu usuwania pojedynczego błędu dla projektu nr 1, obliczamy koszt usunięcia tych defektów jako 340 ⋅ 53,5 USD = 18 190 USD. Używając wskaźnika efektywności usuwania defektów w pozostałych fazach dla projektu nr 1, obliczamy, że w pozostałych fazach wykrytych zostanie 0,83 ⋅ 60 ≈ 50 defektów, których koszt usunięcia wyniesie 50 ⋅ 400 USD = 18 190 USD. Sumując koszty inspekcji oraz usunięcia defektów w fazie wymagań i w fazach następnych, dostajemy 55 000 USD w przypadku projektu bez inspekcji i 42 190 USD w przypadku projektu, w którym inspekcję przeprowadzono. Koszt w tym ostatnim projekcie jest o 23% mniejszy niż w projekcie 1. Ponadto mamy dwa razy mniej defektów polowych (10 wobec 20 w projekcie nr 1). Efektywność inspekcji widać jeszcze wyraźniej, gdy doliczy się koszty usunięcia błędów polowych, które zwykle są o wiele większe niż koszty wykrywania błędów przed wydaniem oprogramowania. Koszt projektu nr 2 może być nawet o 50% mniejszy niż koszt projektu nr 1.

6.1.7. Wdrażanie przeglądów Przeglądy nie są tanie i kierownictwo nie zawsze jest chętne do przeznaczania cennych zasobów (czas, ludzie) do ich wykonania. Aby skutecznie przeprowadzać przeglądy w organizacji, należy wykonać kilka kroków opisanych poniżej. Dobrze jest również opracować plan przeglądów, zawierający w szczególności informacje o tym, co ma być poddane przeglądowi, kto ma uczestniczyć w przeglądzie i jakie są kryteria wejścia i wyjścia. plan przeglądu (ang. review plan) – dokument opisujący podejście, zasoby i harmonogram zamierzonych czynności związanych z przeglądem; identyfikuje on m.in. dokumenty i kod podlegający przeglądowi, typy przeglądów do wykorzystania, uczestników, kryteria wejścia i wyjścia, które będą stosowane w przeglądach formalnych oraz uzasadnienie ich wyboru; jest zapisem procesu planowania przeglądu Zdobyć wsparcie kierownictwa i zademonstrować korzyści wynikające z przeglądów. Należy uświadomić kierownictwu, że przeprowadzenie przeglądu się opłaci. W tym celu najlepiej przedstawić tzw. business-case, np. w formie

podobnej do tej w punkcie 6.1.6. Tego typu symulacje, oparte na konkretnych liczbach, bardzo mocno przemawiają do wyobraźni i pozwalają lepiej zrozumieć korzyści płynące z inwestycji w przeglądy. Kierownictwo powinno także rozumieć długookresowe korzyści z przeprowadzania przeglądów. Stosowanie metryk i wykorzystanie danych historycznych jest bardzo dobrym sposobem na przedstawienie kierownictwu tego typu analiz. Kierownictwo musi mieć wystarczające i dokładne informacje o kosztach, korzyściach i potencjalnych problemach we wdrażaniu przeglądów. Wybrać i opisać procedury przeprowadzania przeglądów. Aby proces przeprowadzania przeglądów przebiegał płynnie i bezproblemowo, musimy mieć opisane wszystkie procedury postępowania, a także wszystkie dokumenty, formularze i wzory raportów, które będą wykorzystywane podczas przeglądu. Należy również zadbać o odpowiednią infrastrukturę, np. wspólne repozytorium zawierające pomiary metryk, takich jak liczba znalezionych błędów, liczba uczestników poszczególnych spotkań, czas trwania przeglądu, efektywność przeglądu. Dane te będą w przyszłości pełniły niezwykle cenną funkcję w szacowaniu kosztów i zysków kolejnych przeglądów. W przypadku inspekcji dane te będą również służyły do oceny poprawy procesu (np. jeśli zauważymy utrzymującą się na tym samym poziomie efektywność inspekcji przy jednoczesnym uczestnictwie mniejszej liczby osób lub krótszym czasie inspekcji, to możemy wnioskować, że jakość procesu wzrosła). Przeprowadzić niezbędne szkolenia. Szkolenia są istotne w przypadku najbardziej sformalizowanych przeglądów, takich jak inspekcje. Szkolenia powinny obejmować zakres czynności wchodzących w skład danego przeglądu, opis poszczególnych ról, jak i techniki pracy poszczególnych uczestników. Uzyskać wsparcie osób uczestniczących w przeglądach. Często zdarza się, że członkowie zespołu sceptycznie podchodzą do technik testowania statycznego. Niektórzy programiści mogą uważać, że np. przejrzenie lub inspekcja nie ma sensu, skoro kod i tak jest przetwarzany przez kompilator, który wykonuje pewne elementy analizy statycznej. Należy uświadomić sceptykom, że przeglądy mogą wykrywać błędy trudne lub niemożliwe do automatycznego wykrycia, takie jak błędy wymagań. Przeglądy mają też olbrzymi walor edukacyjny, ponieważ każdy uczestnik przeglądu dzieli się swoim doświadczeniem z innymi. Przeprowadzić pilotażowe przeglądy. Jeśli w organizacji nie stosowano do tej pory metod testowania statycznego, to można przeprowadzić próbne pilotażowe

przeglądy. Mają one dwa zasadnicze cele. Pierwszy to zapoznanie członków zespołu z tą formą testowania i przyzwyczajenie ich do określonych czynności, które muszą być wykonywane np. podczas spotkań przeglądowych. Członkowie zespołu mogą wdrożyć się w swoje role i w kolejnych przeglądach wykonywać je efektywnie. Drugi cel to znajdowanie potencjalnych problemów w przeprowadzeniu samego procesu. Może się okazać, że np. spotkania trwają zbyt długo, autor czuje się atakowany, uczestnicy nie poświęcili odpowiednio dużo czasu na przygotowanie się do spotkania. Dzięki przeglądom pilotażowym można te problemy rozpoznać i wyeliminować lub przynajmniej zminimalizować ich oddziaływanie w kolejnych przeglądach. Zastosować przeglądy do najważniejszych dokumentów. Przeglądy powinny być efektywne w sensie znajdowania problemów. Niestety nie są one efektywne w sensie swoich „mocy przerobowych”. Dlatego należy zastosować odpowiednią priorytetyzację artefaktów i stosować przeglądy do dokumentów kluczowych, najważniejszych, takich, w których niewykryte błędy mogą nas w przyszłości dużo kosztować. Przykładami takich artefaktów są: wymagania, umowy, plany testów, kod źródłowy kluczowych bądź krytycznych modułów.

6.1.8. Kryteria sukcesu przeglądów Na sukces przeglądu składa się wiele czynników. Sylabus ISTQB [79] wyróżnia następujące czynniki sukcesu w kontekście przeglądów: Czynniki techniczne sukcesu upewnić się, że proces przeglądu jest stosowany poprawnie, zwłaszcza w przypadku przeglądów formalnych takich jak inspekcje; zapisywać koszty przeglądu oraz osiągane korzyści; przeglądać wczesne wersje robocze lub fragmenty dokumentów, aby zidentyfikować wzorce defektów, zanim zostaną wprowadzone w cały dokument; upewnić się, że dokument poddawany kontroli jest gotowy do przeglądu; używać odpowiednich list kontrolnych dla najczęściej spotykanych defektów (w każdej organizacji i w każdym typie projektu mogą to być inne defekty); używać wielu typów przeglądu, w zależności od założonego celu; poddawać przeglądom najważniejsze dokumenty;

namawiać do znajdowania najważniejszych błędów – skupiać się na treści, a nie na formie; ciągle ulepszać proces prowadzenia przeglądu. Czynniki organizacyjne sukcesu upewnić się, że kierownictwo przydzieliło odpowiednią ilość czasu na czynności związane z przeglądami, nawet pod presją terminów; pamiętać, że czas i koszt przeglądów nie są proporcjonalne do znalezionych defektów; zapewnić odpowiednią ilość czasu na poprawianie znalezionych defektów; nigdy nie używać metryk z przeglądów do oceny indywidualnej efektywności ludzi; zapewnić, aby właściwi ludzie byli przypisani do odpowiednich ról i rodzajów przeglądów; organizować formalnych;

szkolenia,

zwłaszcza

w

przypadku

przeglądów

wspierać forum przeprowadzających przeglądy, dzielić się pomysłami i doświadczeniem; upewnić się, że każdy aktywnie uczestniczy w przeglądach; zapewnić wyważony zespół przeglądających oraz mieć na uwadze, że jego uczestnicy mają różne umiejętności i doświadczenie; wspierać działania zmierzające do usprawniania procesu tak, żeby zająć się problemami systemowymi; pokazywać ulepszenia osiągnięte dzięki przeglądom. Czynniki ludzkie sukcesu nauczyć interesariuszy, aby spodziewali się wykrycia i przeznaczyli czas na ich usunięcie oraz ponowny przegląd;

defektów

zagwarantować, aby przegląd był pozytywnym doświadczeniem dla autora; traktować znajdowanie defektów jako coś pożądanego w atmosferze wolnej od obwiniania; zapewnić konstruktywny, pomocny i obiektywny ton komentarzy, unikać subiektywizmu, być profesjonalnym w komunikacji;

nie przeprowadzać przeglądu, jeśli autor tego nie chce lub nie jest do tego przekonany; zachęcać wszystkich do głębokiego przemyślenia najważniejszych aspektów przeglądanych dokumentów.

6.2. Analiza statyczna Analiza statyczna polega na analizowaniu kodu lub architektury systemu bez uruchamiania testowanej aplikacji. Technika ta jest najczęściej stosowana przy użyciu narzędzi. W obszarze metod analizy statycznej kodu można wyróżnić: analizę przepływu sterowania; analizę poprawności sekwencji operacji; analizę przepływu danych; testowanie zgodności ze standardami programowania; generowanie metryk kodu; formalne dowodzenie poprawności; symboliczne wykonanie kodu. W obszarze metod analizy statycznej architektury można wyróżnić: analizę statyczną strony internetowej; analizę grafów wywołań. Modele służące do przeprowadzania analizy statycznej (np. model przepływu sterowania czy danych) są także wykorzystywane w testach dynamicznych, zwłaszcza przy definiowaniu kryteriów pokryć w technikach białoskrzynkowych omówionych w rozdziale 9. analiza statyczna kodu (ang. static code analysis) – analiza kodu źródłowego przeprowadzona bez wykonywania oprogramowania

6.2.1. Analiza przepływu sterowania Analiza przepływu sterowania to metoda analizy kodu pod kątem kolejności wykonywania instrukcji. Może być przeprowadzana bezpośrednio na kodzie lub

wykorzystywać model, np. graf przepływu sterowania opisany w punkcie 5.4.1. Typowe problemy znajdowane w trakcie analizy przepływu sterowania to: znajdowanie martwego kodu; znajdowanie funkcji, które nie są wywoływane. Fakt istnienia zarówno martwego kodu, jak i niewywoływanych funkcji może być wynikiem błędu programisty, ale najczęściej wynika z wielokrotnego wykorzystywania tego samego kodu czy modułów. Jeśli projekt jest bardzo silnie oparty na reużywalnych komponentach bądź fragmentach kodu, dobrze jest stosować analizę statyczną.

1 function f(x) 2 y:=x+1 3 if (y>x) then 4 y:=y*y 5 else 6 y:=y/2 7 end Listing 6.1. Przykład programu z martwym kodem Rozważmy przykład programu z listingu 6.1. Zauważmy, że warunek w linii 3. będzie zawsze spełniony, w wyniku czego instrukcja w linii 6. nigdy nie będzie wykonana. Linia 6. stanowi więc martwy kod. martwy kod, nieosiągalny kod (ang. dead code, unreachable code) – kod, który nie może być osiągnięty przez żaden przepływ sterowania i dlatego nie może być wykonywany W przypadku prostych sytuacji, podobnych do tej z listingu 6.1, martwy kod może być znaleziony przy użyciu narzędzia, a nawet przez kompilator podczas kompilacji. Do bardziej skomplikowanych programów można użyć wykonania symbolicznego opisanego w punkcie 6.2.8.

6.2.2. Poprawność sekwencji operacji

Modele takie jak graf przepływu sterowania mogą być użyte do sprawdzenia poprawności sekwencji wywołań operacji. Na przykład, wiele obiektów abstrakcyjnych typów danych musi być zainicjalizowanych przed ich użyciem, plik musi być otwarty przed dokonaniem jakiejkolwiek operacji na nim. Rozważmy abstrakcyjną strukturę danych kolejki z ograniczoną pojemnością z następującymi operacjami: CreateQueue(Q, n) – operacja tworząca kolejkę Q o pojemności n elementów; Enqueue(Q, e) – operacja wstawiająca do kolejki Q element e; Dequeue(Q) – operacja pobierająca element z kolejki Q; RemoveQueue(Q) – operacja usuwająca kolejkę Q z pamięci. Dla tak zdefiniowanego ograniczenia:

typu

danych

mamy

następujące

naturalne

1. Enquque, Dequeue i RemoveQueue dla Q nie mogą zostać wykonane, zanim nie zostanie wykonane CreateQueue dla Q; 2. po wykonaniu RemoveQueue(Q) nie można wykonać Enqueue, Dequeue ani RemoveQueue dla Q, chyba że wcześniej ponownie wywołane zostanie CreateQueue dla Q; 3. Dequeue nie powinno być wywoływane na pustej kolejce; 4. Enqueue nie powinno być wywoływane dla kolejki zawierającej maksymalną liczbę elementów.

Rysunek 6.4. Graf przepływu sterowania dla programu wykorzystującego kolejkę z ograniczoną pojemnością Rozważmy graf przepływu sterowania, przedstawiony na rysunku 6.4, dla programu wykorzystującego tak zdefiniowaną kolejkę. Widzimy, że metody CreateQueue, Enqueue, Dequeue oraz RemoveQueue są wywoływane odpowiednio w wierzchołkach 1, 2, 6 i 4. Zakładamy, że wszystkie dotyczą jednej i tej samej kolejki Q. Wykorzystując ten graf, możemy przeprowadzić analizę statyczną pod kątem poprawności wywoływania operacji na kolejce. Zauważmy, że występuje tu kilka błędów lub co najmniej podejrzanych miejsc, wymagających szczegółowego sprawdzenia.

pętla (1, 2, 1) może wykonać się więcej razy niż pojemność kolejki, co naruszy zasadę nr 4; należy sprawdzić, czy warunek pętli zapewnia, że dla pełnej kolejki z wierzchołka 1 można przejść wyłącznie do 3; jeśli sterowanie przejdzie od razu ścieżką (1, 3, 6), to zostanie naruszona zasada nr 3 (pobranie elementu z pustej kolejki); należy się upewnić, że krawędzią (1, 3) możemy przejść wyłącznie w sytuacji, gdy kolejka nie jest pusta; pętla (3, 6, 7, 3) może zostać wykonana więcej razy niż liczba elementów w kolejce naruszając ponownie zasadę nr 3; wykonanie ścieżki (4, 7, 3, 6) naruszy zasady nr 1 i 2.

6.2.3. Analiza przepływu danych Analiza przepływu danych wykorzystuje model przepływu danych opisany w punkcie 5.4.3. Dotyczy wykrywania problemów podobnych do niepoprawnych sekwencji wywołań opisanych w poprzednim podrozdziale, przy czym rozważanymi operacjami są użycie, definicja oraz zabicie zmiennej, zapisywane skrótowo przy użyciu liter „d” (ang. definition), „u” (ang. use) oraz „k” (ang. kill). Analiza taka zawsze dotyczy konkretnej zmiennej. W tabeli 6.3 opisano wszystkie możliwe następstwa operacji d, u, k. Jeśli w grafie przepływu danych występuje niepożądane następstwo operacji (np. dk, dd, -u), to prawdopodobnie w kodzie jest defekt, a co najmniej kod wymaga analizy i być może refaktoryzacji. Tabela 6.3. Możliwe typy następstw operacji na zmiennych

T yp Uwagi -d

pierws za definicja zmiennej, normalna s ytuacja

-u

użycie bez definicji – możliwy defekt (ale może też dotyczyć zmiennej g lobalnej)

-k

zabicie bez definiowania – prawdopodobnie defekt

d-

prawdopodobnie defekt – być może martwa zmienna

u-

os tatnia akcja to użycie – dopus zczalne (choć nie ma zabicia)

k-

os tatnia akcja to zabicie zmiennej – normalna s ytuacja

dd

redefinicja zmiennej bez użycia – prawdopodobnie defekt

du

definicja a potem użycie – normalna s ytuacja

dk

prawdopodobnie defekt – zmienna po zdefiniowaniu nie była użyta

ud

redefinicja zmiennej po użyciu – normalna s ytuacja

uu

dwukrotne użycie zmiennej – normalna s ytuacja

uk

zabicie zmiennej po użyciu – normalna s ytuacja

kd

powtórna definicja po zabiciu – dopus zczalne; niektórzy uznają to za defekt

ku

użycie po zabiciu – poważny defekt

kk

powtórne zabicie zmiennej – defekt

Następstwo operacji dla danej zmiennej zawsze dotyczy wykonywalnej sekwencji instrukcji I1, I2, …, In takich, że pierwsza operacja występuje w I1, druga w In i między nimi nie występuje żadna inna operacja na tej zmiennej. Na przykład, we fragmencie programu z listingu 6.2 dla zmiennej x nie istnieje następstwo dd dla wierzchołków 1 i 4, ponieważ, aby dojść z 1 do 4 musimy przejść przez wierzchołek 3, w którym następuje użycie x. Dla tej zmiennej mamy jedynie następstwo du dla wierzchołków 1 i 3 oraz ud dla wierzchołków 3 i 4.

1 x:=3 2 y:=4 3 if (x 0 jest nam potrzebny do dalszego wnioskowania warunek x ≥ 0). Reguła while (6) opisuje działanie pętli while: formuła ψ jest tu niezmiennikiem

pętli – jest prawdziwa zarówno przed, jak i po wykonaniu pętli. Warunek końcowy zawiera negację A, gdyż jest warunkiem zakończenia pętli. Pokażemy teraz na przykładzie, jak stosuje się logikę Hoare’a do dowodzenia poprawności programów. Rozważmy program z listingu 6.4 mający obliczać silnię4 z liczby naturalnej n. {x = n ∧ n > 0} 1 y:=1 2 while (x>0) do 3

y:=y*x

4 x:=x-1 5 end while {y = n!} Listing 6.4. Program obliczający silnię Program przyjmuje na wejściu zmienną x, a na wyjściu zwraca y, które ma być równe x!. Przed programem i po programie są wypisane warunki: początkowy oraz końcowy. Razem z programem (nazwijmy go P) tworzą formułę Hoare’a: {x = n ∧ n > 0} P {y = n!} Formuła ta mówi, że jeśli zmienna x jest równa n, gdzie n jest liczbą dodatnią, to po wykonaniu programu P zmienna y będzie wynosić n!, czyli dokładnie tyle, ile chcemy. Formuły takie, wplecione w kod, są nazywane niezmiennikami (asercjami), gdyż niezależnie od tego jak przebiega sterowanie programu, jeśli znajdzie się ono w miejscu asercji, to warunek przez nią wskazywany zawsze jest prawdziwy. Jeśli udowodnimy, że powyższa formuła Hoare’a zachodzi, to udowodnimy poprawność programu. Dowód można przeprowadzić przez wstawianie kolejnych asercji między linie kodu, wykorzystując aksjomaty i reguły (1)–(6) podane powyżej. Na listingu 6.5 w kod zostały wplecione asercje (dla większej czytelności podane na szarym tle), które stanowią dowód poprawności. A1 i A9 są asercjami stanowiącymi warunek początkowy i końcowy. Pokażemy, że jeśli zachodzi A1, to po wykonaniu P zajdzie A9. Asercja A2 jest prawdziwa wobec implikacji x = n ∧ n > 0 ⇒ x! ⋅ 1 = n! ∧ x ≥ 0 (stosujemy tu regułę

konsekwencji (5)). Aby uzyskać {A2} y := 1 {A3} używamy aksjomatu przypisania {A3 [1/y]} y := 1 {A3}, bo A3 [1/y] = A2. Postać A4 wynika z reguły while (6), a A5 uzyskujemy przez zastosowanie reguły konsekwencji (5). A6 i A7 dostajemy, używając aksjomatu przypisania {A6 [y ⋅ x/y]} y := y ⋅ x {A6} oraz {A7 [x – 1/x]} x := x – 1 {A7}. Postać A8 wynika z tego, że A3 jest niezmiennikiem pętli, więc po jej zakończeniu

niezmiennik

ten

musi

zachodzić

wraz

z

negacją

asercji

odpowiadającej strażnikowi pętli (czyli wyrażeniu x > 0 w instrukcji while). Negacja wynika stąd, że skoro pętla się skończyła, to jej warunek musiał przestać zachodzić. Z A8 wynika, że x = 0, skąd po podstawieniu do x! ⋅ y = n! bezpośrednio uzyskujemy A9. A1 {x = n ∧ n > 0} A2 ⇒ {x! ⋅ 1 = n! ∧ x ≥ 0} 1 y:=1 A3 2 A4 A5 3 A6 4

{x! ⋅ y = n! ∧ x ≥ 0} while (x>0) do {x! ⋅ y = n! ∧ x ≥ 0 ∧ x > 0} ⇒ {(x – 1)! ⋅ y ⋅ x = n! ∧ (x – 1) ≥ 0} y:=y*x {(x – 1)! ⋅ y = n! ∧ (x – 1) ≥ 0} x:=x-1

A7 {x! ⋅ y = n! ∧ x ≥ 0} 5 end while A8 {x! ⋅ y = n! ∧ x ≥ 0 ∧ ~ (x > 0)} A9 ⇒ {y = n! } Listing 6.5. Program obliczający silnię z wstawionymi asercjami Jak widać, przeprowadzenie dowodu jest dosyć żmudne i wymaga czasami pomysłu na dostarczenie odpowiedniego wzmocnienia lub osłabienia warunku (tak jak np. w A2), aby uzyskać ciąg formuł Hoare’a prowadzący od warunku początkowego do warunku końcowego całego programu P. Dlatego, choć istnieją narzędzia umożliwiające w niektórych przypadkach zautomatyzowanie procesu dowodzenia, metoda ta nie znalazła szerokiego zastosowania w praktyce.

Znacznie efektywniejsze jest przeprowadzanie przeglądów, zwłaszcza inspekcji formalnych.

6.2.8. Symboliczne wykonywanie kodu Symboliczne wykonywanie kodu jest metodą powstałą w latach 70. XX wieku dzięki pionierskim pracom Kinga [87] i Clarke [88]. Polega ona na analizie działania programu, w którym zmienne nie przyjmują konkretnych wartości, ale są traktowane algebraicznie. Symboliczne stany reprezentują zatem zbiory możliwych konkretnych stanów (czyli zbiory możliwych wartości zmiennych). Algorytm symbolicznego wykonywania kodu analizuje możliwe ścieżki wykonania programu i dla każdej z nich oblicza tzw. warunek ścieżki (PC, od ang. path condition), który jest wyrażeniem lub zbiorem wyrażeń algebraicznych opisujących związki między zmiennymi, które muszą zajść, aby program przeszedł daną ścieżką. Dzięki tej metodzie jest możliwe projektowanie przypadków testowych pokrywających określone elementy struktury programu, np. instrukcje lub rozgałęzienia (patrz podrozdz. 9.1–9.2). Aby znaleźć test, który podąży określoną ścieżką, należy znaleźć takie wartości zmiennych, które spełnią warunek tej ścieżki. Zwykle warunki te są dosyć skomplikowane i programy do symbolicznego wykonywania kodu mają specjalne narzędzia do rozwiązywania nieliniowych warunków przy użyciu zaawansowanych metod analizy matematycznej czy metod sztucznej inteligencji, takich jak optymalizacja rojem cząstek (ang. particle swarm optimization). Rozważmy program z listingu 6.6. Zamienia on wartości zmiennych x i y podanych na wejściu. Ponadto, w linii 7. znajduje się martwy kod, który nie powinien wykonać się ze względu na fałszywość predykatu w linii 6. Symboliczne wykonanie kodu pozwala znajdować tego typu problemy.

1 function Zamień(x, y) 2 if (x>y) then 3 x=x+y 4 y=x-y 5 x=x-y 6 if (x>y)

7 print “Martwy kod” 8 end Listing 6.6. Program zamieniający miejscami wartości dwóch zmiennych Na rysunku 6.7 przedstawiono symboliczne wykonanie kodu z listingu 6.6. Każdej linii kodu odpowiada prostokąt zawierający warunek ścieżki (czyli formułę logiczną, która musi być spełniona, aby program poszedł daną ścieżką) oraz symboliczna wersja instrukcji. Dla sygnatury funkcji mamy PC = true (bo początek funkcji zawsze będzie wykonany) oraz przypisujemy zmiennym x i y ich symboliczne wersje X, Y, które będą od tej pory traktowane algebraicznie. Pierwszy if zawsze się wykona, więc również ma PC = true. Symboliczna wersja warunku w tej instrukcji if to X > Y. Jeśli warunek nie zachodzi, to kończymy analizę tej ścieżki. W tym przypadku warunkiem PC dla ścieżki jest to, by X ≤ Y. Jeśli jednak warunek zachodzi, to PC = x > y i wykonane zostanie ciało pierwszej instrukcji if. Symboliczne wykonanie przypisania y:=x–y pozwala nam stwierdzić, że po jej wykonaniu Y := X, ponieważ wcześniej X przyjęło wartość X + Y. Mogliśmy więc podstawić tę sumę pod zmienną X w instrukcji Y := X – Y. Analogicznie, wykorzystując ten sam mechanizm algebraicznych przekształceń, symboliczne wykonanie instrukcji x:=x–y pozwala nam obliczyć, że X := Y. Zatem wykonanie if zamieniło wartości zmiennych x i y ze sobą. Warunek ścieżki dla wszystkich instrukcji if jest taki sam (bo instrukcje te nastąpią zawsze sekwencyjnie, jedna po drugiej). Dochodzimy do drugiego if. Warunek ścieżki to PC = X > Y, ale z wykonania symbolicznego wiemy, że X ma teraz wartość Y i na odwrót. Gdyby warunek drugiego if zachodził, to jednocześnie musiałoby być X > Y i Y > X, co jest sprzecznością. Zatem ta ścieżka nigdy nie będzie wykonana.

Rysunek 6.7. Wykonanie symboliczne kodu zamieniającego wartości zmiennych Obecnie istnieje wiele narzędzi wspomagających symboliczne wykonanie kodu. Z najważniejszych można wymienić KLEE [63] autorstwa naukowców ze Stanforda oraz Java PathFinder [89] dla Javy, stworzony w laboratoriach NASA. W momencie znalezienia defektu programy te oferują możliwość wygenerowania konkretnego przypadku testowego, który tę usterkę ujawni. Znajdowanie konkretnych wartości wejściowych dla takiego testu polega na analizie wszystkich warunków ścieżki leżących na ścieżce, którą chcemy wykonać. W przypadku naszego przykładowego programu może to być bardzo proste: jeśli chcemy, by program wykonał ciało pierwszego if, to musi zachodzić warunek tej ścieżki, czyli X > Y. Wystarczy więc wziąć takie x i y, aby x było większe od y. Jeśli jednak program byłby bardziej skomplikowany, to im „głębiej” sięga pożądana przez nas ścieżka, tym więcej warunków ścieżki musi być spełnionych, a więc tym trudniejsza nasza kontrola nad sterowaniem programem. Niektóre z warunków mogą być nieliniowe. Inne mogą być od siebie zależne (np. wykorzystywać te same

zmienne), co jeszcze bardziej komplikuje sprawę. Dlatego programy realizujące symboliczne wykonanie programu używają skomplikowanych algorytmów do rozwiązywania tego typu warunków w celu odnalezienia konkretnych wartości parametrów wejściowych. Symboliczne wykonanie programu może być użyte pod kątem analizy odporności na zagrożenia (ang. vulnerability). Przykładem takiej aplikacji jest program Kudzu [90] dla języka JavaScript, opracowany ze względu na wzrastającą popularność technologii AJAX.

6.2.9. Analiza statyczna strony internetowej Strony www można testować na wiele sposobów. W szczególności, ponieważ na stronę można patrzeć jak na program, którego kodem źródłowym są pliki html, css, JavScript, php, AJAX itd., strona webowa może podlegać wszystkim technikom i typom testowania, które stosuje się do kodu źródłowego (np. [59]). W kontekście analizy statycznej testowanie strony internetowej można rozumieć jako analizę struktury (architektury). Analiza ta może polegać na sprawdzeniu, czy struktura strony jest zbalansowana i czy do każdego jej elementu można dostać się za pomocą małej liczby operacji (kliknięć). Taka forma analizy może być uznana za statyczne testowanie użyteczności. Użytkownicy stron www opuszczają je przed osiągnięciem zamierzonego celu m.in. z następujących powodów: strony są trudne w użyciu; strony odpowiadają zbyt wolno; strony nie są odpowiednio zabezpieczone; występują błędy, np. „strony nie znaleziono”. Identyfikacja drugiego i trzeciego powodu jest domeną testowania niefunkcjonalnego. Do wykrycia błędów takich jak w powodzie czwartym można użyć zautomatyzowanych testów regresji, które sprawdzają poprawność linków w obrębie całej strony. Testowanie statyczne skupia się na powodzie pierwszym. Rozważmy przykład strony internetowej pokazanej na rysunku 6.8.

Rysunek 6.8. Struktura strony www sklepu internetowego Analiza statyczna może wykazać, że od momentu wejścia na stronę potwierdzenie zamówienia uzyskać można w co najmniej czterech krokach. Być może dałoby się zredukować ich liczbę np. przez umieszczenie wyszukiwarki produktów bezpośrednio na stronie głównej.

Rysunek 6.9. Przykład niezrównoważonej i zrównoważonej struktury strony internetowej

Analiza może też ujawnić wadliwość struktury strony. Na rysunku 6.9 przedstawiono dwie różne hierarchie stron tej samej witryny internetowej. Architektura pokazana po lewej jest niezbilansowana – jedna gałąź jest bardzo głęboka, natomiast strony 3, 4, 5 są ulokowane tuż pod stroną główną. W rezultacie, aby dostać się do stron 12, 13, 14, potrzeba aż pięciu kliknięć. Architektura po prawej stronie jest lepsza – dostęp do każdej strony można uzyskać w co najwyżej dwóch krokach.

6.2.10. Grafy wywołań Większość programów używa innych, zewnętrznych wobec siebie funkcji przez ich wywoływanie. Możemy zdefiniować pewną odmianę metryki złożoności cyklomatycznej, która pozwoli nam ocenić stopień skomplikowania struktury wywołań. Tak jak zwykła złożoność cyklomatyczna określa liczbę testów niezbędnych do pokrycia niezależnych ścieżek w programie, tak jej zmodyfikowana wersja mówi o liczbie testów potrzebnych do przeprowadzenia testów integracji. Model ten jest oparty na wywołaniu jednych modułów przez inne. Jego reprezentacją jest graf, stąd nazwa metody – grafy wywołań. Metryka ta, tak jak złożoność cyklomatyczna, została wprowadzona przez McCabe’a. Metoda ją wykorzystująca to tzw. podejście predykatów projektowych (ang. design predicate approach). Na rysunku 6.10 przedstawiono podstawowe typy wywołań. Jeśli moduł M0 zawsze wywołuje M1 (lub zawsze wywołuje wszystkie moduły M1, M2, … Mn), to mamy do czynienia z wywołaniem bezwarunkowym. Złożoność tego wywołania wynosi 0. Jeśli M0 może wywołać M1, ale nie musi, to mamy do czynienia z wywołaniem warunkowym o złożoności 1. Gdy M0 może wywołać jeden i tylko jeden spośród n modułów, jest to wywołanie wzajemnie wyłączające się o złożoności n – 1. Jeśli istnieje możliwość nie wywołania żadnego z nich, to nie możemy mówić o wzajemnym wyłączaniu i każde z wywołań należy traktować oddzielnie. Gdy M0 wywołuje M1 wielokrotnie podczas swojego działania (ale co najmniej raz), mówimy o wywołaniu iteracyjnym. Ma ono złożoność 1. Jeśli jednak w trakcie iteracji M1 może, ale nie musi być wywołany, mamy do czynienia z warunkowym wywołaniem iteracyjnym o złożoności 2. Po stworzeniu grafu wywołań należy policzyć sumę złożoności wnoszonych przez każdy rodzaj wywołania. Stanowi ona miarę wzrostu złożoności systemu z powodu danego modułu.

Rysunek 6.10. Podstawowe typy wywołań między modułami graf wywołań (ang. call graph) – abstrakcyjna reprezentacja przedstawiająca odwołania między procedurami i funkcjami w programie

#include #include #include int int int int

grep(char*, FILE*, char*); match(char*, char*); matchhere(char*, char*); matchstar(int, char*, char*);

int main(int argc, char *argv[]) { int i, nmatch; FILE *f; setprogname(“grep”); if (argc < 2) {printf(“usage: grep regexp [file ...]”); exit(2)} nmatch = 0; if (argc == 2) { if (grep(argv[1], stdin, NULL)) nmatch++; } else { for (i = 2; i < argc; i++) { f = fopen(argv[i], “r”); if (f == NULL) { printf(“can’t open %s:”, argv[i]); continue; } if (grep(argv[1], f, argc>3 ? argv[i] : NULL) > 0) nmatch++; fclose(f); } } return nmatch == 0; } int grep(char *regexp, FILE *f, char *name) { int n, nmatch; char buf[BUFSIZ]; nmatch = 0; while (fgets(buf, sizeof buf, f) != NULL) { n = strlen(buf); if (n > 0 && buf[n-1] == ‘\n’) buf[n-1] = ‘\0’; if (match(regexp, buf)) { nmatch++; if (name != NULL) printf(“%s:”, name); printf(“%s\n”, buf); }

} return nmatch; } int matchhere(char *regexp, char *text) { if (regexp[0] == ‘\0’) return 1; if (regexp[1] == ‘*’) return matchstar(regexp[0], regexp+2, text); if (regexp[0] == ‘$’ && regexp[1] == ‘\0’) return *text == ‘\0’; if (*text!=’\0’ && (regexp[0]==’.’ || regexp[0]==*text)) return matchhere(regexp+1, text+1); return 0; } int match(char *regexp, char *text) { if (regexp[0] == ‘^’) return matchhere(regexp+1, text); do { if (matchhere(regexp, text)) return 1; } while (*text++ != ‘\0’); return 0; } int matchstar(int c, char *regexp, char *text) { do { if (matchhere(regexp, text)) return 1; } while (*text != ‘\0’ && (*text++ == c || c == ‘.’)); return 0; } Listing 6.7. Kod źródłowy programu grep.c

Rozważmy pokazany na listingu 6.7 napisany w C program grep.c, pochodzący z [91] i nieco zmodyfikowany na nasze potrzeby. Policzmy złożoność grafu wywołań dla funkcji main. Po jej uruchomieniu zawsze nastąpi wywołanie setprogname (jednokrotne). Jest to wywołanie bezwarunkowe i nic nie wnosi do złożoności. Gdy warunek argc < 2 będzie spełniony wykona się jednokrotnie printf, ale gdy warunek ten nie będzie spełniony, możemy wejść do pętli, w której warunkowo wywoływane jest printf. Mamy więc do czynienia z warunkowym wywołaniem iteracyjnym, które wnosi 2 do złożoności. To samo dotyczy funkcji fopen, fclose i grep. Każda z nich albo się nie wywoła (jeśli argc < 2), albo może wywołać się wielokrotnie w pętli for. W funkcji grep warunek pętli wywołujący fgets sprawdzony będzie zawsze co najmniej raz, zatem jest to wywołanie iteracyjne o złożoności 1. Jeśli ten warunek już przy pierwszym sprawdzeniu będzie fałszywy, to grep nie wywoła match, które jest w ciele while. W przeciwnym wypadku match może być wywołane wielokrotnie. Jest to warunkowe wywołanie inkrementacyjne wnoszące 2 do całkowitej złożoności. Funkcja match zawsze wywoła przynajmniej raz matchhere: albo zostanie ona wywołana dokładnie raz w funkcji return, albo wielokrotnie (lecz co najmniej raz) w pętli do. To wywołanie inkrementacyjne wnosi 1 do złożoności. Funkcja matchhere wywoła co najwyżej raz jedną z dwóch funkcji: siebie albo matchstar. Każda daje wywołanie warunkowe i wnosi 1 do złożoności. Zauważmy, że nie jest to wywołanie rozłączne, bo może się zdarzyć, że żadna z tych funkcji nie będzie wywołana, np. wtedy, gdy spełniony będzie warunek regexp[0] == ′\0′. Zauważmy, że mamy tu do czynienia z wywołaniem rekurencyjnym – matchhere może wywołać sam siebie. Funkcja matchstar wywoła co najmniej raz funkcję matchhere, więc jest to wywołanie iteracyjne. Funkcje matchstar i matchhere w grafie wywołań tworzą cykl, co oznacza, że może następować rekurencyjne wywołanie jednej z tych funkcji przez drugą i na odwrót.

Rysunek 6.11. Graf wywołań dla programu grep.c Podliczając całkowitą złożoność grafu wywołań (CGC – ang. call graph complexity) dla programu grep.c, otrzymujemy: CGC = (0 + 2 + 2 + 2 + 2) + (2 + 2 + 1) + 1 + (1 + 1) + 1 = 17. Liczba ta oznacza, że istnieje 17 różnych ścieżek, reprezentujących 17 klas wywołań różniących się liczbą i rodzajem wywoływanych funkcji, które powinniśmy przetestować. Podczas stosowania metody grafów wywołań należy pamiętać o kilku ważnych rzeczach: metoda nie uwzględnia wewnętrznej, funkcjonalnej złożoności wywoływanych modułów, skupia się wyłącznie na stopniu skomplikowania wywoływań modułów; liczby przy typach wywołań nie określają samej złożoności, ale jej wzrost; np. wywołanie warunkowe należy rozumieć nie jako element

o złożoności 1, ale jako element, który podwyższa złożoność całego systemu o 1; grafy wywołań nie są grafami przepływu sterowania i nie można ich interpretować w ten sam sposób; w grafach wywołań po wywołaniu jakiejś funkcji wracamy z powrotem do wierzchołka reprezentującego metodę wywołującą.

1 W języku C (a także innych, takich jak C++ czy Java) jest dopuszczone stosowanie uproszczonego zapisu zwiększania wartości zmiennej o 1. Zamiast pisać i=i+1 można użyć operatora inkrementacji „++” i zapisać tę instrukcję równoważnie jako i++. 2 l-wartość to wartość zmiennej, która – po wykonaniu instrukcji – może być później ponownie wykorzystana. r-wartość to wartość wykorzystywana tylko w danej instrukcji. Na przykład w instrukcji a:=b+3 podczas wyliczania wartości zmiennej a do pamięci tymczasowo zapisywana jest wartość b + 3. Po wykonaniu przypisania zmienna a będzie miała nową wartość, która została zapisana w pamięci, natomiast wartość b + 3 zostanie zapomniana. Dlatego b + 3 jest r-wartością. Natomiast samo b jest l-wartością, bo cały czas zmienna ta przechowywana jest w pamięci. 3 Logika pierwszego rzędu to znana dobrze ze szkoły średniej logika wykorzystująca kwantyfikatory ∀ („dla każdego”) i ∃ („istnieje”), wraz z operatorami negacji ∼, koniunkcji ∧, alternatywy ∨, implikacji ⇒ i równoważności ⇔. 4 Silnia liczby naturalnej n (oznaczana jako n!) to iloczyn wszystkich liczb naturalnych od 1 do n: n! = 1 ⋅ 2 … n.

7. Analiza dynamiczna

Analiza dynamiczna jest przeciwieństwem opisanej wcześniej analizy statycznej w tym sensie, że jej przeprowadzenie odbywa się na działającym kodzie. Metoda ta pozwala na wykrywanie anomalii takich jak wycieki pamięci czy wiszące/dzikie wskaźniki. Służy również do przeprowadzania różnego rodzaju analiz wydajności, statystycznych analiz działania programu, czy też tworzenia grafu wywołań dla działającego programu. analiza dynamiczna (ang. dynamic analysis) – proces oceny systemu lub modułu na podstawie jego zachowania w działaniu, np. zarządzanie pamięcią czy wykorzystaniem procesora Metoda ta sprawdza się bardzo dobrze zwłaszcza w przypadku wymienionych powyżej anomalii, czyli wycieków pamięci oraz dzikich wskaźników. Jeśli na skutek usterki w programie następuje wyciek pamięci lub powstaje dziki wskaźnik, zwykle nie objawia się to od razu żadnym zewnętrznym symptomem, natomiast konsekwencje kumulacji takich błędów w czasie mogą powodować poważne problemy. Analiza dynamiczna pozwala szybko wykrywać istnienie tych anomalii oraz pomaga w znajdowaniu miejsc ich wystąpienia. narzędzie do analizy dynamicznej (ang. dynamic analysis tool) – narzędzie rejestrujące informacje o stanie wykonywanego programu; używane zwykle do znajdowania nieprzypisanych wskaźników, sprawdzania alokacji, użycia i dealokacji pamięci oraz do oznaczania jej wycieków Do

przeprowadzenia

analizy

dynamicznej

zwykle

jest

niezbędna

instrumentacja kodu, czyli dodanie do kodu programu instrukcji służących do zbierania informacji na temat działania programu. Instrumentacja jest

dokonywana albo przez specjalny kompilator, który dodaje odpowiednie linie do kodu programu przed jego kompilacją, albo jest przeprowadzana przez zewnętrzny program działający między oryginalnie wykonywanym kodem a systemem operacyjnym. Oto proste zastosowanie

instrumentacji.

Rozważmy

program

Collatz

przedstawiony na listingu 7.1. Program ten symuluje wykonywanie pewnych operacji na liczbach naturalnych i jest związany ze słynną hipotezą Collatza. Hipoteza ta mówi, że jeśli weźmiemy dowolną liczbę naturalną, a następnie będziemy ją przekształcać według dwóch prostych reguł: jeśli liczba jest parzysta, podziel ją przez 2; jeśli liczba jest nieparzysta, pomnóż ją przez 3 i dodaj 1, to po skończonej liczbie takich operacji zawsze dojdziemy do liczby 1. Na przykład, dla liczby 13 otrzymamy następujący ciąg liczb: 13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1. Program Collatz realizuje właśnie taki ciąg operacji dla zadanej liczby naturalnej na wejściu.

1 function Collatz(n) 2 while (n>1) do 3 if (n mod 2 == 0) then 4 5 6

n:=n/2 else n:=3*n+1

7 print n 8 end function Listing 7.1. Pseudokod programu Collatz Przypuśćmy, że z jakiegoś powodu interesuje nas, ile razy podczas działania tego programu wykonały się poszczególne instrukcje. W takiej sytuacji możemy dokonać instrumentacji kodu takiej, jak ta z listingu 7.2. Linie wyjściowego programu zachowały oryginalną numerację. Dodane linie nie są numerowane, aby można było lepiej odróżnić kod oryginalny od instrumentacji.

instrumentacja

(ang.

instrumentation)



dodanie

specjalnego

kodu

do

oryginalnego programu w celu zbierania informacji o jego zachowaniu podczas wykonania, np. w celu pomiaru pokrycia kodu instrumentator, instrumentator programowy (ang. instrumenter, program instrumenter) – narzędzie programowe wykonujące instrumentację kodu Wprowadziliśmy nową tablicę liczb całkowitych op, mającą tyle elementów, ile linii programu. Po i-tej linii oryginalnego kodu wprowadzamy instrukcję op[i]:=op[i]+1. Dzięki temu wartość op[i] zwiększa się za każdym razem, gdy program wykonuje i-tą

1 function Collatz(n) op[1..7]:=0 op[1]:=op[1]+1 2 while (n>1) do op[2]:=op[2]+1 3 4 5 6

if (n mod 2 == 0) then op[3]:=op[3]+1 n:=n/2 op[4]:=op[4]+1 else op[5]:=op[5]+1 n:=3*n+1

op[6]:=op[6]+1 7 print n op[7]:=op[7]+1 print op[1..7] 8 end function Listing 7.2. Zinstrumentowany program Collatz linię wyjściowego programu. Na końcu możemy wypisać zawartość tablicy op, aby np. zobaczyć, które linie wykonywały się częściej niż inne, a które na przykład nie wykonały się w ogóle.

7.1. Wykrywanie wycieków pamięci Wycieki pamięci występują w programach pisanych w językach umożliwiających dynamiczną alokację pamięci bez tzw. odśmiecacza pamięci (ang. garbage collector). Mechanizm ten istnieje m.in. w Javie, C#, Pythonie czy Rubym, ale nie ma go np. w standardowym C czy C++. Wycieki pamięci objawiają się zwykle dopiero wtedy, gdy nastąpi jej przepełnienie lub nadpisanie pamięci w używanym aktualnie miejscu stosu, co prowadzi zwykle do globalnej awarii systemu. Często symptomem istnienia wycieku pamięci jest coraz wolniejszy czas działania programu. Problemy z pamięcią zwykle są spowodowane pomyłką programisty, ale zdarza się – choć rzadko – że mogą być wynikiem błędnie działającego kompilatora, który podczas kompilacji generuje kod z wyciekami pamięci. wyciek pamięci

(ang.

memory

leak)



błąd

mechanizmu

programu

wykonującego dynamiczną alokacje pamięci polegający na niezwalnianiu pamięci po zaprzestaniu jej używania; może on prowadzić do awarii programu spowodowanej brakiem pamięci Wycieki pamięci są najefektywniej wykrywane za pomocą narzędzi dedykowanych do tego celu. Analiza pod kątem problemów z pamięcią zwykle polega na długotrwałym i intensywnym używaniu programu, gdyż nie są to błędy, które pojawiają się błyskawicznie, od razu po uruchomieniu programu. Szansa, że błąd taki zostanie wykryty podczas krótkiej sesji testowej, jest niewielka. Narzędzia monitorujące pamięć analizują ilość pamięci alokowanej i zwalnianej. Gdy dynamicznie zaalokowany blok pamięci „znika” z systemu bez uprzedniego formalnego zwolnienia tej pamięci, narzędzie oznacza miejsce, w którym potencjalnie mogło dojść do wycieku pamięci. W takim przypadku są możliwe dwa scenariusze: albo incydent ten jest logowany i wykonanie programu trwa nadal, albo wykonanie jest natychmiast przerywane. Decyzja o wyborze dalszej drogi zależy oczywiście od testera.

7.2. Wykrywanie dzikich i wiszących wskaźników

Wskaźnik zmiennej x to zmienna wskazująca na adres pamięci, pod którą zmienna x się znajduje. W językach programowania dopuszczających swobodną alokację i dealokację pamięci możliwe jest wystąpienie błędów w postaci wiszących oraz dzikich wskaźników, czyli wskaźników, które nie wskazują na to, na co oryginalnie powinny. Wiszący wskaźnik powstaje, gdy obiekt, na który wskaźnik wskazuje, zostaje usunięty (np. przy użyciu funkcji free w języku C) lub pamięć przeznaczona na ten obiekt jest zwalniana, bez jednoczesnej modyfikacji wartości wskaźnika. Powstanie wiszącego wskaźnika może być spowodowane wyjściem z bloku, w którym jest widoczna wskazywana zmienna. Dziki wskaźnik powstaje, gdy w momencie jego utworzenia nie następuje poprawna inicjalizacja jego wartości. Dobrą praktyką jest inicjalizacja każdego wskaźnika od razu przy jego definiowaniu. wskaźnik (ang. pointer) – zmienna, której wartość określa lokalizację (adres) innej zmiennej [7] wiszący wskaźnik (ang. dangling pointer) – wskaźnik, który nie wskazuje na poprawne obiekty właściwego typu ani nie jest wskaźnikiem pustym (null) w językach, które to umożliwiają dziki wskaźnik (ang. wild pointer) – wskaźnik, który nie został zainicjalizowany przy tworzeniu, przez co jego zawartość jest nieokreślona Na listingu 7.3 przedstawiono fragment programu napisanego w C. W linii 2. następuje deklaracja zmiennej o nazwie w, która ma być wskaźnikiem na zmienną typu int. Deklaracja nie nastąpiła razem z inicjalizacją (np. int *w = NULL), więc w jest dzikim wskaźnikiem. Teoretycznie może wskazywać na cokolwiek. W linii 3. przypisujemy mu wartość null, zatem staje się poprawnym wskaźnikiem, który nie wskazuje na razie na nic. Linie 4.–7. są blokiem, w którego zakresie znajduje się zmienna x. Jest ona niewidoczna poza tym blokiem. W linii 6. każemy wskaźnikowi w wskazywać na adres zmiennej x. W linii 8. chcemy wykorzystać ten wskaźnik, ale linia ta jest poza blokiem, w którym była widoczna zmienna x, dlatego wskaźnik może teraz wskazywać na coś innego. Jeśli między wyjściem z bloku a wykonaniem instrukcji 8. stan pamięci się nie zmienił, to wszystko może

działać poprawnie, ale jeśli ten fragment pamięci został zajęty przez inny proces, to wywołanie linii 8. może spowodować nieprzewidziane zachowanie programu.

1 { 2 int* w;

// w jest dzikim wskaźnikiem

3 4

w = NULL; // teraz w jest poprawnym wskaźnikiem {

5 6 7

int x; w = &x; // wskaźnik w wskazuje na adres zmiennej x } // koniec bloku1 – tracimy widoczność x; w staje się

8

wiszącym wskaźnikiem OperacjaNaWskaźniku(w); // teraz może być problem!

9 } Listing 7.3. Kod z wiszącymi i dzikimi wskaźnikami Konsekwencje stosowania dzikich i wiszących wskaźników mogą być następujące: program może działać zgodnie z oczekiwaniem – tak może być, jeśli zwolniona pamięć, na którą nadal wskazuje wskaźnik, nie została zajęta przez inny obiekt; stan programu jest błędny; program może się zawiesić – jeśli np. użycie wskaźnika spowodowało nadpisanie obszaru pamięci zajmowanego przez system operacyjny; program działa niepoprawnie – bo nie może uzyskać dostępu do swoich obiektów lub używa nieświadomie innych wartości dla tych obiektów; program może spowodować nieprawidłowe działanie innego programu – jeśli użycie wiszącego wskaźnika spowoduje nadpisanie danych, z których korzysta inna aplikacja.

7.3. Błędy API API (ang. Application Programming Interface), czyli interfejs programowy aplikacji, to zestaw funkcji pozwalających na uzyskiwanie zdalnej funkcjonalności aplikacji. Przykładem API może być zestaw funkcji dostarczanych przez serwisy takie jak

Google, Twitter, Facebook, które umożliwiają kontaktowanie się z określonym serwisem i wykonywanie pewnych operacji lub żądanie określonych usług. Błędy mogą pochodzić z niedostatecznej kontroli wywoływanych funkcji API. Jeśli funkcja API zwróci z jakiegoś powodu kod błędu oznaczający, że coś poszło nie tak, a programista wykorzystujący funkcje API nie uwzględni tego w swoim kodzie, może to doprowadzić do nieprzewidzianego zachowania się programu. Narzędzia analizy dynamicznej mogą wykrywać tego typu problemy przez przechwytywanie wywołań API oraz zwracanych wartości i sprawdzać, czy argumenty wywołania są zgodne z oczekiwaną postacią listy parametrów lub czy funkcja nie zwróciła wartości oznaczającej błąd. Użycie takich narzędzi oczywiście znacznie spowalnia wykonanie testowanego programu.

7.4. Analiza wydajności (profiling) Analiza wydajności (czyli tzw. profiling) pozwala określić, ile czasu program przeznacza na wykonywane przez niego operacje, które fragmenty kodu wykonywane są częściej, a które rzadziej oraz jakie funkcje zostały przez niego wywołane. Analiza ta umożliwia odnalezienie fragmentów kodu, które działają wolniej niż się spodziewaliśmy lub np. wywołują podejrzanie dużo razy pewne funkcje. Przeprowadza się ją przy użyciu odpowiednich narzędzi, na programach mających duży kod źródłowy, niemożliwy do całościowej oceny przez przegląd czy inspekcję. Eksperymentalnie pokazano, że dla czasu i obszaru wykonywanego programu zachodzi zasada Pareto w wersji 90/10, tzn. że około 90% swojego czasu program spędza w 10% swojego kodu. To oznacza, że w większości przypadków zasadnicze przyspieszenie działania programu można uzyskać, poprawiając niewielkie fragmenty kodu, a lokalizację tych fragmentów umożliwia właśnie analiza wydajności. Zwykle analiza wydajności składa się z trzech kroków: kompilacja testowanego programu z umożliwieniem profilowania; wykonanie programu skutkujące generacją pliku analizy wykonania; uruchomienie narzędzia analizującego plik analizy wykonania. Załóżmy, że chcemy dokonać analizy wydajności programu mojprogram.c. Na listingu 7.4 pokazano, jak zrobić to przy użyciu programu gprof.

cc –o mojprogram mojprogram.c –g –pg ./mojprogram gprof gmon.out > analiza.txt Listing 7.4. Przeprowadzenie analizy wydajności Pierwsza linia to użycie kompilatora cc w celu skompilowania pliku mojprogram.c. Parametr –pg informuje kompilator o włączeniu opcji profilowania. Druga linia to uruchomienie skompilowanego programu. Program może działać nieco wolniej niż w przypadku nie wykorzystującym profilowania, gdyż w trakcie działania są zbierane informacje o wykonaniu. Informacje te są zapisywane w domyślnym pliku gmon.out. Trzecia linia to wywołanie programu gprof na pliku gmon.out, przy czym wyjście jest przekazane do pliku tekstowego analiza.txt. Wyjście programu gprof składa się z dwóch części. Pierwszą jest tzw. płaski profil (ang. flat profile) pokazujący, ile czasu nasz program spędzał na wywoływaniu poszczególnych funkcji. Druga to znany nam już z punktu 6.2.10 graf wywołań.

Flat profile: Each sample counts as 0.01 seconds. % cumulative self self time seconds seconds calls ms/call 33.34 16.67 16.67 16.67 16.67 0.00 0.00 0.00 0.00 0.00

0.02 0.03 0.04 0.05 0.06 0.06 0.06 0.06 0.06 0.06

0.02 0.01 0.01 0.01 0.01 0.00 0.00 0.00 0.00 0.00

total ms/call

7208 244 8 7

0.00 0.04 1.25 1.43

0.00 0.12 1.25 1.43

236 192 47 45 1

0.00 0.00 0.00 0.00 0.00

0.00 0.00 0.00 0.00 50.00

name open offtime memccpy write mcount tzset tolower strlen strchr main

0.00 0.00

0.06 0.06

0.00 0.00

1 1

0.00 0.00

0.00 10.11

memcpy print

0.00

0.06

0.00

1

0.00 ...

0.06

0.00

1

0.00

0.00

profil

0.00

50.00

report

Listing 7.5. Przykładowy płaski profil programu generowany przez gprof Przykładowy płaski profil wygenerowany przez gprof [92] jest pokazany na listingu 7.5. Funkcje są posortowane według czasu spędzonego na ich wykonaniu. Z raportu wynika, że najwięcej czasu zajmowało wykonywanie otwieranie plików (funkcja open). Funkcje mcount i profil są funkcjami używanymi przez mechanizm profilujący i dlatego są obecne w każdym raporcie. Czasy ich wykonania mogą być użyte do obliczenia narzutu, jaki powstał w wyniku używania narzędzia profilującego. Poszczególne kolumny raportu przedstawiają: % time – procent czasu spędzonego w danej funkcji (powinny sumować się do 100%); cumulative seconds – całkowita liczba sekund spędzonych w danej funkcji oraz wszystkich wymienionych powyżej; self seconds – liczba sekund spędzonych w danej funkcji; calls – liczba wywołań danej funkcji; jeśli nie było wywołań lub nie da się określić ich liczby (np. gdy funkcja ta nie była skompilowana z opcją możliwości profilowania), to pole jest puste; self ms/call – średni czas (w milisekundach) spędzony w funkcji na jedno wywołanie; total ms/call – jw., ale dla danej funkcji oraz wszystkich funkcji wywoływanych przez nią (o ile były profilowane – w przeciwnym razie pole jest puste); name – nazwa funkcji. Jeśli chcemy obliczyć efektywny czas spędzony np. na wykonaniu funkcji open, to musimy odjąć od łącznego czasu czas działania procedur mcount i profil. Okazuje się, że efektywny czas dla funkcji open wynosi nie 33.34, ale 40%: %time(open) = 0.02/(0.06 – 0.01) ⋅ 100% = 0.02/0.05 ⋅ 100% = 40%.

granularity: each sample hit covers 2 byte(s) for 20.00% of 0.05 seconds index % time

self

children

[1]

0.00 0.00

0.05 0.05

100.0

called

1/1

name

start [1] main [2]

0.00 0.00 1/2 on_exit [28] 0.00 0.00 1/1 exit [59] ----------------------------------------------0.00 0.05 1/1 start [1] [2] 100.0 0.00 0.05 1 main [2] 0.00 0.05 1/1 report [3] ----------------------------------------------0.00 0.05 1/1 [3]

100.0

0.00 0.00 0.00

0.05 0.03 0.01

1 8/8 1/1

0.00 0.00

0.01 0.00

9/9 12/34

0.00

0.00

8/8

0.00 0.00 1/1 0.00 0.00 8/8 0.00 0.00 8/16 ----------------------------------------------[4] 59.8 0.01 0.02 8+472 0.01

0.02

244+260

0.00

0.00

236+1

main [2]

report [3] timelocal [6] print [9]

-----------------------------------------------

fgets [12] strncmp [40] lookup [20] fopen [21] chewtime [24] skipspace [44] [4] offtime [7] tzset [26]

Listing 7.6. Graf wywołań wygenerowany przez gprof Na listingu 7.6 jest pokazany graf wywołań. Jest on podzielony na sekcje oddzielone kreskami. Każda sekcja dotyczy jednej funkcji. Funkcje są opisywane numerem (w kwadratowych nawiasach) oraz nazwą. W każdej sekcji linia z numerem w kolumnie „index” dotyczy rozważanej funkcji. Linie nad nią dotyczą funkcji, które ją wywołują, linie pod nią – funkcje przez nią wywoływane. Liczby w postaci x/y dotyczą całkowitej liczby wywołań oraz liczby wywołań nierekursywnych. Cykle są traktowane jak pojedyncze funkcje, aby pojęcie czasu spędzonego w wywoływanych funkcjach miało sens. Jeśli bowiem funkcja A wywołuje funkcję B, a B – funkcję A, to na czas spędzony w A składa się czas spędzony w B, ale ten z kolei zawiera… czas spędzony w A. Dokładny opis struktury obu opisanych raportów można znaleźć w [92]. Pojedyncze wykonanie profilowania może być obarczone dużym błędem losowym. Aby zwiększyć dokładność pomiaru można albo przeprowadzać analizę przez dłuższy czas, albo – jeśli nie jest to możliwe – przeprowadzić kilka analiz. Programy takie jak gprof umożliwiają łączenie ze sobą raportów z plików gmon.out.

1 W języku C klamry { } oznaczają tzw. bloki kodu. Zakres widoczności zmiennej ogranicza się tylko do bloku, w którym została zdefiniowana.

8. Techniki oparte na specyfikacji (czarnoskrzynkowe)

Znajomość technik projektowania testów opartych na specyfikacji jest kluczową umiejętnością analityka testów. Testowanie oparte na specyfikacji traktuje program jak czarną skrzynkę o nieznanej strukturze wewnętrznej. Stąd inna nazwa tej techniki: testowanie czarnoskrzynkowe. Podstawą tworzenia testów w tej technice są dokumenty pierwotne wobec kodu, takie jak wymagania, dokumentacja techniczna, projekt architektury, dokumentacja użytkownika czy przypadki użycia. Zaletą technik opartych na specyfikacji jest możliwość wczesnego projektowania testów, jeszcze przed rozpoczęciem kodowania. Testy można tak naprawdę tworzyć już po zatwierdzeniu dokumentu wymagań. Podczas tworzenia przypadków testowych tester nie ma wglądu do kodu programu, dlatego nie musi mieć doświadczenia w programowaniu. W rozdziale tym dokonamy przeglądu wszystkich najważniejszych technik z tej rodziny. Rozpoczniemy od omówienia metod najbardziej klasycznych: podziału na klasy równoważności, analizy wartości brzegowych, tablic decyzyjnych, grafów przyczynowo-skutkowych oraz maszyny skończenie stanowej (podrozdz. 8.1–8.5). Następnie w podrozdziale 8.6 opiszemy metodę Category-Partition. Jest ona jedną z najstarszych technik testowania opartych na specyfikacji. Drzewa klasyfikacji oraz tzw. techniki kombinacyjne, omówione w podrozdziałach 8.7 i 8.8, można uznać za odmiany tej metody. Podrozdział 8.9 jest poświęcony zagadnieniu testowania dziedziny, czyli rozszerzeniu metody podziału na klasy równoważności dla przypadków wielowymiarowych. W podrozdziałach 8.10, 8.11 i 8.12 opiszemy metody oparte na

perspektywie użytkownika, czyli testowanie na podstawie przypadków użycia, scenariuszy oraz tzw. historyjek użytkownika, stosowanych głównie w projektach opartych na metodykach zwinnych. Podrozdział 8.13 jest poświęcony różnym odmianom testowania losowego, podrozdział 8.14 dotyczy metod opartych na składni, a 8.15 – testowania opartego na cyklu życia danych. Wreszcie, w podrozdziale 8.16 omówimy praktyczne zagadnienia dotyczące łączenia różnych technik czarnoskrzynkowych ze sobą. Dla każdej z omówionych metod opiszemy w szczególności: wymagania testowe dla danej metody – czyli zbiór cech, atrybutów, funkcji lub elementów, na podstawie których będą konstruowane przypadki testowe; elementy pokrycia – czyli zbiór cech, atrybutów, funkcji lub elementów, które należy pokryć testami; kryterium pokrycia – czyli warunek, który musi być spełniony, aby uznać, że w pełni wykorzystano dany model do tworzenia przypadków testowych; stopień pokrycia – czyli wartość z przedziału 0–100% mówiącą w jakim stopniu zostało spełnione kryterium pokrycia dla zadanego zestawu testów; zwykle jest to liczba wymagań testowych (lub elementów pokrycia) pokrytych testami podzielona przez liczbę wszystkich wymagań testowych (lub elementów pokrycia). Należy pamiętać, że dla wielu metod osiągnięcie 100% pokrycia często nie jest możliwe. W takich przypadkach, jeśli kryteria wyjścia uwzględniają stopień pokrycia, należy użyć rozsądnej wartości, która z jednej strony będzie możliwie bliska 100%, ale z drugiej – będzie z dużym prawdopodobieństwem osiągalna. czarnoskrzynkowa technika (projektowania przypadków testowych), projektowanie przypadków testowych na podstawie specyfikacji, technika oparta na specyfikacji (ang. black-box (test design) technique, specification-based test design technique, specification-based technique) – procedura wyprowadzająca i/lub wybierająca przypadki testowe na podstawie analizy specyfikacji (funkcjonalnej lub niefunkcjonalnej) modułu lub systemu bez odniesienia do jego wewnętrznej struktury

Z pojęciem kryterium pokrycia wiąże się inne ważne pojęcie: subsumpcji. Powiemy, że kryterium K 1 subsumuje kryterium K 2 wtedy i tylko wtedy, gdy każda suita testowa, spełniająca K 1 spełnia również K 2.

8.1. Podział na klasy równoważności 8.1.1. Opis metody Metoda podziału na klasy równoważności jest jedną z najstarszych, najprostszych koncepcyjnie i najpowszechniej stosowanych technik czarnoskrzynkowych. Jest ona związana z modelowaniem dziedziny testowanego programu, przy czym modelowaniu mogą podlegać zarówno wejścia, jak i wyjścia programu. dziedzina (ang. domain) – zbiór wszystkich możliwych (dozwolonych) wartości wejścia lub wyjścia z programu dziedzina danych wejściowych (ang. input domain) – zbiór wszystkich możliwych (dozwolonych) wartości wejściowych programu dziedzina danych wyjściowych (ang. output domain) – zbiór wszystkich możliwych (dozwolonych) wartości wyjściowych programu Technika polega na podziale dziedziny przedmiotu testu lub elementu testowego na klasy równoważności, które stają się elementami pokrycia. Podziału dokonuje się za pomocą tzw. charakterystyk, czyli opisów właściwości elementów dziedziny. Charakterystyka powinna zawsze wynikać ze specyfikacji wymagań bądź dokumentów opisujących funkcjonalność programu. Na przykład, jeśli program na wejściu pobiera liczbę a i z jakiegoś powodu jest ważne, czy jest to liczba dodatnia czy ujemna, to charakterystyką może być relacja wartości a do zera. Dzieli ona zbiór wszystkich możliwych wartości a na trzy klasy: wartości mniejsze od zera, zero i wartości większe od zera. Metoda podziału na klasy równoważności może wykorzystywać jedną lub więcej charakterystyk. Mamy wtedy do czynienia z jednym lub kilkoma podziałami dziedziny na klasy równoważności. Z każdej klasy wybiera się przynajmniej jeden element, a następnie tworzy się przypadki testowe dla

wszystkich wybranych elementów. Klasa równoważności powinna być zbiorem wartości wejściowych lub wyjściowych, dla których tester spodziewa się podobnego działania programu. Klasy powinny więc być tworzone tak, że jeśli podanie na wejście (lub wymuszenie na wyjściu) wartości należącej do jednej z nich spowoduje nieprawidłowe działanie programu, to z dużym prawdopodobieństwem powinno to również nastąpić dla każdej innej wartości z tej klasy. klasa równoważności, klasa abstrakcji, podzbiór równoważności

(ang.

equivalence partition, equivalence class) – podzbiór dziedziny danych wejściowych lub wyjściowych, dla którego zakłada się, na podstawie specyfikacji, że zachowanie modułu lub systemu jest takie samo Istotą metody jest dokonanie podziału dziedziny. Jest to czynność kluczowa i – wbrew temu, co może się wydawać – w wielu przypadkach nietrywialna. Podział musi bowiem spełniać pewne warunki, które omówimy w dalszej części tego podrozdziału. Tester może dokonać podziału na klasy równoważności według swojego uznania – nie ma żadnego ścisłego, uniwersalnego przepisu na tę czynność. Rodzaj podziału zależy zawsze od programu, jego funkcjonalności, specyfikacji wymagań, przedmiotu testowania, ale przede wszystkim od intuicji testera. Jedna z fundamentalnych zasad testowania mówi, że testowanie gruntowne jest niewykonalne. Dzięki technice podziału na klasy równoważności możemy zredukować potencjalnie nieskończony zbiór danych testowych do skończonej liczby przypadków równej liczbie klas równoważności. W przypadku, gdy mamy do czynienia z jednym podziałem minimalna liczba przypadków testowych pokrywających wszystkie warunki testowe to liczba klas równoważności tego podziału. Jeżeli stosujemy jednocześnie kilka różnych podziałów P1, P2, …, Pk , to minimalna liczba przypadków testowych potrzebna do spełnienia kryterium pokrycia to maksimum z {|P1|, |P2|, …, |Pk |}, gdzie |Pi| jest liczbą klas równoważności i-tego podziału. W tabeli 8.1 podsumowano technikę podziału na klasy równoważności pod kątem warunków testowych oraz elementów, kryterium i stopnia pokrycia. Należy jednak pamiętać – i uwaga ta odnosi się do wszystkich metod testowania – że fakt osiągnięcia stuprocentowego pokrycia w danej metodzie nie oznacza stuprocentowego przetestowania programu. To, jak już wspomnieliśmy wcześniej,

jest niewykonalne. Osiągnięcie pełnego pokrycia oznacza jedynie, że w pełni wykorzystaliśmy możliwości, jakie oferuje nam konkretna technika testowania i że dany model nie jest w stanie wygenerować więcej interesujących przypadków testowych. podział

na

klasy

równoważności

(ang.

equivalence

partitioning)



czarnoskrzynkowa technika projektowania przypadków testowych, w której przypadki te projektowane są tak, aby użyć elementów z klas równoważności; w szczególności przypadki testowe są projektowane tak, aby co najmniej raz pokryć każdą klasę równoważności Tabela 8.1. Podsumowanie metody podziału na klasy równoważności

8.1.2. Formalna definicja podziału Zdefiniujmy teraz formalnie pojęcie podziału. Z matematycznego punktu widzenia podział dziedziny D na klasy równoważności jest wyznaczony przez relację równoważności1 określoną (niekoniecznie skończonym) zbiorem. Zbiory X1, X2,

…, Xn, n > 0 tworzą podział zbioru X, jeśli są spełnione następujące warunki: 1) wszystkie Xi są niepuste: ∀i ∈ {1, …, n} Xi ≠ ∅; 2) suma wszystkich Xi daje cały zbiór X:

3) Xi są parami rozłączne: ∀i, j ∈ {1, …, n} i ≠ j ⇒ Xi ∩ Xj ≠ ∅.

Wyjaśniając jeszcze inaczej, podział zbioru X to rodzina niepustych zbiorów Xi taka, że każdy element X należy do dokładnie jednego z nich. Na rysunku 8.1 przedstawiono poprawny podział dziedziny D pewnego programu. Każdy element

dziedziny jest reprezentowany przez czarną kropkę, a zbiory Xi są reprezentowane przez szare zaokrąglone prostokąty. W tym przykładzie dziedzina programu została podzielona na 7 klas równoważności. Zauważmy, że klasa równoważności może składać się tylko z jednego elementu (w naszym przykładzie X4).

Rysunek 8.1. Podział dziedziny na klasy równoważności Na rysunku 8.2 przedstawiono błędny podział dziedziny D. Występują tu dwa problemy: po pierwsze, zostały zdefiniowane trzy klasy, ale dwie z nich, X1 i X3, mają niepustą część wspólną. Po drugie, niektóre elementy dziedziny nie należą do żadnej klasy.

Rysunek 8.2. Niepoprawny podział dziedziny

8.1.3. Poprawne i niepoprawne klasy równoważności Dziedzina programu zawiera tylko wartości dozwolone, ale w ogólności możemy również rozważać klasy równoważności zawierające dane niepoprawne. Na przykład, jeśli dziedziną wejściową programu jest zbiór liczb naturalnych od 1 do 100, to wartościami niepoprawnymi mogą być liczby ujemne, zero, litery, wartość null, łańcuchy znaków typu string. To, czy dopuszczać w analizie wartości niepoprawne, zależy od testera. Czasami ich uwzględnienie ma sens, np. wtedy, gdy testujemy funkcję API, której możemy jako parametry przekazać dowolne wartości, w tym syntaktycznie niepoprawne lub gdy testujemy program wczytujący parametry z zewnętrznego pliku lub z linii poleceń. Czasami jednak rozważanie wartości niepoprawnych nie jest konieczne, np. wtedy, gdy testujemy formularz mający już kontrolę poprawności wprowadzenia danych lub pole zawierające predefiniowaną listę wszystkich możliwych (poprawnych) wartości. Klasy równoważności zawierające dane niepoprawne są nazywane klasami niepoprawnymi lub klasami niepoprawności, natomiast klasy zawierające wartości poprawne – klasami poprawnymi lub klasami poprawności. Zauważmy, że klasa równoważności nie może jednocześnie zawierać wartości poprawnej i niepoprawnej, bo dla wartości niepoprawnych program z definicji powinien reagować w sposób odmienny niż dla wartości poprawnych.

8.1.4. Procedura tworzenia przypadków testowych Po wyznaczeniu klas równoważności należy przystąpić do tworzenia przypadków testowych. W podrozdziale 3.2 omówiliśmy dwa skrajne podejścia do tworzenia przypadków testowych: jeden do jednego, w którym jeden przypadek testowy spełnia dokładnie jedno wymaganie testowe, oraz podejście minimalizujące, w którym jednym testem staramy się pokryć jak najwięcej wymagań testowych. Zastosowanie pierwszego podejścia może skutkować bardzo dużą liczbą przypadków testowych. Z kolei zastosowanie drugiego może skutkować trudnościami w debagowaniu w przypadku odkrycia usterki. Dlatego w przypadku metody klas równoważności wykorzystuje się podejście zrównoważone: dla klas poprawności stosujemy podejście minimalizujące, a dla klas niepoprawności – podejście jeden-do-jednego. Proces tworzenia przypadków odbywa się zatem zgodnie z następującymi regułami: 1) każdej klasie równoważności należy przypisać unikalny numer pozwalający na jej jednoznaczną identyfikację; 2) należy sukcesywnie tworzyć przypadki testowe, z których każdy pokrywa jak najwięcej niepokrytych jeszcze klas poprawności; proces ten należy kontynuować do momentu, gdy wszystkie klasy poprawności zostaną pokryte (w przypadku, gdy mamy tylko jedną charakterystykę, każdy nowy przypadek pokryje dokładnie jedną klasę równoważności); 3) należy sukcesywnie tworzyć przypadki testowe, z których każdy pokrywa jedną i tylko jedną klasę niepoprawności; proces ten należy kontynuować do momentu pokrycia wszystkich klas niepoprawności. Powodem, dla którego przypadek testowy powinien pokrywać tylko jedną klasę niepoprawności jest chęć wykrycia jak największej liczby nieprawidłowości oraz uniknięcia tzw. maskowania błędów. Może zdarzyć się, że wykrycie jednej nieprawidłowości uniemożliwia wykrycie innej. Może też zdarzyć się, że wystąpienie błędu spowodowane nieprawidłową wartością wejściową zostanie zamaskowane przez podanie nieprawidłowej wartości innej zmiennej wejściowej. Program po wykryciu jednego błędu często zaprzestaje dalszej kontroli błędów. Poniższy przykład ilustruje zjawisko maskowania błędów. Rozważmy program z listingu 8.1 obliczający wynik końcowy przedmiotu, na który składają

się punkty z egzaminu (max. 70) oraz punkty z laboratoriów (max. 30). Załóżmy, że ograniczenia na maksymalne liczby punktów nie są podane w specyfikacji. Wynik końcowy zależy od sumy punktów. Ocena 5.0 jest przyznawana za uzyskanie ponad 90 punktów, 4.0 – za uzyskanie co najmniej 71 i nie więcej niż 90 punktów, 3.0 – za uzyskanie co najmniej 51 i nie więcej niż 70 punktów. Suma mniejsza od 51 skutkuje oceną 2.0.

1

string ObliczOcenę(int pktEgz, int pktLab) if (pktEgz + pktLab > 90) then

2 3 4 5 6 7

return "5.0" else byte pktRazem = pktEgz+pktLab if (pktRazem > 70) then return "4.0 z powodu liczby punktów ="+pktRazem else if (pktRazem > 50) then

8 9

return "3.0 z powodu liczby punktów ="+pktRazem return "2.0 z powodu liczby punktow ="+pktRazem

Listing 8.1. Program obliczający końcową ocenę z przedmiotu Klasą poprawności EGZ1 dla zmiennej pktEgz jest zbiór {0, 1, …, 70}, klasami niepoprawności mogą być: EGZ2 = zbiór liczb całkowitych mniejszych od 0 oraz EGZ3 = zbiór liczb całkowitych większych od 70. Analogiczne klasy (LAB1, LAB2, LAB3) możemy zbudować dla dziedziny zmiennej pktLab. Załóżmy teraz, że chcemy pokryć przypadkami testowymi w szczególności klasy niepoprawności EGZ1 i LAB3. Z odpowiednich klas równoważności wybierzmy wartości –1000 oraz 1095. Rozważmy dwie sytuacje. W pierwszej konstruujemy jeden przypadek testowy PT1 pokrywający obie te klasy niepoprawności (pktEgz = –1000, pktLab = 1095). W drugiej tworzymy dwa przypadki, z których każdy pokrywa dokładnie jedną klasę niepoprawności: PT1′ (pktEgz = –1000, pktLab = 25) oraz PT2′ (pktEgz = 70, pktLab = 1095). W sytuacji jednego przypadku PT1 warunek w linii 1. będzie prawdziwy, bo suma punktów wynosi 1095 – 1000 = 95 i program – zgodnie ze specyfikacją! – zwróci wartość „5.0”. Błędy przekroczenia zakresu obu zmiennych zniosły się i fakt ten nie został wykryty. Rozważmy teraz przypadki PT1′ i PT2′. Po

uruchomieniu pierwszego z nich warunek w linii 1. nie zostanie spełniony. Sterowanie dojdzie do linii 4., w której nastąpi błąd przepełnienia zakresu zmiennej pktRazem, która ma typ byte, a więc jej zakres wynosi od 0 do 255. Dzięki pokrywaniu tylko jednej klasy niepoprawnej w jednym przypadku testowym udało się wykryć defekt. Miarą pokrycia w metodzie podziału na klasy równoważności jest pokrycie klas równoważności. pokrycie klas równoważności (ang. equivalence partition coverage) – odsetek klas równoważności, które zostały sprawdzone przez zestaw testów

8.1.5. Przykład Rozważamy program TypTrójkąta2 pochodzący z [14]. Na wejściu otrzymuje on trzy liczby całkowite a, b, c oznaczające długości odcinków. Na wyjściu (oznaczmy je jako zmienną t) ma określić, jaki typ trójkąta tworzą te trzy odcinki. Możliwe odpowiedzi to: „równoboczny”, „równoramienny”, „różnoboczny” oraz „nie trójkąt”. Ostatnia odpowiedź powinna być zwracana w przypadku, gdy co najmniej jedno z wejść nie jest liczbą całkowitą lub gdy zmienne wejściowe nie spełniają warunku trójkąta, tzn. gdy istnieją wśród tych liczb dwie takie, że ich suma jest mniejsza lub równa trzeciej. Krok 1. Określenie przedmiotu i elementów testów. Mamy tylko jeden przedmiot testowy – program TypTrójkąta. Jest on jednocześnie jedynym elementem testów: ET1: TypTrójkąta. Krok 2. Wyprowadzenie warunków testowych. Warunki testowe wyprowadzimy przez identyfikację charakterystyk programu. Możemy tu posłużyć się specyfikacją wymagań lub innym dokumentem opisującym funkcjonalność programu. Charakterystyki określą klasy równoważności, które w tej metodzie są jednocześnie warunkami testowymi. Intuicja podpowiada nam, że skoro program ma określać typ trójkąta, to najprostszą charakterystyką będzie właśnie ten typ. Jest to przykład charakterystyki opisującej dziedzinę wyjściową programu i odpowiada wartościom zmiennej t:

Charakterystyka „typ trójkąta”: równoboczny, równoramienny, różnoboczny, nie trójkąt. Charakterystyka powinna dokonać podziału dziedziny wejściowej (czyli zbioru wszystkich możliwych trójek liczb naturalnych) na cztery klasy równoważności. Zauważmy jednak, że każdy trójkąt równoboczny jest jednocześnie trójkątem równoramiennym, zatem jedna klasa całkowicie zawiera się w drugiej. Zdefiniowana przez nas charakterystyka nie tworzy poprawnego podziału! Musimy wprowadzić modyfikację polegającą na wyłączeniu trójkątów równobocznych z klasy trójkątów równoramiennych: Charakterystyka Ch1: „typ trójkąta”: „równoboczny”, „równoramienny i nie równoboczny”, „różnoboczny”, „nie trójkąt”. Tak zdefiniowana charakterystyka wyznacza już poprawny podział dziedziny wyjściowej na klasy równoważności. Przy okazji odkryliśmy defekt (niejednoznaczność) w specyfikacji polegający na tym, że trójkątom o trzech równych bokach można przypisać dwa typy: równoboczny i równoramienny. Specyfikacja powinna w wyraźny sposób stwierdzać, że z typu „równoramienny” należy wyłączyć trójkąty równoboczne. Wyjście „równoramienny” traktować więc będziemy jako „równoramienny i nie równoboczny”. Możemy dodatkowo określić klasy niepoprawności dla wyjścia. Nieprawidłowa wartość wyjścia to każda wartość inna od czterech zdefiniowanych powyżej. Jeśli uda się uzyskać wartość nieprawidłową na wyjściu, oznacza to istnienie defektu w przedmiocie testów, podstawie testów lub w obu tych artefaktach. Określanie wyjść nieprawidłowych jest rzeczą subiektywną, zależy od testera i jego znajomości testowanego programu. Możemy np. zdefiniować klasę niepoprawności null, czyli sytuację, w której program nie zwraca żadnego łańcucha znaków na wyjściu. Istnieje 5 klas, dlatego wyznaczają one 5 warunków testowych opisanych w tabeli 8.2. Tabela 8.2. Zestaw warunków testowych dla programu TypTrójkąta na podstawie charakterystyki „typ trójkąta”

Nr Odpowiadający warunku element W arunek testowy testowego testów

WT1

ET1

Trójkąt równoboczny (wymus zony przez a = b = c)

WT2

ET1

Trójkąt równoramienny (wymus zony przez (a = b ∨ a = c ∨ b = c) ∧~(a = b = c))

WT3

ET1

Trójkąt różnoboczny (wymus zony przez a ≠ b ∧ a ≠ c ∧ b ≠ c)

WT4

ET1

Nie trójkąt (wymus zony przez a + b < c ∨ a + c < b ∨ b + c < a lub przez to, że co najmniej jedno wejś cie nie jes t typu int)

WT5

ET1

null

Intuicyjnie czujemy, że np. przypadek testowy (4, 4, 4), pokrywający warunek testowy WT1, powinien spowodować podobne działanie programu jak (6, 6, 6), (11, 11, 11) czy każdy inny przypadek opisujący trzy identyczne liczby całkowite dodatnie na wejściu. Inne podejście do problemu może polegać na analizie dziedziny wejściowej. Skoro specyfikacja mówi, że na wejściu program spodziewa się trzech liczb całkowitych dodatnich, to możemy zdefiniować trzy następujące charakterystyki: relacja a do zera, relacja b do zera oraz relacja c do zera. Dodatkowo, dla każdej zmiennej wprowadźmy dwie klasy niepoprawności „liczba niecałkowita” oraz „nie liczby” (czyli ciągi znaków zawierające litery lub znaki specjalne). Dla każdej charakterystyki otrzymujemy więc następujące podziały: Ch2: a ≤ 0, a ≥ 1, a nie jest całkowite, a nie jest liczbą; Ch3: b ≤ 0, b ≥ 1, b nie jest całkowite, b nie jest liczbą; Ch4: c ≤ 0, c ≥ 1, c nie jest całkowite, c nie jest liczbą. Otrzymaliśmy 3 podziały (każdy z czterema klasami równoważności) dla trzech dziedzin będących zbiorami wartości poszczególnych wejść do programu. W sumie mamy 12 klas równoważności (tab. 8.3).

Tabela 8.3. Zestaw warunków testowych dla programu TypTrójkąta na podstawie charakterystyk Ch2, Ch3 i Ch4

Nr warunku testowego

Odpowiadający przedmiot testów

Charakterystyka

W arunek testowy

WT6

ET1

Ch2

a ≤ 0, a całkowite

WT7

ET1

Ch2

a ≥ 1, a całkowite

WT8

ET1

Ch2

a liczbą niecałkowitą

WT9

ET1

Ch2

a zawiera litery lub znaki

WT10

ET1

Ch3

b ≤ 0, b całkowite

WT11

ET1

Ch3

b ≥ 1, b całkowite

WT12

ET1

Ch3

b liczbą niecałkowitą

WT13

ET1

Ch3

b zawiera litery lub znaki

WT14

ET1

Ch4

c ≤ 0, c całkowite

WT15

ET1

Ch4

c ≥ 1, c całkowite c liczbą

WT16

ET1

Ch4

niecałkowitą

WT17

ET1

Ch4

c zawiera litery lub znaki

Warunki testowe 8, 9, 12, 13, 16, 17 reprezentują klasy niepoprawności. Pozostałe reprezentują klasy poprawności. Krok 3. Wyprowadzenie elementów pokrycia. Elementy pokrycia (klasy równoważności) są jednocześnie warunkami testowymi. Mamy zatem następujących 17 elementów pokrycia: EP1: wyjście „równoboczny” wymuszone przez a = b = c (wyprowadzony z WT1); EP2: wyjście „równoramienny” wymuszone przez (a = b ∨ a = c ∨ b = c) ∧ ~(a = b = c) (z WT2); EP3: wyjście „różnoboczny” wymuszone przez a ≠ b ∧ a ≠ c ∧ b ≠ c (z WT3); EP4: wyjście „nie trójkąt” wymuszone przez a + b < c ∨ a + c < b ∨ b + c < a lub przez to, że co najmniej jedno wejście nie jest typu int (z WT4); EP5: null (z WT5); EP6: a ≤ 0, całkowite (z WT6); EP7: a ≥ 1, całkowite (z WT7); EP8: a liczbą niecałkowitą (z WT8); EP9: a zawiera litery lub znaki specjalne (z WT9); EP10: b ≤ 0, całkowite (z WT10); EP11: b ≥ 1, całkowite (z WT11); EP12: b liczbą niecałkowitą (z WT12); EP13: b zawiera litery lub znaki specjalne (z WT13); EP14: c ≤ 0, całkowite (z WT14); EP15: c ≥ 1, całkowite (z WT15); EP16: c liczbą niecałkowitą (z WT16); EP17: c zawiera litery lub znaki specjalne (z WT17). Krok 4. Zdefiniowanie przypadków testowych. Po zdefiniowaniu warunków testowych i elementów pokrycia przechodzimy do tworzenia przypadków

testowych. Stosując opisaną w punkcie 8.1.1 procedurę, staramy się, aby każdy kolejny przypadek testowy pokrywał jak najwięcej niepokrytych dotąd poprawnych klas równoważności, a następnie – aby każdy kolejny przypadek pokrywał dokładnie jedną klasę niepoprawności. Rezultat tego postępowania jest przedstawiony w tabeli 8.4. Kolejność tworzenia przypadków wyznaczają ich identyfikatory. Pogrubioną czcionką zaznaczono elementy pokryte po raz pierwszy. Tabela 8.4. Przypadki testowe pokrywające klasy równoważności dla programu TypTrójkąta

W ejście T est

Pokryte elementy pokrycia

b

c

dot. klas poprawności

PT1

3

3

3

EP1, EP7, EP11, EP15

Równoboczny

PT2

–3

–3 –3

EP4, EP6, EP10, P14

Nie trójkąt

PT3

5

7

5

EP2, EP7, EP11, EP15

Równoramienny

PT4

4

5

6

EP3, EP7, EP11, EP15

Różnoboczny EP5

null

PT6

6.33 4

0

EP11, EP15

EP8

Nie trójkąt

PT7

1

– 3 3.7

EP7, EP15

EP12

Nie trójkąt

PT8

–1

5

8.94 EP6, EP11

EP16

Nie trójkąt

PT9

xy

4

2

EP9

Nie trójkąt

PT5

EP11, EP15

dot. klas niepoprawn.

Oczekiwane wyjście

a

PT10 –9

B4 4

EP6, EP15

EP13

Nie trójkąt

PT11 0

0

abc EP6, EP10

EP17

Nie trójkąt

Przypadki PT1–PT4 pokrywają wszystkie klasy poprawności wszystkich czterech charakterystyk. Przypadek PT5 pokrywa klasę niepoprawności charakterystyki Ch1, a przypadki PT6–PT11 – klasy niepoprawności charakterystyk Ch2, Ch3 i Ch4. Zauważmy, że przypadki PT6–PT11 pokrywają jednocześnie klasę poprawności jednej charakterystyki i klasę niepoprawności innej charakterystyki. Jest tak dlatego, że charakterystyki te są od siebie niezależne i jedna dotyczy wejścia, a druga wyjścia. Wyjście programu (opisane charakterystyką nr 1) przewiduje możliwość wpisania błędnych danych wejściowych („nie trójkąt”), więc stanowi ona klasę poprawności dla dziedziny wyjściowej. Jednocześnie – zgodnie ze specyfikacją – oczekuje na wejściu liczb całkowitych, więc każde wejście niebędące liczbą całkowitą należy do jednej z dwóch zdefiniowanych przez nas klas niepoprawności. Jedenaście testów zdefiniowanych w tabeli 8.4 pokrywa wszystkie zdefiniowane klasy równoważności (warunki testowe), zatem pokrycie klas równoważności wynosi 100%. Gdybyśmy ograniczyli zbiór testów do dwóch przypadków PT1 i PT2, to pokrycie dla takiego zbioru wyniosłoby 8/17 = 46%, ponieważ przypadki te w sumie pokrywają 8 elementów pokrycia z 17 możliwych. Krok 5. Stworzenie suit testowych. Grupowanie przypadków testowych w suity (zbiory) testowe może zależeć od różnych czynników. Jeśli np. wiemy, że można zautomatyzować weryfikację wyniku rzeczywistego z oczekiwanym tylko w sytuacji, gdy wejście lub wyjście należy do jednej z klas poprawności, to możemy stworzyć dwie suity testowe. Pierwsza złożona będzie z przypadków poddających się automatyzacji, druga – z przypadków wykonywanych manualnie: ST1: Automatyczne testowanie – PT1, PT2, PT3, PT4; ST2: Testy manualne – PT5, PT6, PT7, PT8, PT9, PT10, PT11. Krok 6. Stworzenie procedur testowych. Dla testów automatycznych procedura testowa może wyglądać następująco:

PROC1: Testowanie automatyczne suity testowej ST1 przy użyciu skryptu, zgodnie z kolejnością, w jakiej testy pojawiają się w ST1. Dla testów manualnych procedura testowa może wyglądać następująco: PROC2: Testowanie manualne suity testowej ST2, w kolejności występowania testów w ST2.

8.1.6. Przykład śledzenia artefaktów procesu testowego Każdy artefakt wytworzony podczas opisanego wcześniej procesu może być powiązany z innymi artefaktami. W punkcie 3.1.3 opisaliśmy rolę tzw. obustronnego śledzenia, które zapewnia to powiązanie. Na rysunku 8.3 pokazano obustronne śledzenie między elementami testowymi, warunkami testowymi i elementami pokrycia, a także między przypadkami testowymi a suitami testowymi dla naszego przykładu z punktu 8.1.5. Brakujący element na tym rysunku – śledzenie między elementami pokrycia a przypadkami testowymi – jest pokazany w tabeli 8.5 w formie tzw. macierzy identyfikowalności (ang. traceability matrix). Każdy wiersz macierzy odpowiada jednemu elementowi pokrycia, a każda kolumna – przypadkowi testowemu. Znak „X” na przecięciu wiersza i oraz kolumny j oznacza, że przypadek testowy PTj pokrywa element pokrycia EPi.

Rysunek 8.3. Obustronne śledzenie między artefaktami procesu testowego

Tabela 8.5. Macierz identyfikowalności dla elementów pokrycia i przypadków testowych

PT 1 PT 2 PT 3 PT 4 PT 5 PT 6 PT 7 PT 8 PT 9 PT 10 PT 11 EP1

X

EP2

X

EP3

X

EP4

X

EP5

X

EP6 EP7

X X

X X

X

EP8

X X X

EP11 X

X X

X

X

EP12

X

X

X

EP13 EP14 EP15 X EP16 EP17

X

X

EP9 EP10

X

X X X

X

X

X

X

X

X X

W rozważaniach tych wszystkie charakterystyki były jednowymiarowe, to znaczy dotyczyły jednej zmiennej. Dla przypadków wielowymiarowych oraz

kryteriów kombinacyjnych istnieją osobne, specjalne wersje metody podziału na klasy równoważności. Są one opisane w podrozdziałach 8.8 oraz 8.9.

8.2. Analiza wartości brzegowych 8.2.1. Opis metody Technika analizy wartości brzegowych AWB (ang. Boundary Value Analysis, BVA) jest oparta na metodzie podziału na klasy równoważności. Tak jak w tej ostatniej dziedzinę wejściową lub wyjściową dzieli się na klasy, ale różnica polega na tym, że wybierane do testów elementy leżą na brzegach tych klas. Istnienie brzegów implikuje również, że metoda analizy wartości brzegowych działa tylko dla zmiennych, które da się opisać na skali porządkowej. Innymi słowy, na wartościach tych zmiennych można określić relację porównującą elementy ze sobą. To z kolei ogranicza stosowanie analizy wartości brzegowych do przypadków jednowymiarowych. Przypadki wielowymiarowe opiszemy w podrozdziale 8.9. Ponadto, jeśli klasa równoważności zawiera elementy a i b (a < b), to musi zawierać również wszystkie elementy większe od a i mniejsze od b. Oznacza to, że klasa równoważności musi zawierać zwarty zbiór wartości. Nie może być „przedzielona” inną klasą. Przykłady zbiorów, dla których można przeprowadzić AWB: podzbiór zbioru liczb rzeczywistych (np. typ double); podzbiór zbioru liczb całkowitych (np. typ int); podzbiór znaków (jeśli np. traktujemy znaki jako liczby kodu ASCII, umożliwia to porównanie ich między sobą); zbiór enumeratywny na skali porządkowej (np. poziom zadowolenia na skali typu Likerta: „bardzo niezadowolony”, „niezadowolony”, „obojętny”, „zadowolony”, „bardzo zadowolony”). Hipoteza błędu polega tu na tym, że programista może się pomylić w stosowaniu operatorów porównań i np. zamiast silnej nierówności użyć słabej. Inną możliwością jest np. przetwarzanie tablicy począwszy od elementu o indeksie jeden zamiast zero. Tego typu błędy czasami nazywa się „pomyłkami o jeden”. Podstawowym pojęciem tej techniki jest wartość brzegowa. Wartość brzegowa zawsze dotyczy konkretnego przedziału. Jest to liczba zawarta w tym przedziale

i leżąca na jego granicy. Granicami mogą być także wartości ekstremalne zakresu zmiennej, dla której rozważamy podziały. Słownik ISTQB [6] definiuje wartość brzegową w następujący sposób: „wartość brzegowa – wartość wejścia lub wyjścia, która leży na granicy klas równoważności; również minimalna lub maksymalna wartość zakresu”. Pojęcie „leżenia na granicy klas” jest nie tylko nieprecyzyjne, lecz także wręcz błędne (i może wprowadzać pewne zamieszanie – patrz p. 8.2.3), ponieważ wartość brzegowa zawsze należy do konkretnej klasy. Pamiętajmy, że klasy równoważności pokrywają całą dziedzinę i każdy element należy do dokładnie jednej z nich. Oczywiście dla zmiennej x typu int i warunku x > 5 można zdefiniować granicę jako wartość 5.5, oddzielającą klasę [MININT, 5] od klasy [6, MAXINT]. Jest to jednak dosyć sztuczne, bo: po pierwsze nie ma sensu używać wartości nienależących do dziedziny; po drugie można zapytać, dlaczego akurat wartością tą ma być 5.5, a nie np. 5.89 albo 5.003; po trzecie – jak zobaczymy w punkcie 8.2.2 – nie bardzo wiadomo, z którego z dwóch sąsiadujących z tą wartością przedziałów należy brać wartości graniczne jako elementy pokrycia. Kolejna uwaga, jaką można mieć do powyższej definicji, jest taka, że wartość brzegowa powinna być zdefiniowana jako wartość brzegowa dla konkretnego przedziału, a nie sugerować, że dotyczy ona więcej niż jednej klasy równoważności! Tak postawiona definicja może bowiem wprowadzić testera w pewną konfuzję, o której piszemy w dalszych podrozdziałach. Przyjmiemy następującą, prostą i naturalną definicję wartości brzegowej: wartość brzegowa dla przedziału X (ang. boundary value) – najmniejsza lub największa wartość należąca do X Przedział może być wyznaczony przez klasę równoważności, ale musimy pamiętać, że klasa ta nie może mieć „dziur” – musi być „zwarta”. Jeśli przedział jest ograniczony obustronnie, to ma dwie wartości brzegowe. Na przkład przedział [a, b] ma wartości brzegowe a oraz b. Przedziały jednostronnie ograniczone – najczęściej występujące w predykatach instrukcji warunkowych – mają tylko jedną wartość brzegową, chyba że uwzględnimy maksymalne i minimalne wartości dopuszczane przez reprezentację komputerową. Na przykład przedział opisany warunkiem x > a dla zmiennej x typu int ma wartość brzegową a + 1, ponieważ jest to najmniejsza liczba należąca do przedziału [a + 1,

∞) wyznaczonego tym warunkiem. Biorąc pod uwagę reprezentację liczb w komputerze, możemy przyjąć drugą wartość brzegową jako MAXINT. Przy określaniu wartości brzegowych bardzo ważny jest typ zmiennej występującej w warunku definiującym przedział. Tę kwestię omówimy dokładnie w punkcie 8.2.4. analiza

wartości

brzegowych

(ang.

boundary

value

analysis)



czarnoskrzynkowa metoda projektowania przypadków testowych, w której przypadki te są tworzone na podstawie wartości brzegowych W rozdziale przedstawimy metodę AWB na dosyć prostych przykładach. Może to sprawiać wrażenie, że jest ona prostą, banalną techniką, niespecjalnie pomocną w projektowaniu przypadków testowych. Metoda ta jest jednak dużo bardziej potężna, niż to się może wydawać. Niestety wiele podręczników (a także sylabusy i treści pytań egzaminacyjnych ISTQB) trywializuje pojęcie wartości brzegowych. Zaawansowane zastosowanie tej techniki omówimy w podrozdziale 8.9, a w punkcie 20.1.3 pokażemy przykład wymagań prowadzących do nieoczywistych wartości brzegowych, których odkrycie wymaga dalece bardziej wysublimowanej analizy, niż ta stosowana w tym rozdziale.

8.2.2. Metody dwóch oraz trzech wartości granicznych Wartości brzegowe stanowią warunki testowe, z których wyprowadza się elementy pokrycia. Istnieją dwie powszechnie przyjęte metody tego wyprowadzenia: metoda dwóch wartości granicznych; metoda trzech wartości granicznych. Omówimy je na przykładzie. Załóżmy, że chcemy przeprowadzić analizę wartości brzegowych dla następującego wymagania programu ZniżkaMPK przydzielającego różnego typu zniżki na bilety komunikacji miejskiej: „zniżka na bilet okresowy przysługuje osobom do 18 roku życia”, przy czym zmienna określająca wiek jest typu int. Pierwszym krokiem jest określenie klas równoważności. Naturalnymi kandydatami są: klasa

reprezentująca wiek osób uprawnionych do zniżki (liczby całkowite {0, 1, 2, …, 17, 18}) oraz klasa reprezentująca wiek osób nieuprawnionych do zniżki (liczby całkowite od 19 wzwyż). Możemy zdefiniować również klasę niepoprawności, reprezentującą wartości ujemne. W rezultacie dziedzina zmiennej została podzielona na trzy klasy równoważności X1, X2, X3 tak, jak to przedstawiono na rysunku 8.4.

Rysunek 8.4. Klasy równoważności i wartości brzegowe dla programu ZniżkaMPK Klasa X2 reprezentuje przedział całkowity [0, 18]. Jedną z wartości brzegowych klasy X2 jest 18. W metodzie dwóch wartości granicznych jako elementy pokrycia bierze się oryginalną wartość brzegową (w naszym przypadku 18) oraz najbliższą jej wartość leżącą poza klasą X2. Taką wartością jest 19, należące już do X3.

W metodzie trzech wartości granicznych oprócz tych dwóch liczb bierze się dodatkowo wartość najbliższą brzegowej, ale leżącą po jej drugiej stronie. W naszym przypadku jest to wartość 17. Jeśli rozważana klasa równoważności jest złożona z więcej niż jednego elementu, to wartość ta należy do tej samej klasy, co wartość brzegowa. W przeciwnym wypadku należy ona do innej klasy. Podsumowując, dla klasy X2 i wartości brzegowej 18 (dla warunku 0 ≤ wiek ≤ 18) mamy następujące elementy pokrycia:

dla metody dwóch wartości granicznych: 18, 19; dla metody trzech wartości granicznych: 17, 18, 19. Ilustracja graficzna porównania tych dwóch metod jest pokazana na rysunku 8.5. Wartość brzegowa klasy X2 jest zaznaczona kółkiem. Formalnie, dana niech będzie jednowymiarowa dziedzina D i ustalona klasa równoważności X ⊆ D reprezentująca przedział. Wartościami brzegowymi dla X są elementy min{X} i max{X} oznaczające odpowiednio najmniejszy i największy element zbioru X. Zauważmy, że takie elementy zawsze istnieją, nawet dla

zmiennych rzeczywistych, w komputerze.

ze

względu

na

dyskretną

reprezentację

liczb

Rysunek 8.5. Metody dwóch oraz trzech wartości granicznych Elementami pokrycia dla wartości brzegowej min{X} są: {min{X}, max{x: x < min{X}}} dla metody dwóch wartości granicznych; {min{X}, max{x: x < min{X}}}, min{x: x > min{X}}} dla metody trzech wartości granicznych. Analogicznie, elementami pokrycia dla wartości brzegowej max{X} są: {max{X}, min{x: x > max{X}}} dla metody dwóch wartości granicznych; {max{X}, min{x: x > max{X}}, max{x: x < max{X}}} dla metody trzech wartości granicznych. Jak widać, metoda trzech wartości granicznych jest silniejsza od metody dwóch wartości granicznych, gdyż jej elementy pokrycia są nadzbiorem elementów pokrycia tej ostatniej. Można jednak zadać pytanie: jeśli sprawdzamy warunek wiek ≤ 18 dla wartości brzegowej 18, to po co sprawdzać dodatkowo wartość 17? Czy poprawność działania testu dla przypadku 18 nie implikuje poprawności działania testu dla przypadku 17? Niestety nie możemy tego zagwarantować. Programista mógłby warunek wiek ≤ 18 zaimplementować

błędnie jako wiek = 18. Wtedy testy stworzone za pomocą metody dwóch wartości granicznych nie wykryłyby defektu, bo dla wartości 18 i 19 powyższy warunek, choć błędny, dałby poprawne wartości logiczne, tak jak warunek poprawny wiek ≤ 18. Dodanie do testów wartości 17 wykryłoby defekt, ponieważ poprawny warunek dla wiek = 17 powinien zachodzić (bo 17 ≤ 18 jest prawdą), natomiast w błędnej implementacji nie zajdzie (bo 17 = 18 jest fałszem). Niektórzy autorzy (np. Rex Black [93]) uważają, że używanie metody trzech wartości granicznych to strata czasu. O ile można się do pewnego stopnia zgodzić z tezą, że stosowanie tej metody nie zwiększa istotnie prawdopodobieństwa wykrycia błędu, o tyle – w świetle tego przykładu – trudno przychylić się do kategorycznych ocen mówiących o jej bezużyteczności. Stosowanie metody trzech wartości granicznych generuje większą liczbę przypadków testowych i jeśli tylko tester jest w stanie wykonać je w rozsądnym czasie, to może ją stosować. Wybór metody zależy od różnych czynników, w tym od poziomu dopuszczalnego ryzyka. Metoda trzech wartości granicznych sprawia jednak pewne problemy, które dyskutujemy w punkcie 8.2.3. Należy pamiętać, że wartościami brzegowymi są również wartości leżące na granicach zakresu zmiennych. Wartości te zwykle nie są podawane jawnie w specyfikacji. Na przykład specyfikacja dla programu ZniżkaMPK sugeruje, że wiek może być dowolnie duży, jednak zmienna reprezentująca go w implementacji zawsze będzie mieć ograniczony zakres. Jeśli jest to zmienna typu byte, zapisywana na 8 bajtach, to jej maksymalna wartość wynosi 255. Poprawne wartości brzegowe oraz poprawne elementy pokrycia to te, które należą do klas poprawności. Analogicznie, niepoprawne wartości brzegowe oraz niepoprawne elementy pokrycia to te, które należą do klas niepoprawności. Miarą pokrycia w metodzie analizy wartości brzegowej jest liczba pokrytych testami elementów pokrycia w stosunku do wszystkich zidentyfikowanych elementów pokrycia. W tabeli 8.6 przedstawiono podsumowanie metody analizy wartości brzegowych. pokrycie wartości brzegowych – odsetek wyprowadzonych z wartości brzegowych elementów pokrycia, który został pokryty testami Tabela 8.6. Podsumowanie metody analizy wartości brzegowych

Warunki tes towe

Zbiór ws zys tkich wartoś ci brzeg owych, czyli g ranic zidentyfikowanych klas równoważnoś ci

Zbiór ws zys tkich wartoś ci g ranicznych (po 2 dla wartoś ci Elementy brzeg owej w przypadku metody dwóch wartoś ci pokrycia g ranicznych lub po 3 w przypadku metody trzech wartoś ci g ranicznych) Kryterium Dla każdej wartoś ci g ranicznej is tnieje przynajmniej jeden pokrycia tes t, który wykorzys tuje tę wartoś ć Pokrycie

p = (liczba pokrytych tes tami wartoś ci g ranicznych/liczba ws zys tkich wartoś ci g ranicznych) × 100%

8.2.3. Które wartości rozważać jako brzegowe? Rozważmy prosty przykład. Dla warunku x ≤ 6 mamy dwie klasy równoważności: K1: MININT ≤ x ≤ 6 oraz K2: 6 ≤ x ≤ MAXINT. Jedną z wartości brzegowych dla tego warunku jest 6. W tym momencie uważny Czytelnik powinien zauważyć pewną subtelność. Warunek x ≤ 6 jest przecież równoważny warunkowi x < 7, co daje klasy równoważności MININT ≤ x < 7 oraz 7 ≤ x ≤ MAXINT. Są to dokładnie te same klasy równoważności. Dlaczego więc w przypadku klas K1 i K2 jako wartość brzegową wybieramy 6, a nie 7? Odpowiedź jest prosta – wartością brzegową jest wartość leżąca na brzegu konkretnego przedziału, zawarta w tym przedziale. Nasz przedział wyznaczony jest warunkiem x ≤ 6, czyli jest to przedział (–∞, 6], zatem największą wartością należącą do niego jest liczba 6, tak samo, jak dla (identycznego) przedziału wyznaczonego przez warunek x < 7. Jeśli chcemy rozważać wartości brzegowe przedziału dopełniającego, czyli x > 6, to dla niego wartością brzegową jest oczywiście 7. W przypadku metody dwóch wartości granicznych dyskusja ta nie ma żadnego znaczenia, bo niezależnie od tego, czy za wartość brzegową przyjmiemy element graniczny rozważanej klasy (6), czy też element graniczny klasy sąsiedniej (7), będzie to skutkować dokładnie takim samym zbiorem elementów pokrycia, tzn. {6, 7}. Zbiór wszystkich wartości brzegowych wszystkich klas równoważności będzie zawsze równy zbiorowi wszystkich wartości granicznych dla tych wartości brzegowych.

Gdy rozważamy pojedynczą klasę równoważności, wyznaczanie wartości granicznych nie sprawia żadnych problemów. Jednak tester zazwyczaj sprawdza wartości

graniczne

wszystkich

zidentyfikowanych

klas

równoważności,

w szczególności klas sąsiadujących ze sobą. W takiej sytuacji, stosując metodę trzech wartości granicznych stajemy przed pewnym problemem. Jeśli dla warunku x ≤ 6 przyjmiemy 6 za wartość brzegową, to elementami pokrycia będą wartości graniczne 5, 6 i 7. Dla sąsiedniej klasy wartością brzegową jest 7 i elementami pokrycia będą wtedy wartości graniczne 6, 7 i 8. Jeśli przyjmujemy obie te wartości brzegowe jako obowiązujące warunki testowe (bo np. chcemy testować wartości brzegowe wszystkich klas równoważności), to w takim przypadku metoda powinna raczej nazywać się metodą czterech wartości granicznych. Zawsze bowiem da w rezultacie zbiór czterech elementów pokrycia – elementy brzegowe oraz elementy bezpośrednio z nimi sąsiadujące (w naszym przykładzie jest to zbiór {5, 6, 7, 8}). Jeśli wartości brzegowych jest n, to w sumie do pokrycia testami otrzymamy liczbę wartości granicznych rzędu3 2n – ok. dwa razy więcej niż dla metody dwóch wartości granicznych. To zmusza nas do stworzenia większej liczby testów, co nie zawsze jest pożądane. Aby wybrnąć z tego problemu (zostając przy metodzie trzech wartości granicznych i nie generując zbyt dużej liczby przypadków testowych), rozważając wartości brzegowe w kontekście dwóch sąsiadujących przedziałów, za wartość brzegową można przyjąć wartość występującą w specyfikacji. Na przykład jeśli pojawia się w niej sformułowanie: „bezpłatny przejazd przysługuje dzieciom do lat 6”, to mamy do czynienia z przedziałami wyznaczonymi warunkami x ≤ 6, x ≥ 7, ale interesującą nas wartością brzegową będzie 6, czyli wartość brzegowa dla klasy x ≤ 6, gdyż występuje ona jawnie w specyfikacji. Liczby 7 nie rozważa się wtedy jako wartości brzegowej. Stosując metodę trzech wartości granicznych jako elementy pokrycia otrzymamy jednoznacznie wyznaczony zbiór {5, 6, 7}. W ogólności, dla n wartości brzegowych liczba elementów pokrycia będzie rzędu 3n/2, czyli o ok. 25% mniejsza niż dla przypadku 2n. Takie podejście do tworzenia elementów pokrycia nazywać będziemy techniką selektywną. Jeśli hipoteza błędu dotyczy źle zastosowanego operatora relacyjnego, to w kontekście analizy wartości brzegowych lepiej jest definiować wymagania za pomocą nierówności ostrych lub – przyjmując metodę trzech wartości granicznych – stosować technikę selektywną. Pokazuje to następujący przykład. Rozważmy występujący w specyfikacji warunek x ≤ p i równoważną mu postać x
p +1

+

+

+

+

x ≥ p +1

+

+

+

+

W tabeli 8.7 przedstawiono wyniki dla obu wersji implementacji warunku oraz do czterech wymienionych powyżej technik identyfikacji wartości granicznych. Plus oznacza, że w danej sytuacji defekt zostanie wykryty, to znaczy, że któraś z wartości granicznych po podstawieniu do (błędnego) warunku da inną wartość logiczną niż warunek poprawny. Minus oznacza, że defekt pozostanie nieujawniony, tzn. każda wartość graniczna da tą samą wartość logiczną zarówno dla poprawnego, jak i błędnego warunku. Zauważmy, że metoda trzech wartości granicznych z użyciem techniki selektywnej oraz metoda trzech wartości granicznych dla obu wartości brzegowych pozwalają na wykrycie defektu niezależnie od rodzaju popełnionej przez programistę pomyłki oraz od wersji implementacji. Ponadto wszystkie cztery techniki identyfikacji wartości granicznych pozwolą na ujawnienie defektu niezależnie od rodzaju popełnionej pomyłki, jeśli stosujemy warunek z ostrą nierównością (m.in. dlatego w warunkach pętli zaleca się stosowanie właśnie tej konwencji, np. for (i=0; i 0, bo wyrażenie „ostrzeżenie jest większe od braku ostrzeżenia” nie ma sensu. Krok 3. Wyprowadzenie elementów pokrycia. Z każdej wartości brzegowej wyprowadzamy dwa elementy pokrycia (wartości graniczne): jednym jest sama wartość brzegowa, drugim – najbliższa jej wartość należąca do sąsiedniej klasy równoważności. Pamiętajmy, że zmienne s, t oraz v przyjmują wartości rzeczywiste. Droga (w m) jest podana z dokładnością do centymetra, czyli z dokładnością do 1/100 = 0.01 m, zatem εs = 10–2. Czas (w sekundach) jest podany z dokładnością do setnej sekundy. Zatem minimalna wartość, o jaką może zmienić się t to 1/100 = 0.01 s. Możemy więc bezpiecznie przyjąć εt = 10–2. Prędkość v stanowi zmienną typu double, jest wyliczana jako iloraz drogi i czasu, przy czym według specyfikacji czas t jest nie większy niż 1 sekunda. Zmienna v nie powinna zmieniać się o więcej niż iloraz minimalnego skoku drogi i maksymalnego skoku prędkości, czyli 10–2 m/1 s = 10–2 m/s. Możemy więc przyjąć ev = 10–2.

EP1: s = MINDBL (z WT1); EP2: s = –0.01 (z WT2); EP3: s = 0 (z WT2); EP4: s = MAXDBL (z WT3); EP5: t = MINDBL (z WT4); EP6: t = –0.01 (z WT5); EP7: t = 0 (z WT5); EP8: t = 0.01 (z WT5); EP9: t = 1 (z WT6); EP10: t = 1.01 (z WT6); EP11: t = MAXDBL (z WT7); EP12: v = MINDBL (z WT8); EP13: v = –0.01 (z WT9); EP14: v = 0 (z WT9); EP15: v = 0.01 (z WT9); EP16: v = 120 (z WT10); EP17: v = 120.01 (z WT10); EP18: v = MAXDBL (z WT11), Elementy pokrycia EP1–EP11 dotyczą wartości dziedziny wejściowej. Elementy EP12–EP18 dotyczą wartości dziedziny wyjściowej. Niepoprawnymi elementami pokrycia są elementy EP1, EP2, EP5, EP6 i EP10, EP11, EP12, EP13. Pozostałe, ponieważ należą do klas poprawności, są elementami poprawnymi. Krok 4. Zdefiniowanie przypadków testowych. Ze względu na instrumentację kodu opisaną w poprzednim kroku zmienne wewnętrzne total_t i total_s traktujemy jak dane wejściowe. Zastosujemy technikę minimalizującą dla poprawnych wartości granicznych i jeden-do-jednego dla niepoprawnych. Innym podejściem mogłoby być np. wykorzystanie technik kombinacyjnych opisanych w podrozdziale 8.8. Tabela 8.8. Przypadki testowe w analizie wartości brzegowych dla programu CarPanel

Oczekiwane wyjścia

Id

(WARN = 1 gdy v > Pokryte elementy 120) pokrycia

W ejścia

s

t

v = s/t

WARN

PT1

0

0

0

0

EP3, EP7, EP14

PT2

120

1

120

0

EP9, EP16

PT3

MAXDBL 1

MAXDBL

1

EP4, EP18, EP9

PT4

1

0.01

0.01

0

EP8, EP15

PT5

120.01

1

120.01

1

EP17, EP9

PT6

MINDBL

2

MINDBL/2 0

EP1

PT7

–0.01

0.02

–2

0

EP2

PT8

2

MINDBL

2/MINDBL

0

EP5

PT9

–40

0.4

0

EP6

121.2

PT10 120

1.01

1

EP10

PT11 2

MAXDBL 2/MAXDBL 0

EP11

2⋅ PT12 MINDBL

2

MINDBL

0

EP12

PT13 –0.02

2

–0.01

0

EP13

Pogrubione elementy pokrycia w ostatniej kolumnie tabeli oznaczają, że dany element został pokryty po raz pierwszy. Jedynie element EP9 został pokryty trzykrotnie, w PT2, PT3 i PT5. Zestaw opisanych w tabeli 8.8 13 przypadków testowych spełnia w 100% kryterium pokrycia wartości brzegowych. Gdyby np. zbiór testowy ograniczyć tylko do przypadków PT1–PT5, pokrycie wyniosłoby 10/18 ≈ 56%.

Kolejny krok to stworzenie suit i procedur testowych. Można np. stworzyć osobną suitę testową dla testów wykorzystujących nieprawidłowe wartości i osobną dla wszystkich wartości wejściowych poprawnych.

8.3. Tablice decyzyjne 8.3.1. Opis metody Metody: podziału na klasy równoważności oraz analizy wartości brzegowej sprawdzają się dobrze przy dzieleniu zbioru możliwych wartości na klasy i rozważaniu każdej z nich z osobna. Podstawową słabością tych metod jest to, że nie uwzględniają one kombinacji różnych wartości wejściowych. Na przykład działanie programu CarPanel opisanego w poprzednim rozdziale może skutkować awarią, jeśli w jednym przypadku testowym przyjmiemy wartości wejściowe s = MAXDBL, t = 0.01, to wtedy wartość v = s/t przekroczy zakres zmiennej typu double. Tablice decyzyjne (omawiane w tym rozdziale) oraz grafy przyczynowo-skutkowe, techniki kombinacyjne, drzewa decyzyjne i analiza dziedziny opisane w podrozdziałach 8.4, 8.7, 8.8 i 8.9 pozwalają na testowanie programu pod kątem występowania różnych kombinacji wartości wejściowych. Hipoteza błędu w przypadku tablic decyzyjnych mówi, że błędy mogą powstawać na skutek nieprawidłowego obsłużenia sytuacji będącej wynikiem kombinacji różnych czynników. Tablice decyzyjne są techniką przydatną do testowania logiki biznesowej programu. Działanie wielu programów można opisać systemami regułowymi: jeśli wystąpią warunki X i Y, to należy wykonać akcję Z. Tablice decyzyjne pomagają w systematycznym tworzeniu przypadków testowych, z których każdy składa się z zestawu warunków (wejść) oraz akcji (wyjść), które powinny nastąpić, gdy te warunki zajdą. Są więc narzędziem, które pomaga stworzyć zbiór testów pokrywających logiczne związki między wejściami i wyjściami programu. W niektórych przypadkach umożliwiają również redukcję zbioru testów. tablica decyzyjna, przyczynowo-skutkowa tablica decyzyjna (ang. decision table, cause-effect decision table) – tablica opisująca kombinację wejść i/lub czynników (przyczyn) z odpowiadającymi im wyjściami i akcjami (skutkami), pomocna w projektowaniu przypadków testowych

Tablica decyzyjna jest tabelą, której kolumny oznaczają tzw. reguły decyzyjne5, z których tworzy się przypadki testowe. Wiersze są podzielone na dwie grupy. Pierwsza z nich to grupa warunków. Każdy wiersz tej grupy odpowiada jednemu warunkowi, czyli jednemu wejściu. Dalej znajduje się grupa akcji. Każdy wiersz tej części tabeli odpowiada jednej akcji, czyli jednemu wyjściu. Na przecięciu wierszy i kolumn znajdują się wartości poszczególnych warunków i akcji. Najczęściej są to wartości logiczne P (prawda) oraz F (fałsz), choć zarówno w przypadku warunków, jak i akcji mogą to być również inne wartości, np. liczby czy elementy określonego zbioru. Przykładowa postać tablicy decyzyjnej dla trzech warunków i trzech decyzji jest pokazana w tabeli 8.9. W tablicy tej reguła decyzyjna RD1 opisuje następujące zachowanie programu: jeśli zajdą warunki 1 oraz 3 i nie zajdzie warunek 2, to działanie programu powinno skutkować wykonaniem akcji 1 oraz 2 i nie wykonaniem akcji nr 3. Używając notacji logicznej, regułę tę można zapisać następująco: RD1 (warunek 1 ∧ ~warunek 2 ∧ warunek 3) ⇒ (akcja 1 ∧ akcja 2 ∧ ~akcja 3) Tablicę decyzyjną tworzy się zgodnie z następującym algorytmem: 1) na podstawie specyfikacji programu znajdź warunki oraz określ ich możliwe wartości; 2) na podstawie specyfikacji programu znajdź akcje, które są logicznie powiązane z warunkami oraz określ ich możliwe wartości; 3) wygeneruj wszystkie możliwe kombinacje warunków; 4) dla każdej kombinacji warunków określ, które akcje zachodzą, a które nie; 5) jeśli to konieczne, zminimalizuj tablicę. Tabela 8.9. Ogólna postać tablicy decyzyjnej

Reguły decyzyjne →

RD1

RD2

RD3



RDn

warunek1

P

P

F



F

warunek2

F

F

P



F

W arunki

warunek3

P

F

F



F

akcja1

P

F

P



F

akcja2

P

P

F



F

akcja3

F

F

P



F

Akcje

Jeśli mamy 3 warunki i każdy z nich może zachodzić (P) lub nie (F), to możliwych jest 23 = 8 kombinacji wartości tych warunków: PPP, PPF, PFP, PFF, FPP, FPF, FFP, FFF. W ogólności, jeśli mamy n warunków i warunek i-ty może przyjmować w i wartości, to liczba możliwych kombinacji wynosi W przypadku trudności z określeniem wszystkich możliwych kombinacji można posłużyć się prostą metodą drzewa. Dziećmi korzenia są wierzchołki odpowiadające wszystkim możliwym wartościom pierwszego warunku. Dziećmi każdego z nich są wierzchołki odpowiadające wszystkim możliwym wartościom drugiego warunku itd. Gdy zbudujemy całe drzewo (mające razem z korzeniem n + 1 poziomów), wartości leżące na każdej gałęzi biegnącej od korzenia do liścia reprezentują jedną kombinację wszystkich warunków. Rozważmy prosty przykład. Dane niech będą trzy warunki, z czego pierwszy i trzeci mogą przyjmować wartości logiczne prawda (P) lub fałsz (F), natomiast drugi może przyjmować wartość 10, 20 lub 30. Drzewo zbudowane według tych reguł jest pokazane na rysunku 8.6. Identyfikujemy wszystkie ścieżki biegnące od korzenia do liści, otrzymując następujący zbiór 2 ⋅ 3 ⋅ 2 = 12 kombinacji wartości trzech warunków: 1. (P, 10, P); 3. (P, 20, P); 5. (P, 30, P); 7. (F, 10, P); 9. (F, 20, P); 11. (F, 30, P); 2. (P, 10, F); 4. (P, 20, F); 6. (P, 30, F); 8. (F, 10, F); 10. (F, 20, F); 12. (F, 30, F).

Rysunek 8.6. Drzewo możliwych wartości wszystkich warunków Warunki oraz akcje tablicy decyzyjnej stanowią warunki testowe. Elementami pokrycia są reguły decyzyjne. Każda kolumna tablicy odpowiada jednej regule decyzyjnej, a więc i jednemu elementowi pokrycia. Kolejne przypadki testowe powstają przez wybór jednej z niepokrytych dotąd reguł decyzyjnych i przez określenie wejść gwarantujących spełnienie odpowiednich warunków oraz oczekiwanych wyjść na podstawie akcji tej reguły. pokrycie tablicy decyzyjnej (ang. decision table coverage) – odsetek kolumn tablicy decyzyjnej, dla których zdefiniowano i wykonano odpowiadające im przypadki testowe Podczas budowy tablicy decyzyjnej powinniśmy móc określić zachodzenie wszystkich akcji dla dowolnej kombinacji warunków. Jeśli istnieje kombinacja warunków, dla której nie możemy powiedzieć, czy dana akcja ma zajść, czy nie, oznacza to, że albo specyfikacja nie jest kompletna i należy ją poprawić, albo taka kombinacja warunków jest nieosiągalna (co również powinno być wyraźnie wyspecyfikowane w dokumentacji projektowej) – tę ostatnią sytuację omówimy dokładniej w następnym podrozdziale. Podsumowanie metody tablicy decyzyjnej jest przedstawione w tabeli 8.10. Tabela 8.10. Podsumowanie metody tablicy decyzyjnej

8.3.2. Wartości nieistotne i minimalizacja tablicy decyzyjnej Wprowadzimy teraz pojęcie wartości nieistotnych, analizując poniższy przykład. System Egzaminator obsługuje proces egzaminacyjny na uczelni. Na wejściu otrzymuje liczbę punktów uzyskaną przez studenta w postaci przedziału, w którym mieści się wynik (możliwe wartości: 0–50, 51–80, 81–100) oraz informację o tym, czy uzyskał on zaliczenie z ćwiczeń. W odpowiedzi system generuje dwie informacje: o zdaniu lub niezdaniu egzaminu oraz o tym, czy student może przystąpić do egzaminu poprawkowego. System działa według następujących reguł: jeśli wynik egzaminu wynosi 50 punktów lub mniej, to egzamin jest niezdany. W takiej sytuacji student może przystąpić do poprawki tylko wtedy, gdy ma zaliczenie. W przypadku braku zaliczenia, niezależnie od wyniku egzaminu, egzamin jest uznany za niezdany i student nie ma prawa przystąpić do poprawki. Jeśli student ma zaliczenie i liczba punktów jest większa od 50, to zdaje egzamin, przy czym jeśli ma co najwyżej 80 punktów, to otrzymuje prawo do poprawki. W systemie wyróżnić możemy 2 warunki: wynik egzaminu – możliwe wartości: 0–50, 51–80, 81–100; czy ma zaliczenie? – możliwe wartości: P, F (prawda, fałsz) oraz dwie akcje: egzamin zdany? – możliwe wartości: P, F; dopuszczony do poprawki? – możliwe wartości: P, F. Tablica decyzyjna dla tego systemu jest pokazana w tabeli 8.11. Tabela 8.11. Tablica decyzyjna dla programu Egzaminator

Reguły decyzyjne →

RD1 RD2

RD3

RD4 RD5

RD6

wynik eg zaminu

0– 50

51– 80

81– 100

0– 50

51– 80

81–100

czy ma zaliczenie?

P

P

P

F

F

F

czy eg zamin zdany?

F

P

P

F

F

F

czy dopus zczony do poprawki?

P

P



F

F

F

W arunki

Akcje

Zauważmy, że specyfikacja nic nie mówi o możliwości dopuszczenia do poprawki w przypadku, gdy student otrzymał co najmniej 81 punktów z egzaminu i ma zaliczenie. Taka sytuacja może być czymś zupełnie normalnym (np. wynik jest na tyle dobry, że student nie musi chcieć go poprawiać, bo i tak otrzyma najwyższą ocenę), ale może też być wynikiem defektu w specyfikacji. Jednak z punktu widzenia testera sytuacja musi być jasna – albo akcja zachodzi, albo nie. Tester musi więc dowiedzieć się od projektanta systemu, jak wygląda zachowanie programu w takim przypadku. Budowa tablicy decyzyjnej, dzięki systematycznej metodzie wyprowadzania reguł decyzyjnych, pozwala w bardzo efektywny sposób odkrywać wiele błędów i niejasności w specyfikacji zanim w ogóle zaczniemy testować aplikację. W naszym przykładzie w dalszych rozważaniach założymy, że dla reguły RD3 akcja „dopuszczony do poprawki” zachodzi, tzn. ma wartość P. Kolejna interesująca właściwość omawianej tablicy polega na tym, że w przypadku braku zaliczenia, niezależnie od tego, jaką wartość przyjmie warunek dotyczący wyniku egzaminu, wartości wszystkich akcji są stałe – akcje „egzamin zdany” oraz „czy dopuszczony do poprawki” zawsze mają wartość F. Oznacza to, że w przypadku, gdy student nie ma zaliczenia, informacja o liczbie uzyskanych z egzaminu punktów nie ma znaczenia dla podjęcia obu decyzji – niezależnie od wyniku egzaminu uznany on będzie za niezdany, a student nie będzie dopuszczony do poprawki.

Widzimy, że podczas tworzenia reguł decyzyjnych może się okazać, iż w pewnych sytuacjach niektóre warunki nie mają żadnego znaczenia – są irrelewantne wobec pozostałych warunków i akcji. Takie wartości nazywać będziemy nieistotnymi (ang. „don’t-care” value) i oznaczać w tablicy decyzyjnej symbolem kreski. Wykorzystanie wartości nieistotnych ogranicza liczbę reguł decyzyjnych, bo nie musimy ich tworzyć dla wszystkich możliwych kombinacji wartości uznanych za nieistotne. Jednocześnie należy pamiętać, że podczas tworzenia przypadków testowych warunki nieistotne muszą przyjmować konkretne wartości, abyśmy mogli uruchomić test i porównać wynik uzyskany z oczekiwanym. Możemy zatem reguły decyzyjne RD4, RD5 i RD6 reprezentować pojedynczą regułą, w której wartość warunku „wynik egzaminu” będzie nieistotna. Tak zminimalizowana tablica jest pokazana w tabeli 8.12. Tabela 8.12. Zminimalizowana tablica decyzyjna dla programu Egzaminator

Reguły decyzyjne →

RD1

RD2

RD3

RD4′

W arunki wynik eg zaminu

0–50

51–80

81–100



czy ma zaliczenie?

P

P

P

F

czy eg zamin zdany?

F

P

P

F

czy dopus zczony do poprawki?

P

P

P

F

Akcje

Zauważmy, że minimalizacji nie moglibyśmy dokonać, gdyby którykolwiek z pozostałych warunków lub akcji zmieniał swoją wartość w obrębie minimalizowanych reguł. Opiszmy formalnie warunki, pod którymi można dokonać minimalizacji. Dana niech będzie tablica decyzyjna z warunkami w 1, w 2, …, w k i akcjami a 1, a 2, …, a n przyjmującymi wartości odpowiednio ze zbiorów W1, W2, …, Wk , A 1, A 2, …, A n złożona z p reguł decyzyjnych RD = {d 1, …, d p }, gdzie . Każdą regułę reprezentujemy jako wektor o długości k + n wartości warunków oraz akcji

tej reguły:

gdzie

Zdefiniujmy ponadto operator rzutowania p i(d), który zwraca i-ty element wektora d. Na przykład, jeśli d = (w 1, w 2, a 1, a 2), to p 2(d) = w 2, p 3(d) = a 1 itd. Tablicę decyzyjną możemy zminimalizować względem warunku w i, jeśli

istnieją w niej reguły decyzyjne

takie, że

oraz

Reguły te można zastąpić jedną regułą r taką, że p i (r) = ” – ” (wartość nieistotna), a pozostałe wartości warunków i akcji pozostają niezmienione.

Rysunek 8.7. Ilustracja minimalizacji tablicy decyzyjnej Na rysunku 8.7 przedstawiono ideę minimalizacji tablicy decyzyjnej. Sześć decyzyjnych pokazanych z lewej strony rysunku może zostać

reguł

zminimalizowanych do jednej reguły pokazanej z prawej strony, ponieważ wartość warunku 2 nie wpływa na zmianę pozostałych warunków i akcji. Proces minimalizacji można prowadzić dopóty, dopóki istnieją grupy reguł dla których opisane przesłanki mają zastosowanie do jakiegoś warunku. Regułę minimalizowania można jednak nieco osłabić. W przypadku warunku X przyjmującego wartości liczbowe z pewnego przedziału (np. warunek „wynik egzaminu” dla programu Egzaminator) można dokonać minimalizacji względem X w przypadku, gdy pozostałe warunki i akcje nie zmieniają się tylko dla pewnego podzbioru wartości X. Wtedy powstała reguła decyzyjna przyjmuje w warunku X podzbiór możliwych wartości, będący sumą wartości tego warunku z reguł, do których zastosowano minimalizację. Na przykład, w tablicy decyzyjnej 8.12 możemy zminimalizować reguły RD2 i RD3 względem dwóch wartości warunku 1

(51–80 i 81–100), mimo że reguły te nie obejmują wszystkich możliwych wartości tego warunku. Zminimalizowana tablica jest pokazana w tabeli 8.13. Reguła RD2′ odpowiada zminimalizowanym regułom RD2 i RD3. Czasami kombinacja warunków może być nieosiągalna (ang. infeasible). Takie reguły decyzyjne wymagają specjalnego potraktowania. Jeśli konstrukcja programu uniemożliwia ich wystąpienie, to można je pominąć w dalszej analizie. W przeciwnym wypadku należy przeanalizować, czy da się je wymusić. Jeśli tak, to oznacza to defekt albo w oprogramowaniu, albo w specyfikacji, która powinna określać, jak program ma reagować w takiej sytuacji. W każdym razie tester zawsze powinien przynajmniej spróbować wymusić wystąpienie warunków nieosiągalnych. Tabela 8.13. Jeszcze bardziej zminimalizowana tablica decyzyjna programu Egzaminator

Reguły decyzyjne →

RD1

RD2′

RD4′

warunek 1

0–50

51–100



warunek 2

P

P

F

akcja 1

F

P

F

akcja 2

P

P

F

W arunki

Akcje

Rozważmy przykład z rysunku 8.8. Przedstawiono na nim fragment okna opcji programu MS Word. Zauważmy, że nie można mieć aktywnej listy rozwijanej „Domyślny styl akapitu” dla odznaczonej opcji „Włącz klikanie i wpisywanie”. Taki warunek może być podany wprost w specyfikacji, jednak tester powinien sprawdzić, czy da się wymusić naruszenie tych reguł przez odznaczenie opcji przy ciągle aktywnej liście rozwijanej.

Rysunek 8.8. Opcje programu Word

8.3.3. Przykład Program Bankier przyznaje kredyt oraz wysyła klientom oferty kredytu i kart kredytowych zgodnie z następującymi regułami. Jeśli dochody klienta przewyższają 5000 USD/mc i klient nie ma jeszcze karty kredytowej, to system wyśle mu ofertę karty oraz kredytu. Oferta kredytu zostanie wysłana także, gdy

klient nie składał wniosku o kredyt. Jeśli klient składał wniosek o kredyt i jego dochody przekraczają 5000 USD/mc, to system decyduje o przyznaniu kredytu. W przypadku, gdy którykolwiek z warunków nie jest spełniony, odpowiednia akcja nie zostanie wykonana. Chcemy przetestować program Bankier za pomocą tablic decyzyjnych. Krok 1. Określenie przedmiotu i elementów testów. Mamy tylko jeden przedmiot testów, program Bankier. ET1: Bankier. Krok 2. Wyprowadzenie warunków testowych. Warunkami testowymi są warunki i akcje. Mamy następujące 3 warunki: WT1: (W1) dochody miesięczne powyżej 5000 USD; WT2: (W2) posiadanie karty kredytowej; WT3: (W3) złożenie wniosku o kredyt oraz następujące 3 akcje: WT4: (A1) wysłanie oferty karty kredytowej; WT5: (A2) wysłanie oferty kredytu; WT6: (A3) przyznanie kredytu. Stanowią one w sumie sześć warunków testowych, wszystkie dla elementu testowego ET1. Krok 3. Wyprowadzenie elementów pokrycia. Tablica decyzyjna umożliwia zdefiniowanie elementów pokrycia jako jej kolumn reprezentujących reguły decyzyjne. W tabeli 8.14 opisano reguły decyzyjne programu Bankier. Tabela 8.14. Tablica decyzyjna dla programu Bankier

Reguły decyzyjne → W arunki

RD1 RD2 RD3 RD4 RD5 RD6 RD7 RD8

W1 dochody powyżej 5000 US D?

P

P

P

P

F

F

F

F

W2 ma kartę kredytową?

P

P

F

F

P

P

F

F

W3 złożył wnios ek o kredyt?

P

F

P

F

P

F

P

F

A1 wys łanie oferty karty kredytowej

F

F

P

P

F

F

F

F

A2 wys łanie oferty kredytu

F

P

P

P

F

P

F

P

A3 decyzja o przyznaniu kredytu

P

F

P

F

F

F

F

F

Akcje

Zauważmy, że można zminimalizować tę tablicę przez połączenie warunków i to na kilka sposobów. Możemy połączyć ze sobą reguły RD2 i RD6 (względem W1) oraz RD5 i RD7 (względem W2). Zamiast RD2 i RD6 możemy wybrać parę RD6 i RD8 (względem W2). Nie możemy jednak wybrać jednocześnie RD2, RD6 i RD8, ponieważ odpowiednie pary tych reguł można minimalizować względem odmiennych warunków. Wybierzmy pierwszy sposób minimalizacji, otrzymując tablicę decyzyjną 8.15. Reguła RD2′ odpowiada zminimalizowanym RD2 i RD6, a RD5′ jest zminimalizowaną regułą dla RD5 i RD7. Tabela 8.15. Zminimalizowana tablica decyzyjna dla programu Bankier

Reguły decyzyjne →

RD1 RD2′ RD3 RD4 RD5′ RD8

W arunki W1 dochody powyżej 5000 US D?

P



P

P

F

F

W2 ma kartę kredytową?

P

P

F

F



F

W3 złożył wnios ek o kredyt?

P

F

P

F

P

F

Akcje A1 wys łanie oferty karty kredytowej

F

F

P

P

F

F

A2 wys łanie oferty kredytu

F

P

P

P

F

P

A3 decyzja o przyznaniu kredytu

P

F

P

F

F

F

Krok 4. Zdefiniowanie przypadków testowych. W przypadku oryginalnej tablicy osiem elementów pokrycia odpowiada ośmiu kolumnom. W przypadku tablicy zminimalizowanej sześć elementów pokrycia odpowiada sześciu kolumnom. Zaprojektujmy przypadki testowe dla tej drugiej. Są one przedstawione w tabeli 8.16. Zbiór elementów do pokrycia to {RD1, RD2′, RD3, RD4, RD5′, RD8}. Tabela 8.16. Przypadki testowe dla programu Bankier

W ejścia Id

Oczekiwane wyjścia

Po dochody ma kartę złożył oferta oferta przyznanie el po (USD) kredytową? wniosek? karty kredytu kredytu

PT1 6 000

TAK

TAK

NIE

NIE

TAK

RD

PT2 3 500

TAK

NIE

NIE

TAK

NIE

RD

PT3 5 000.01

NIE

TAK

TAK

TAK

TAK

RD

PT4 12 832

NIE

NIE

TAK

TAK

NIE

RD

PT5 2 004.22

TAK

TAK

NIE

NIE

NIE

RD

PT6 0

NIE

NIE

NIE

TAK

NIE

RD

W przypadku PT2 wartość dochodów była nieistotna, ale ponieważ przypadek testowy musi mieć konkretne wartości wejścia, trzeba było wybrać jakąś wartość. Tak samo postąpiliśmy w przypadku PT5 dla wymagania nr 2. Przy okazji PT3 i PT6 wykorzystaliśmy także wartości brzegowe dla warunku na dochód (0 USD i 5000 USD). Pokrycie tabeli decyzyjnej wynosi 100%, ponieważ dla każdego elementu pokrycia (warunku decyzyjnego) stworzyliśmy wykorzystujący

go test. Gdybyśmy np. dysponowali tylko testami PT1, PT2 i PT3, pokrycie wyniosłoby 3/6 = 50%.

8.4. Grafy przyczynowo-skutkowe 8.4.1. Opis metody Grafy przyczynowo-skutkowe (w skrócie grafy P-S) są metodą podobną do tablic decyzyjnych. Opisują logiczne zależności między warunkami (w terminologii grafów nazywanych przyczynami) a akcjami (nazywanymi skutkami), które mogą pojawić się w systemie. W tym sensie są metodą równoważną, jednak występują między nimi pewne różnice. Po pierwsze, grafy P-S pozwalają dodatkowo na określanie zależności między samymi przyczynami lub między samymi skutkami. Takiej możliwości tablice decyzyjne nie dają. Po drugie, w przypadku dużej liczby przyczyn liczba reguł decyzyjnych (a więc i rozmiar tablicy) rośnie wykładniczo, natomiast rozmiar grafu P-S rośnie liniowo względem liczby przyczyn, skutków oraz relacji między nimi. Hipoteza błędu jest natomiast taka sama jak w przypadku tablic decyzyjnych: błędy są powodowane przez wystąpienie określonej kombinacji czynników. Graf P-S może być przekształcony na tablicę decyzyjną i tester może wyprowadzać przypadki testowe na jej podstawie. Jeśli jednak duża liczba warunków sprawia, że rozmiar tablicy staje się problematyczny, to można zastosować tzw. metodę śledzenia wstecznego, która pozwala znacznie zredukować liczbę przypadków testowych (w sensie liczby kolumn odpowiadającej grafowi tablicy decyzyjnej) do niewielkiego podzbioru. Co więcej, procedura ta jest mechaniczna i może być zautomatyzowana. graf przyczynowo-skutkowy (ang. cause-effect graph) – graficzna reprezentacja logicznych zależności między wejściami (przyczynami) a odpowiadającymi im wyjściami (efektami), które to zależności mogą być wykorzystane do zaprojektowania przypadków testowych Grafy P-S składają się z wierzchołków oraz odpowiednio oznaczanych krawędzi reprezentujących logiczne zależności między elementami grafu. Istnieją trzy typy wierzchołków:

wierzchołki reprezentujące przyczyny, wierzchołki reprezentujące skutki, wierzchołki wewnętrzne. Wierzchołki wewnętrzne odgrywają pomocniczą rolę i służą do upraszczania graficznej postaci grafu oraz reprezentowania bardziej złożonych warunków logicznych, np. wykorzystujących kilka rodzajów operatorów logicznych (standardowe oznaczenia krawędzi umożliwiają wykorzystanie tylko pojedynczych operatorów logicznych). Wierzchołki przyjmują wartości logiczne prawdy lub fałszu (reprezentowane stałymi 1 i 0). Prawda oznacza, że dana przyczyna bądź skutek zachodzi, fałsz – że nie zachodzi. Po ustaleniu wartości logicznych dla przyczyn, na podstawie zdefiniowanych relacji oblicza się wartości logiczne wierzchołków wewnętrznych oraz skutków.

Rysunek 8.9. Elementy składowe grafów przyczynowo-skutkowych Na rysunku 8.9 są przedstawione podstawowe elementy, z których można budować grafy P-S. Pierwsza kolumna opisuje zależności logiczne między wierzchołkami, które nie są jednocześnie przyczynami bądź jednocześnie skutkami. Podstawową relacją jest tożsamość b = a, która mówi, że wartość logiczna wierzchołka b jest taka sama, jak wartość logiczna wierzchołka a. Negacja b = ~a zamienia wartość logiczną na przeciwną: jeśli a jest prawdą, to b jest fałszem i na odwrót: jeśli a jest fałszem, to b jest prawdą. Alternatywa c = a ∨ b powoduje, że c jest prawdą, jeśli przynajmniej jeden spośród wierzchołków a i b jest prawdą. W przeciwnym razie c jest fałszem. Koniunkcja c = a ∧ b powoduje prawdziwość c tylko wtedy, gdy zarówno a, jak i b są prawdą. Operator NOR reprezentuje negację alternatywy, c = ~(a ∨ b): c jest prawdą tylko wtedy, gdy zarówno a, jak i b są fałszem. W przeciwnym razie c jest fałszem. Operator NAND to negacja koniunkcji, c = ~(a ∧ b): c jest prawdą, gdy przynajmniej jedno spośród a i b jest fałszem. W przeciwnym razie c jest fałszem. W środkowej kolumnie rysunku przedstawiono operatory ograniczające, czyli nakładające ograniczenia na przyczyny. Operator E (od ang. exclusive) mówi, że co najwyżej jeden wierzchołek nim związany może być prawdą (wszystkie mogą być też jednocześnie fałszem). Operator I (od ang. inclusive) mówi, że przyczyny nim związane nie mogą być jednocześnie fałszem – przynajmniej jedna z nich musi zachodzić. Operator O (od ang. one and only one) mówi, że dokładnie jedna przyczyna nim związana musi zachodzić, a pozostałe muszą być fałszem. Operator R (od ang. requires) jest operatorem „skierowanym”, oznaczanym strzałką. Mówi on, że jeśli przyczyna a zachodzi, to musi również zachodzić b. Jeśli a nie zachodzi, to b może być zarówno prawdą, jak i fałszem. Operatory E, I, O można stosować do dowolnej liczby przyczyn. Operator R jest dwuargumentowy. Na ostatniej kolumnie rysunku przedstawiono operator M maskowania (od ang. masks), który nakłada ograniczenie na skutki. Tak jak operator R jest on dwuargumentowy i mówi, że jeśli skutek a zachodzi, to skutek b nie może zajść (jest maskowany skutkiem a). Jeśli a nie zachodzi, to b może przyjąć dowolną wartość logiczną. Powiedzieliśmy wcześniej, że metoda grafów P-S jest równoważna metodzie tablic decyzyjnych (a nawet, że jest bardziej ogólna). Jak jednak reprezentować w grafie przyczyny bądź skutki, które zamiast wartości logicznych mogą przyjąć

dowolną liczbę innych wartości (jak np. w warunku „wynik egzaminu” z poprzedniego rozdziału, który to skutek mógł przyjmować trzy możliwe wartości: 0–50, 51–80 i 81–100)? Z pomocą przychodzą operatory ograniczające. Rozważmy przykład skutku „wynik egzaminu”. Może on przyjąć dokładnie jedną spośród trzech wartości, zatem można go modelować za pomocą operatora O dla trzech następujących przyczyn: P1: wynik egzaminu jest między 0 a 50? (prawda lub fałsz); P2: wynik egzaminu jest między 51 a 80? (prawda lub fałsz); P3: wynik egzaminu jest między 81 a 100? (prawda lub fałsz). Gdyby wynik egzaminu nie był przyczyną, ale skutkiem, również moglibyśmy zamodelować taką sytuację, używając operatora maskowania. Zakładamy istnienie trzech skutków S1, S2 i S3 (analogicznych do przyczyn P1, P2, P3 podanych powyżej) z których każdy może zachodzić lub nie, ale zajście jednego z nich powoduje, że wszystkie pozostałe zajść nie mogą. Jedynym dostępnym operatorem dla skutków jest operator maskowania. Musimy go zastosować wielokrotnie – dla każdego zachodzącego skutku wobec wszystkich pozostałych, niezachodzących skutków. Odpowiednie fragmenty grafu P-S modelujące wymienione sytuacje, zarówno dla przyczyn, jak i dla skutków, są pokazane na rys. 8.10. Istnienie przyczyn lub skutków przyjmujących więcej niż dwie możliwe wartości powoduje większe skomplikowanie grafu.

Rysunek 8.10. Wykorzystanie operatorów O i M dla reprezentacji przyczyn i skutków o więcej niż dwóch możliwych wartościach tworzenie grafów przyczynowo-skutkowych, analiza przyczynowo-skutkowa (ang. cause-effect graphing, cause-effect analysis) – czarnoskrzynkowa technika projektowania przypadków testowych, w której przypadki te są tworzone na podstawie grafów przyczynowo-skutkowych [36]

8.4.2. Przekształcanie między grafami P-S i tablicami decyzyjnymi Jeśli logika programu nie powoduje wystąpienia kombinacji warunków, która jest nieosiągalna, to tablice decyzyjne są równoważne grafom P-S, tzn. każdą tablicę decyzyjną da się przekształcić na graf P-S i na odwrót. Jeśli istnieją nieosiągalne kombinacje warunków, to musimy wprowadzić dodatkowe oznaczenie tego faktu w tablicy decyzyjnej oraz specjalny, dodatkowy skutek w grafie P-S. Operacji przekształcenia tablicy na graf można dokonać wprost, reprezentując każdą regułę decyzyjną jako kombinację warunków logicznych w grafie P-S. Jeśli jednak chcemy mieć możliwie mały graf, to tablicę można wcześniej zminimalizować. Z kolei przekształcenie grafu na tablicę polega na kolejnym wybieraniu wszystkich możliwych kombinacji przyczyn, obliczania na podstawie grafu skutków i zapisywaniu wyniku jako kolumny tablicy decyzyjnej. Aby

zredukować liczbę reguł decyzyjnych w tablicy, możemy podczas transformacji grafu na tablicę wykorzystać metodę śledzenia wstecznego omówioną w punkcie 8.4.3. Rozważmy przykład tablicy decyzyjnej pokazanej w tabeli 8.17. Wymagania RD6–RD8 są nieosiągalne, co oznaczamy, wprowadzając specjalny, dodatkowy wiersz w części opisującej akcje. Znak X oznacza, że dane wymaganie jest nieosiągalne. Reguły nieosiągalne również mogą podlegać minimalizacji, przy czym nie bierze się tu pod uwagę akcji (bo ich nie ma). Przekształcamy tablicę na graf P-S. Możemy przekształcać ją wprost, reguła po regule. Na przykład dla reguły RD3, opisującej następującą logikę: W1 ∧ ~W2 ∧ W3

⇒ A 1 ∧ ~A 2 otrzymamy fragment grafu P-S pokazany na rysunku 8.11 z lewej strony. Przyczyny P1, P2, P3 odpowiadają warunkom W1, W2, W3, a skutki S1, S2 – akcjom A1, A2. Nieosiągalną regułę RD6 można przekształcić na graf, wprowadzając dodatkowy wierzchołek N reprezentujący wymagania

nieosiągalne. Odpowiedni fragment grafu jest pokazany na rysunku 8.11 z prawej strony. Tabela 8.17. Przykładowa tablica decyzyjna z wymaganiami nieosiągalnymi

Reguły decyzyjne →

RD1 RD2 RD3 RD4 RD5 RD6 RD7 RD8

W arunki W1

P

P

P

P

F

F

F

F

W2

P

P

F

F

P

P

F

F

W3

P

F

P

F

P

F

P

F

A1

P

P

P

P

F

A2

F

F

F

F

P X

X

X

Akcje

(nieos iąg alne)

Rysunek 8.11. Fragment grafu P-S odpowiadający jednej regule tablicy decyzyjnej Jeśli jednak naniesiemy w ten sposób wszystkie reguły na graf, to będzie on bardzo skomplikowany i nieczytelny. Lepiej jest najpierw zminimalizować tablicę decyzyjną. Przy transformowaniu reguły z wartościami nieistotnymi wierzchołki reprezentujące te wartości pomijamy, gdyż ich wartość nie ma wpływu na żaden skutek w tej regule. Zminimalizowana tabela 8.17 pokazana jest w tabeli 8.18. Tabela 8.18. Przykładowa tablica decyzyjna z wymaganiami nieosiągalnymi po zminimalizowaniu

Reguły decyzyjne →

RD1′

RD5

RD6

RD7′

W1

P

F

F

F

W2



P

P

F

W3



P

F



A1

P

F

A2

F

P X

X

W arunki

Akcje

(nieos iąg alne)

Zredukowaliśmy liczbę reguł do czterech. Reguła RD1′ odpowiada wyrażeniu logicznemu W1 ⇒ A 1 ∧ ~A 2. Jak widać, wykorzystujemy tu tylko jedną przyczynę,

bo skutek nie zależy od W2 ani W3. Pełny graf P-S dla zminimalizowanej tablicy jest pokazany na rysunku 8.12. Wierzchołki v, w, x, z są wierzchołkami wewnętrznymi (pomocniczymi).

Rysunek 8.12. Graf P-S stworzony na podstawie tablicy decyzyjnej Przekształćmy teraz ten graf P-S z powrotem na tablicę decyzyjną. Znajdźmy wartości akcji, jeśli wszystkie przyczyny są prawdziwe. Z identyczności P1 ⇒ S1 otrzymujemy, że S1 jest prawdą. Zauważmy, że ten sam wynik osiągnęlibyśmy, wykorzystując węzeł pośredni v, gdyż, podstawiając wartości prawdy za P1, P2, P3 w regule R: ~((~P1 ∧ P2) ∧ P3), otrzymamy wartość logiczną prawdy. Graf został stworzony na podstawie tablicy decyzyjnej, w której akcje są wyznaczone jednoznacznie na podstawie warunków, dlatego wartość logiczna reguły R zawsze będzie taka, jak wartość logiczna P1. Wartość S2 najłatwiej obliczyć z negacji S2 =

~P1. Skoro P1 jest prawdą, to S2 musi być fałszem. Zauważmy, że nie musimy już obliczać wartości dla skutku N, ponieważ reprezentuje on nieosiągalną kombinację warunków. Skoro pozostałe skutki przyjęły wartości logiczne, oznacza to, że kombinacja warunków P1 = P2 = P3 = 1 jest osiągalna. Skutek N jest więc fałszem. Oczywiście możemy to obliczyć wprost na podstawie reguły dla skutku N: N = w ∨ z = (x ∧~ P3) ∨ (~P1 ∧~ P2) = (~P1 ∧ P2 ∧~P3) ∨ (~P1 ∧~P2). Podstawiając wartości P1 = P2 = P3 = 1, otrzymujemy (~1 ∧ 1 ∧~1) ∨ (0 ∧ 0) = 0 ∨ 0 = 0. Postępując podobnie dla wszystkich pozostałych kombinacji przyczyn, otrzymujemy wartości skutków i w ten sposób tworzymy wszystkie kolumny tablicy decyzyjnej. W tabeli 8.19 podsumowano najważniejsze cechy metody grafów P-S. Kryterium pokrycia i pokrycie jest takie samo jak w przypadku tablic decyzyjnych, ze względu na opisany wyżej związek między tymi dwoma podejściami. pokrycie grafu przyczynowo-skutkowego (ang. cause-effect graph coverage) – odsetek pokrytych testami kolumn tablicy decyzyjnej stworzonej na podstawie tego grafu Tabela 8.19. Podsumowanie metody grafów przyczynowo-skutkowych

Warunki tes towe

Zbiór ws zys tkich przyczyn i s kutków wyznaczonych z analizy pods tawy tes tów

Zbiór ws zys tkich reg uł decyzyjnych, tablicy decyzyjnej Elementy s tworzonej na pods tawie g rafu P-S ; tablica może być pokrycia zminimalizowana wpros t lub przez zas tos owanie metody ś ledzenia ws teczneg o Dla każdej reg uły decyzyjnej (kolumny tablicy) is tnieje co Kryterium najmniej jeden tes t, który wymus za s pełnienie pokrycia odpowiednich warunków tej reg uły decyzyjnej Pokrycie

p = (liczba pokrytych tes tami reg uł decyzyjnych/liczba ws zys tkich reg uł decyzyjnych) × 100%

8.4.3. Metoda śledzenia wstecznego – redukcja liczby testów Dla n przyczyn mamy 2n możliwych kombinacji ich wartości logicznych. Każda kombinacja odpowiada jednemu przypadkowi testowemu, więc rozmiar suity testowej rośnie wykładniczo wraz ze wzrostem liczby przyczyn. Gdy zależy nam na zminimalizowaniu tego rozmiaru, możemy wykorzystać tzw. metodę śledzenia wstecznego. Jej idea jest następująca: dla każdej możliwej wartości logicznej każdego możliwego skutku staramy się znaleźć minimalną sensowną liczbę przypadków testowych, które wymuszają ten warunek. Sensowność wyrażona jest przez reguły śledzenia wstecznego6, które opiszemy dalej. Mówiąc bardzo nieformalnie, metoda stara się pomijać te przypadki testowe, w których szansa na wykrycie defektu jest niewielka. Algorytm generowania przypadków testowych w metodzie śledzenia wstecznego jest opisany w listingu 8.2.

ŚledzenieWsteczne wejście: graf P-S zbiór przyczyn P zbiór skutków S={S1, S2, …, Sn} wyjście: zbiór T={T1, …, Tt} przypadków spełniających kryterium pokrycia śledzenia wstecznego 1 T:=Ø // zbiór pusty 2 for (i:=1 to n) do 3 ustaw Si na wartość logiczną 1 4 dokonaj śledzenia wstecznego dla Si=1 5 niech T′:=zbiór przypadków dla tego śledzenia 6 T:=T ∪ T′ 7 for (t:=1 to |T|) do 8 ustaw wartości przyczyn występujących w Tt 9 10 11 12

przypisz wartości wierzchołkom, które można obliczyć z grafu uwzględniając warunki ograniczające uzupełnij przypadek Tt wartościami przyczyn i skutków

nie występujących w Tt uwzględniając warunki ograniczające 13 return T

Listing 8.2. Algorytm redukcji liczby testów dla grafu P-S Algorytm ten zwraca zbiór przypadków testowych w postaci zestawu t kombinacji przyczyn i skutków, które w prosty sposób można przekształcić do postaci t-kolumnowej tablicy decyzyjnej. Metoda śledzenia wstecznego przeprowadzana w linii 4. algorytmu jest dosyć skomplikowana i dlatego zwykle następujących czterech regułach:

jest

automatyzowana.

Opiera

się

na

Reguła 1. Przy śledzeniu wstecznym przyczyn powodujących ustawienie wierzchołka or na 1 nie należy nigdy ustawiać na 1 więcej niż jednego wejścia do tego wierzchołka7. Reguła 2. Przy śledzeniu wstecznym przyczyn powodujących ustawienie wierzchołka or na 0 należy rozpatrzyć wszystkie sytuacje wywołujące wartości 0 w wierzchołkach do niego wchodzących. Reguła 3. Przy śledzeniu wstecznym wejść do wierzchołka and którego wartość ma być 0, należy uwzględnić wszystkie kombinacje wejść wywołujących tę wartość, przy czym dla tych wejść, które mają wartość 1, nie ma potrzeby uwzględniania przyczyn, które tę wartość wywołują. Dla przypadku, gdy wszystkie wejścia do wierzchołka and są zerowe, należy rozpatrzyć tylko jedną kombinację przyczyn wywołującą wartości 0 w tych wierzchołkach8. Reguła 4. Przy śledzeniu wstecznym wejść do wierzchołka and, którego wartość ma być 1, należy rozpatrzyć wszystkie sytuacje wywołujące wartości 1 w wierzchołkach wchodzących. Wierzchołki nand i nor można wyrazić przy użyciu operatorów alternatywy, koniunkcji i negacji9, dlatego nie musimy dla nich tworzyć dodatkowych reguł. Na przykład: p NAND q ≡ (p ∧ q) ≡ ~p ∨ ~q, więc wierzchołek nand jest po prostu wierzchołkiem or z zanegowanymi wszystkimi wejściami.

Rysunek 8.13. Graficzna ilustracja reguł śledzenia wstecznego

Rysunek 8.14. Graf P-S do którego zastosowano śledzenie wsteczne Zobaczmy, jak działa metoda śledzenia wstecznego w praktyce. Rozważmy graf P-S z rysunku 8.14. Skutek S1 wystąpi, gdy z = 0. Wierzchołek z jest typu and, więc, stosując Regułę 3, musimy rozważyć trzy przypadki dla pary (w, y): (0, 0), (0, 1) oraz (1, 0). Dla (w, y) = (0, 0) musimy, zgodnie z Regułą 3, rozważyć tylko jedną kombinację przyczyn powodującą w = y = 0. Możemy to zrobić, przyjmując P1 = 1, P2 = … = P8 = 0. Ta kombinacja stanowi regułę RD1 w wynikowej tabeli decyzyjnej 8.20. Dla (w, y) = (0, 1), zgodnie z Regułą 3, wystarczy rozważyć jedną kombinację dającą y = 1 (co i tak można uzyskać tylko na jeden sposób, przyjmując P4 = … = P8

= 1) i wykonać śledzenie wsteczne dla w = 0. Jest on typu or, zatem zgodnie z Regułą 2 musimy przyjąć P1 = 1 i v = 0, przy czym dla v = 0 musimy z kolei – na podstawie tej samej reguły – rozważyć wszystkie możliwe kombinacje P2 i P3, których koniunkcja da v = 0. Możliwe przypadki dla (P2, P3) to: (0, 0), (1, 0) oraz (1, 1). Zauważmy, że żaden z nich nie narusza warunku ograniczającego R dla pary (P3, P1). Dostajemy zatem trzy kombinacje przyczyn stanowiące reguły RD2–RD4 w wynikowej tabeli 8.20. Gdy (w, y) = (1, 0), musimy rozważyć tylko jedną kombinację przyczyn dającą w = 1, co może nastąpić tylko wtedy, gdy P1 = 0 i P2 = P3 = 1, ale w takim przypadku nastąpiłoby naruszenie warunku ograniczającego (jeśli P3 = 1, to P1 też musi być 1). Nie musimy dalej rozważać tego przypadku, bo wymagana kombinacja przyczyn P1, P2, P3 jest nieosiągalna. Tabela 8.20. Wynikowa tablica decyzyjna po zastosowaniu metody śledzenia wstecznego dla skutku S1

Reguły decyzyjne →

RD1 RD2 RD3 RD4 RD5 RD6 RD7 RD8

W arunki W1=S 1

0

1

1

1

0

0

0

1

W2=S 2

0

0

1

0

0

0

1

1

W3=S 3

0

0

0

1

0

1

0

1

W4=S 4

0

1

1

1

1

1

1

1

W5=S 5

0

1

1

1

1

1

1

1

W6=S 6

0

1

1

1

1

1

1

1

W7=S 7

0

1

1

1

1

1

1

1

W8=S 8

0

1

1

1

1

1

1

1

1

1

1

1

0

0

0

0

Akcje A1=P1

Algorytm śledzenia wstecznego znajduje zbiory przypadków dla poszczególnych skutków ustawianych na 1. Możemy jednak wykonać śledzenie wsteczne również dla wymuszenia tego, że skutek nie zajdzie. Zróbmy to dla naszego przykładu, wymuszając S1 = 0. Będzie tak, jeśli z = 1. Na mocy Reguły 4 musimy rozważyć wszystkie kombinacje dające w = y = 1. Dla y = 1 sytuacja taka nastąpi tylko wtedy, gdy P4 = … = P8 = 1. Dla w = 1 może tak być wtedy, gdy (P1, v) jest postaci (0, 0), (1, 1) lub (0, 1), ale ta ostatnia jest nieosiągalna ze względu na warunek ograniczający. Dla v = 0, zgodnie z Regułą 3, musimy rozważyć (P2, P3) postaci (0, 0), (0, 1) lub (1, 0), co daje 3 kombinacje. Dla v = 1 jedyna możliwość to P2 = P3 = 1. W sumie dostajemy cztery kombinacje (P1, P2, P3): (0, 0, 0,), (0, 0, 1), (0, 1, 0) oraz (1, 1, 1). Stanowią one reguły RD5–RD8 w tablicy 8.20. Zastosowanie metody śledzenia wstecznego dla uzyskania obu możliwych wartości skutku S1 daje tylko 8 reguł decyzyjnych wobec 120 wszystkich możliwych10. Jak widać, śledzenie wsteczne istotnie zmniejsza liczbę reguł, a więc i przypadków testowych.

8.4.4. Przykład Załóżmy, że w testowanym przez nas programie (nazwijmy go JakiśProgram) można wyróżnić 8 przyczyn P1–P8 i 1 skutek S1. Krok 1. Określenie przedmiotu i elementów testów. Mamy tylko jeden element testów, testowany program: ET1: JakiśProgram. Krok 2. Wyprowadzenie warunków testowych. Warunkami testowymi są przyczyny P1–P8 oraz skutek S1, wszystkie dla ET1. WT1: P1; WT2: P2; WT3: P3; WT4: P4; WT5: P5; WT6: P6; WT7: P7;

WT8: P8; WT9: S1. Krok 3. Wyprowadzenie elementów pokrycia. Elementami pokrycia są reguły tablicy decyzyjnej odpowiadającej grafowi. Możemy zdecydować się na zastosowanie metody śledzenia wstecznego, uzyskując tablicę decyzyjną 8.20. Otrzymujemy 8 elementów pokrycia: EP1: RD1; EP2: RD2; EP3: RD3; EP4: RD4; EP5: RD5; EP6: RD6; EP7: RD7; EP8: RD8. Krok 4. Zdefiniowanie przypadków testowych. Podobnie jak w przypadku tablic decyzyjnych, każdy przypadek odpowiada jednej kolumnie tabeli. Pamiętajmy, że wystąpienia określonych przyczyn i skutków mogą być wynikiem przyjęcia określonych wartości przez różne zmienne. Jeśli na przykład wystąpienie przyczyny P1 (tzn. warunku W1 w tablicy decyzyjnej) zależy od tego, że zmienna stanKonta jest większa od 5000 USD, to np. przypadek testowy odpowiadający regule RD1 musi podawać na wejście programu taką wartość tej zmiennej, która nie spowoduje wystąpienia tej przyczyny (bo WT1 dla PT1 w RD1 wynosi 0). Przykładowo, wartość stanKonta może być równa 4500 USD. Ostatnim etapem jest stworzenie suit i procedur testowych. Odbywa się to dokładnie na takiej samej zasadzie, jak w poprzednio omówionych metodach.

8.5. Testowanie przejść między stanami 8.5.1. Opis metody Dotychczas omówione metody koncentrowały się na „statycznym” aspekcie oprogramowania: po otrzymaniu danych na wejściu interesowało nas wyłącznie

to, co program zwrócił na wyjściu. Testowanie przejść między stanami pozwala przetestować dynamikę działania programu, a narzędziem, które to umożliwia, jest maszyna skończenie stanowa (zwana też automatem skończonym), czyli abstrakcyjny model działania programu. Analiza maszyny stanowej pod kątem różnych kryteriów pokrycia pozwala w automatyczny sposób wygenerować zbiór przypadków testowych weryfikujących zachowanie modelowanego systemu. Diagram maszyny stanowej jest jednym ze standardowych diagramów UML [94]. Maszyna skończenie stanowa składa się ze stanów oraz przejść między nimi. Przejścia te mogą być etykietowane dwoma typami etykiet: zdarzeniami oraz akcjami. Wystąpienie zdarzenia wywołuje przejście z jednego stanu do drugiego. Podczas przejścia może być wykonana akcja. Dodatkowo, przy zdarzeniu możemy dopisać warunek. Jest to wyrażenie logiczne, które musi zostać spełnione, aby wystąpienie zdarzenia spowodowało akcję i przejście do kolejnego stanu. Ogólny model stanów oraz przejść między nimi jest pokazany na rysunku 8.15. Jeśli system znajduje się w stanie 1 i nastąpi wtedy zdarzenie, którym jest etykietowana strzałka idąca od stanu 1 do stanu 2, to maszyna – pod wpływem tego zdarzenia – zmieni stan na 2 oraz dokona akcji. Hipoteza błędu w przypadku modelu maszyny stanowej mówi, że błędy powstają w wyniku nieprawidłowej implementacji sekwencji działań programu (np. błędy w logice biznesowej).

Rysunek 8.15. Ogólny model stanów oraz przejść między nimi. Dwie konwencje zapisu Na rysunku 8.15 przedstawiono dwie stosowane w praktyce konwencje zapisu zdarzeń i akcji. W pierwszej nazwę akcji zapisuje się pod nazwą zdarzenia

i oddziela się je poziomą kreską. W drugiej nazwy zdarzenia i akcji zapisuje się w jednej linii, oddzielając je od siebie ukośnikiem. maszyna skończenie stanowa, automat skończony (ang. finite state machine, finite automaton) – model obliczeniowy składający się ze skończonej liczby stanów i przejść między nimi, możliwie z towarzyszącymi im akcjami [7] diagram stanów (ang. state diagram) – diagram przedstawiający stany, jakie moduł lub system może przyjąć oraz zdarzenia lub okoliczności, które powodują zmiany stanów i/lub wynikają z tych zmian [7] zmiana stanu (ang. state transition) – przejście między dwoma stanami systemu lub modułu Niektóre przejścia mogą być etykietowane tylko zdarzeniem, bez akcji. W takim przypadku wystąpienie zdarzenia skutkuje wyłącznie zmianą stanu. Między dwoma stanami może istnieć więcej niż jedno przejście. Zazwyczaj oznacza się je za pomocą jednej strzałki i zestawu par zdarzenie/akcja. Jak odróżnić zdarzenia od stanów i akcji? Zdarzenia nadchodzą spoza modelowanego systemu i ich pojawienie się skutkuje przejściem między stanami. Mogą to być np. akcje użytkownika takie, jak naciśnięcie klawisza, wybór opcji czy akceptacja formularza. Zdarzenie ma zawsze charakter chwilowy – można na nie patrzeć, jak na zjawisko punktowe lub trwające przez bardzo krótki czas. Stan natomiast ma charakter stały, stabilny, rozciągnięty w czasie. Stanem może być np. „oczekiwanie na zapłatę”, tak, jak w maszynie z rysunku 8.16 opisującej działanie automatu biletowego. Dopóki nie nastąpi jakaś nowa akcja, maszyna cały czas będzie oczekiwała w tym samym stanie (np. oczekiwanie na wrzucenie monety). Akcja jest odpowiedzią systemu, która następuje podczas wykonywania przejścia. Tak jak zdarzenie, akcja może być chwilowa lub trwać przez bardzo krótki czas.

Rysunek 8.16. Maszyna stanowa dla automatu biletowego Dlaczego definiuje się pojęcie warunku, skoro teoretycznie może być on reprezentowany jako zdarzenie? Warunek różni się od zdarzenia tym, że najczęściej występują w nim zmienne „wewnętrzne” systemu, zaimplementowane w programie. Odwołuje się więc do wewnętrznej konfiguracji modelowanego obiektu, w przeciwieństwie do zdarzenia, które, jak już powiedzieliśmy wcześniej, ma najczęściej charakter zewnętrzny wobec systemu. Jeśli maszyna, znajdując się w stanie S, przechodzi do stanu T pod wpływem zdarzenia z, to będziemy zapisywać ten fakt jako S → z → T lub S → z[w] → T, gdy oprócz zdarzenia przejście dodatkowo jest etykietowane warunkiem (spełnionym w momencie przejścia). Jeśli będziemy chcieli zaznaczyć również, iż w momencie przejścia nastąpiła akcja a, to będziemy stosować zapis S → z/a → T lub S → z[w]/a → T. Notacji tej używać będziemy także do reprezentowania ścieżek, czyli sekwencji większej liczby przejść. Zapis S1 → z1/a 1 → S2 → z2/a 2 → … → Sn–1 → zn/a n → Sn oznaczać będzie ciąg przejść ze stanu S1 przez stany S2, S3, …, Sn–1 aż do stanu Sn, pod wpływem kolejnych zdarzeń z1, z2, …, zn, podczas którego zostaną wykonane

akcje a 1, a 2, …, a n. Jeśli nie będą interesować nas zdarzenia ani akcje, a jedynie to, przez jakie stany przechodziliśmy, idąc daną ścieżką, będziemy to oznaczać w następujący sposób: S1 S2 S3 … Sn Długość ścieżki to liczba jej przejść. Na przykład ścieżka S1 → S2 → S3 ma długość 2, ponieważ występują w niej 2 przejścia. Ścieżka S1 złożona z pojedynczego stanu ma długość 0. W maszynie stanowej wyróżnić możemy trzy typy stanów, których graficzne reprezentacje są pokazane na rysunku 8.17: stan początkowy – stan, od którego maszyna rozpoczyna swoje działanie; reprezentuje on stan programu w momencie jego uruchomienia; stan końcowy – jeśli maszyna znajdzie się w tym stanie (osiągnie go), to kończy swoje działanie; stan wewnętrzny – stan, który nie jest ani początkowy, ani końcowy.

Rysunek 8.17. Graficzna reprezentacja różnych typów stanów W maszynie stanowej może być tylko jeden stan początkowy i jeden lub więcej stanów końcowych. Powiemy, że maszyna jest syntaktycznie poprawna, jeśli każdy stan da się osiągnąć ze stanu początkowego S0, to znaczy, gdy dla dowolnego stanu S tej maszyny istnieje taka sekwencja zdarzeń z1, z2, …, zn, że zachodzi

Poprawność syntaktyczną należy odróżnić od semantycznej. Może się zdarzyć, że istniejąca sekwencja przejść między stanami maszyny nie jest możliwa do uzyskania w modelowanym systemie. Rozważmy maszynę z rysunku 8.16 modelującą działanie automatu biletowego. Ma on dwie opcje: sprzedaż biletów jednorazowych lub wyświetlenie rozkładu jazdy. Użytkownik w stanie S1 wybiera, czy chce kupić bilet, czy wyświetlić rozkład jazdy. Jeśli wybierze zakup biletu, to program przejdzie do stanu S2 i otworzy się wrzutnik na monety. Po wrzuceniu

odpowiedniej kwoty automat drukuje bilet i użytkownik może dokupić kolejne bilety lub zakończyć korzystanie z automatu. Analogicznie, użytkownik może zażądać wyświetlenia dowolnej liczby rozkładów jazdy przez wybór określonych linii. Załóżmy, że program jest tak skonstruowany, że z chwilą pierwszego wyboru (bilet lub rozkład) kolejne wybory muszą być takie same lub polegać na anulowaniu wyboru bądź zakończeniu korzystania z automatu. Zauważmy, że chociaż według modelu ścieżka S1 → KupBilet → S2 → Zapłać → S1 → WybierzLinię jest syntaktycznie poprawna, to nie będzie się jej dało uzyskać w systemie, bo w ramach jednej sesji można wyłącznie albo kupować bilety, albo przeglądać rozkłady jazdy. Techniki projektowania przypadków testowych dla maszyny stanowej, oparte na kryteriach omówionych w punkcie 8.5.3 pozwalają efektywnie wykrywać tego typu problemy. Sam fakt wystąpienia takiego problemu nie musi oznaczać, że model jest zły. Maszyna stanowa opisuje po prostu, co ma się zdarzyć, jeśli jest w danym stanie i wystąpi określone zdarzenie. Nie uwzględnia ona tego, że w niektórych sytuacjach pewne przejścia, choć istnieją w modelu, nie są możliwe do zrealizowania w rzeczywistym programie. Można temu zaradzić przez wzbogacenie modelu, stosując dodatkowo formalne ograniczenia, wyrażane przez warunki czy też np. za pomocą języka OCL [68].

Rysunek 8.18. Maszyna stanowa z warunkami na niektórych przejściach Warunki można zastosować tak, jak to pokazano na rysunku 8.18. Jest to zmodyfikowana maszyna stanowa dla automatu biletowego z rysunku 8.16. Przed rozpoczęciem działania maszyny zmienna tryb jest ustawiana na pusty łańcuch znaków. Przejście ze stanu S1 do stanu S2 może nastąpić tylko, jeśli wystąpi zdarzenie KupBilet i jednocześnie zmienna tryb będzie pusta (będzie tak przy pierwszym przejściu) lub ustawiona na „b”. Dodatkową akcją, oprócz otwarcia wrzutnika, jest ustawienie zmiennej tryb na „b”. Dzięki temu zapamiętujemy pierwszy wybór użytkownika (kupno biletu) i maszyna nie zezwoli w przyszłości na przejście ze stanu S1 do S3, ponieważ zmienna tryb będzie ustawiona cały czas na „b”. Analogicznie, wybór wyświetlenia rozkładu jako pierwszego żądania użytkownika skutkować będzie ustawieniem zmiennej tryb na „r” i maszyna nie umożliwi przejścia z S1 do S2. Z formalnego punktu widzenia maszyna stanowa to siódemka M = (S, Z, A, f, λ, S0, T), gdzie: S jest skończonym, niepustym zbiorem stanów; Z jest skończonym, niepustym zbiorem zdarzeń;

A jest skończonym zbiorem akcji; f: S × Z → S jest funkcją (częściową) opisującą przejścia między stanami; λ: S × Z → A jest funkcją (częściową) opisującą występowanie akcji przy przejściach; S0 ∈ S jest stanem początkowym; T ⊂ S jest niepustym zbiorem stanów końcowych. Czasami funkcje f i λ reprezentuje się za pomocą jednej funkcji ∆: S × Z → S × A.

8.5.2. Tabelaryczne reprezentacje przejść Maszynę stanową można zdefiniować nie tylko za pomocą reprezentacji graficznej, lecz także przy użyciu tabel. Opiszemy dwie takie metody. Przydadzą się one podczas konstrukcji przypadków testowych dla różnych kryteriów pokrycia omówionych w punkcie 8.5.3. Metoda pierwsza to proste przekształcenie diagramu maszyny na tabelę. Dla maszyny z rysunku 8.16 jej postać tabelaryczna jest pokazana w tabeli 8.21. Tabela 8.21. Tabelaryczna postać maszyny stanowej automatu biletowego

Stan

Zdarzenie

Akcja

Nowy stan

S1

KupBilet

OtwórzWrzutnik

S2

S1

WybierzLinię

Wyś wietlRozkład

S3

S1

Koniec



S4

S2

Zapłać

DrukujBilet

S1

S2

Anuluj

ZwróćPieniądze

S4

S3

ZmieńLinię

Czyś ćEkran

S1

S3

Anuluj



S4

Tabela składa się z czterech kolumn. W pierwszej zapisujemy stan wyjściowy. Druga opisuje zdarzenie, jakie może nastąpić podczas przebywania w tym stanie. Trzecia i czwarta opisują odpowiednio akcję, jaka zajdzie oraz stan docelowy,

w jakim się znajdziemy, jeśli dane zdarzenie nastąpi. Tabela dla maszyny stanowej zawsze ma tyle wierszy, ile zdefiniowanych przejść w maszynie. Jest ona również tabelarycznym opisem funkcji ∆. Zwróćmy uwagę, że w tabeli opisano wyłącznie przejścia poprawne. Z punktu widzenia testera interesujące jest także sprawdzanie przejść niepoprawnych (tzw. testowanie negatywne). Aby zdefiniować pełną tabelę maszyny stanowej, uwzględniającą zarówno poprawne, jak i niepoprawne przejścia, musimy zidentyfikować najpierw wszystkie możliwe stany i zdarzenia. Mamy: S ={S1, S2, S3, S4} Z = {KupBilet, WybierzLinię, ZmieńLinię, Zapłać, Anuluj, Koniec} Tabela 8.22. Pełna tabela przejść maszyny dla automatu biletowego

Stan

Zdarzenie

Akcja

Nowy stan

S1

KupBilet

OtwórzWrzutnik

S2

S1

WybierzLinię

Wyś wietlRozkład

S3

S1

ZmieńLinię

(niezdefiniowane)

(niezdefiniowany)

S1

Zapłać

(niezdefiniowane)

(niezdefiniowany)

S1

Anuluj

(niezdefiniowane)

(niezdefiniowany)

S1

Koniec



S4

S2

KupBilet

(niezdefiniowane)

(niezdefiniowany)

S2

WybierzLinię

(niezdefiniowane)

(niezdefiniowany)

S2

ZmieńLinię

(niezdefiniowane)

(niezdefiniowany)

S2

Zapłać

DrukujBilet

S1

S2

Anuluj

ZwróćPieniądze

S4

S2

Koniec

(niezdefiniowane)

(niezdefiniowany)

S3

KupBilet

(niezdefiniowane)

(niezdefiniowany)

S3

WybierzLinię

(niezdefiniowane)

(niezdefiniowany)

S3

ZmieńLinię

Czyś ćEkran

S1

S3

Zapłać

(niezdefiniowane)

(niezdefiniowany)

S3

Anuluj



S4

S3

Koniec

(niezdefiniowane)

(niezdefiniowany)

S4

KupBilet

(niezdefiniowane)

(niezdefiniowany)

S4

WybierzLinię

(niezdefiniowane)

(niezdefiniowany)

S4

ZmieńLinię

(niezdefiniowane)

(niezdefiniowany)

S4

Zapłać

(niezdefiniowane)

(niezdefiniowany)

S4

Anuluj

(niezdefiniowane)

(niezdefiniowany)

S4

Koniec

(niezdefiniowane)

(niezdefiniowany)

Rozważamy teraz wszystkie kombinacje elementów S i Z. Ich liczba to liczba stanów pomnożona przez liczbę zdarzeń. W naszym przypadku będzie 4 × 6 = 24 kombinacji. Pełną tabelę przejść (tzw. tablicę stanów) konstruujemy dokładnie tak samo jak poprzednio, z jedną różnicą – dla kombinacji reprezentujących niepoprawne przejścia w kolumnach opisujących akcje i stany docelowe wpisujemy wartość „niezdefiniowane”. W tabeli 8.22 jest pokazana pełna tabela przejść dla automatu biletowego. tablica stanów (ang. state table) – tablica, która dla każdego stanu zestawia przejścia z tego stanu z każdym możliwym zdarzeniem. Obrazuje zarówno dozwolone, jak i niedozwolone przejścia Z formalnego punktu widzenia, tablica stanów jest równoważna częściowej funkcji przejść f rozszerzonej do funkcji pełnej, tzn. zdefiniowanej dla dowolnej pary (stan, zdarzenie).

8.5.3. Kryteria pokrycia dla maszyny stanowej

W przypadku maszyny stanowej istnieje kilka kryteriów pokryć, gdyż można wyróżnić w niej różne warunki testowe. Zanim je omówimy, zdefiniujmy maszynę stanową, którą wykorzystamy jako przykład ilustrujący te kryteria. Maszyna pokazana na rysunku 8.19 składa się z 6 stanów {P, Q, R, S, T ,U} oraz 10 przejść etykietowanych zdarzeniami {a, b, c, d, e, f, g, h, i, j}. Stan P jest początkowy, a T – końcowy.

Rysunek 8.19. Przykładowa maszyna stanowa Najprostszą metodą pokrycia maszyny stanowej jest wymóg, aby przejść przez każdy stan i wykorzystać każde przejście. Nazywa się je pokryciem 0-przełączeń (ang. 0-switch coverage). W takim przypadku zbiór elementów pokrycia to stany i przejścia. Jest to najprostsze i zarazem najsłabsze kryterium. Zauważmy, że spełnienie tego kryterium oznacza również wykonanie wszystkich możliwych akcji. Gdybyśmy do zbioru elementów pokrycia nie dodali stanów, to dla

maszyny złożonej z 1 stanu i 0 przejść kryterium byłoby spełnione z definicji, w szczególności dla pustego zbioru testów (bo w takiej maszynie nie ma żadnej krawędzi). Stosując kryterium 0-przełączeń dla maszyny z rysunku 8.19, otrzymujemy zbiór elementów pokrycia złożony z 6 stanów i 10 krawędzi: EP={P, Q, R, S, T, U, PR, RQ, QR, QU, UR, UT, RS, SP, ST, SS} Zbiór ten możemy pokryć na przykład następującymi dwoma przypadkami testowymi: PT1: P → a → R → b → Q → c → R → b → Q → d → U → e → R → g → S → j → S → i → T PT2: P → a → R → g → S → h → P → a → R → b → Q → d → U → f → T Pierwszy przypadek powoduje ciąg akcji a, b, c, b, d, e, g, j, i i pokrywa wszystkie stany oraz krawędzie PR, RQ, QR, QU, UR, RS, SS, ST. Drugi powoduje ciąg akcji a, g, h, a, b, d, f i dodatkowo pokrywa krawędzie SP oraz UT. Zauważmy, że aby spełnić kryterium pokrycia stanów i przejść musimy stworzyć co najmniej dwa przypadki, ponieważ stan T jest końcowy (nie da się z niego nigdzie przejść), a prowadzą do niego dwie krawędzie – od stanów S i U. W jednym przypadku testowym możemy ponadto wielokrotnie odwiedzać te same stany i krawędzie. Zwykle im dłuższy ciąg akcji, tym bardziej skomplikowany i trudniejszy do realizacji przypadek. Należy więc znaleźć złoty środek między długością ścieżek a liczbą przypadków testowych, gdyż jedna wartość jest odwrotnie proporcjonalna do drugiej. Zachowanie systemu może zależeć od tego, jaki był poprzedni stan. Drugie kryterium pokrycia, zwane pokryciem 1-przełączeń (ang. 1-switch coverage) bierze to pod uwagę i wymaga, aby pokryć wszystkie możliwe ścieżki o długości co najwyżej 2, to znaczy wszystkie stany, przejścia oraz pary przejść. Stan reprezentuje ścieżkę o długości 0. Forma tego kryterium może wydać się dosyć dziwna – dlaczego ścieżki o długości co najwyżej, a nie dokładnie 2? Chodzi o to, aby formalnie kryterium to subsumowało kryterium 0-przełączeń. Jeśli bowiem wymagalibyśmy wyłącznie pokrycia ścieżek o długości dokładnie 2, to w sytuacji maszyny o dwóch stanach i jednym przejściu między nimi kryterium byłoby spełnione przez pusty zbiór testowy, bo taka maszyna w ogóle nie ma ścieżek o długości większej niż 1. Identyfikacja elementów pokrycia reprezentujących stany oraz ścieżki o długości 1 jest trywialna – są to po prostu wszystkie stany i wszystkie przejścia.

W przypadku identyfikacji ścieżek o długości 2 dobrze jest zastosować systematyczną metodę ich znajdowania, aby uniknąć przeoczenia niektórych ścieżek. Metoda polega najpierw na wypisaniu wszystkich możliwych ścieżek o długości 1 (czyli 0-przełączeń) dla każdego możliwego stanu. W naszym przykładzie z rysunku 8.19 ścieżki te są zebrane w tabeli 8.23. Dla T nie ma żadnych przejść, gdyż jest on stanem końcowym. W sumie ścieżek powinno być dokładnie tyle, ile maszyna ma przejść, czyli 10. W drugim kroku metody bierzemy po kolei te ścieżki, dla każdej z nich patrzymy, jakim stanem X jest zakończona, a następnie tworzymy zbiór ścieżek o długości 2 przez doklejenie na końcu rozważanej ścieżki wszystkich możliwych końców ścieżek o długości 1 rozpoczynających się stanem X. Ścieżki te mamy wypisane w wierszu tabeli odpowiadającym stanowi X. Tabela 8.23. Krawędzie wychodzące z poszczególnych stanów

Stan

Ścieżki o długości 1 wychodzące z danego stanu

P

PR

Q

QR, QU

R

RQ, RS

S

SS, SP, ST

T

Ø

U

UR, UT

Popatrzmy, jak metoda działa w praktyce. Rozważmy ścieżkę PR. Kończy się ona stanem R. W wierszu tabeli odpowiadającym R znajdujemy ścieżki RQ i RS. Doklejamy więc do PR stany Q oraz S, otrzymując dwie ścieżki o długości 2: PRQ i PRS. Tak samo postępujemy dla każdej ścieżki o długości 1. W rezultacie otrzymujemy zbiór ścieżek o długości 2 zebranych w tabeli 8.24 (w ostatniej kolumnie). Tabela 8.24. Obliczanie ścieżek o długości 2

Ścieżka o długości 1

Możliwe kontynuacje

Zbiór wynikowych ścieżek o długości 2

PR

RQ, RS

PRQ, PRS

QR

RQ, RS

QRQ, QRS

QU

UR, UT

QUR, QUT

RQ

QR, QU

RQR, RQU

RS

SS, SP, ST

RSS, RSP, RST

SS

SS, SP, ST

SSS, SSP, SST

SP

PR

SPR

ST

Ø

Ø

UR

RQ, RS

URQ, URS

UT

Ø

Ø

Otrzymaliśmy 17 ścieżek o długości 2. Zauważmy, że w tym przykładzie pokrycie ścieżek o długości 2 zapewni jednocześnie pokrycie ścieżek o długości 1 oraz 0. Nasz zbiór elementów pokrycia może więc zawierać wyłącznie ścieżki o długości 2: EP={PRQ, PRS, QRQ, QRS, QUR, QUT, RQR, RQU, RSS, RSP, RST, SSS, SSP, SST, SPR, URQ, URS} Istnieją 3 ścieżki o długości 2 kończące się w T, dlatego zbiór testów spełniający pokrycie 1-przełączeń musi mieć co najmniej 3 testy. Do ich zaprojektowania można wykorzystać tzw. graf de Brujina11 [95]. Jego wierzchołkami są wszystkie ścieżki o długości 2, a między wierzchołkami X1 X2 X3 oraz Y1 Y2 Y3 krawędź występuje tylko wtedy, gdy ścieżki te „zachodzą” na siebie dwoma elementami, to znaczy gdy X2 = Y1 ∧ X3 = Y2. Graf de Brujina dla naszego automatu jest pokazany na rysunku 8.20.

Rysunek 8.20. Graf de Brujina dla ścieżek długości 2 Każda ścieżka testowa rozpoczyna się w wierzchołku zaczynającym się od stanu początkowego (w naszym przypadku P; wierzchołki te oznaczone są na biało) i kończy w wierzchołku kończącym się stanem końcowym (u nas jest to T; stany te są oznaczone podwójną obwódką). Kryterium 1-przełączeń będzie spełnione, jeśli pokryjemy ścieżkami testowymi wszystkie wierzchołki grafu de Brujina. Na przykład możemy stworzyć następujące 3 przypadki testowe: PRSSSPRSPRST (pokrywa PRS, RSS, SSS, SSP, SPR, RSP, RST); PRQRSPRQURSST (pokrywa dodatkowo PRQ, RQR, QRS, RQU, QUR, URS, SST); PRQRQURQUT (pokrywa dodatkowo QRQ, URQ, QUT). Wykorzystanie grafu de Brujina pozwala na kontrolowanie długości oraz liczby ścieżek. Pokrywając go, możemy manipulować ścieżkami, wydłużać je, skracać lub dodawać w razie potrzeby, tak, aby uzyskać zbiór ścieżek testowych satysfakcjonujący nas pod względem rozmiaru i długości. Kryteria 0- i 1-przełączeń można uogólnić na tzw. kryterium N-przełączeń dla N ≥ 0. Rozważanymi elementami pokrycia są wtedy sekwencje przejść (ścieżki) o długości co najwyżej N + 1. Tak jak poprzednio, sformułowanie „co najwyżej” jest użyte z powodów formalnych, aby kryterium N-przełączeń subsumowało kryterium K-przełączeń dla każdego 0 ≤ K < N.

pokrycie N-przełączeń (ang. N-switch coverage) – odsetek sekwencji przejść o długości co najwyżej N + 1 w maszynie stanowej, które były wykorzystane podczas wykonywania zestawu testowego [96] Metryki pokrycia N-przełączeń są też nazywane metrykami pokrycia Chowa (ang. Chow’s coverage metrics), od nazwiska ich twórcy, prof. Chowa. Ostatnim kryterium pokrycia jest tzw. pokrycie „wszystkich przejść” (ang. alltransitions). Jest to kryterium wymagające, aby przetestować wszystkie przejścia, zarówno poprawne, jak i niepoprawne. Należy zaznaczyć, że pokrycie Nprzełączeń dotyczy wyłącznie sekwencji przejść poprawnych. W tabeli 8.25 podsumowano metodę grafów przyczynowo-skutkowych. Tabela 8.25. Podsumowanie metody grafów przyczynowo-skutkowych

8.5.4. Diagram maszyny stanowej w UML W praktyce testerzy mają najczęściej do czynienia z maszynami stanowymi modelowanymi w języku UML. Notacja ta dopuszcza w budowie maszyny znacznie więcej elementów niż te, które omówiliśmy, w szczególności: stany złożone – jeden stan może reprezentować podmaszynę stanową; rozbudowaną strukturę stanu – oprócz nazwy stan może zawierać sekcję czynności wewnętrznych, sekcję przejść wewnętrznych oraz – w przypadku stanów złożonych – sekcję dekompozycji; obszary współbieżne – w ramach jednej maszyny niektóre ze stanów mogą być aktywowane współbieżnie; bogatą symbolikę opisującą scalanie, rozwidlenie, punkty węzłowe, decyzje, punkty zniszczenia.

Wzbogacona symbolika pozwala opisywać maszynami stanów np. działanie protokołów czy procesów biznesowych, ale należy pamiętać, że im bogatszy opis maszyny, tym z reguły trudniejszy proces testowania. Na przykład jeśli maszyna wykorzystuje obszary współbieżne, to należy podczas wykonywania przypadków testowych monitorować, czy odpowiednie sekwencje rzeczywiście wykonują się współbieżnie, a nie sekwencyjnie. Jeśli maszyna opisuje proces biznesowy, to również pewne czynności mogą odbywać się równolegle. Jeśli przypadek testowy obejmuje przejście przez stan złożony, to należy sprawdzić poprawność działania podmaszyny stanowej w tym stanie itd.

8.5.5. Przykład Redakcja czasopisma internetowego stosuje aplikację Czasopismo wspomagającą proces recenzowania i publikowania artykułów. Autor może wysłać swój artykuł redaktorowi, który dokonuje przeglądu wstępnego i decyduje, czy odrzucić artykuł (jeśli np. nie wpisuje się w profil czasopisma), czy też przekazać go do recenzji. Recenzent może skontaktować się z autorem i zażądać poprawek12. Po ich naniesieniu autor ponownie wysyła artykuł do redaktora. Ten przekazuje go recenzentowi. Recenzent w końcu podejmuje decyzję, czy zaakceptować artykuł do druku i przekazuje ją redaktorowi. Redaktor na podstawie recenzji decyduje o publikacji lub odrzuceniu artykułu. Schemat ten, z punktu widzenia cyklu życia artykułu, jest opisany maszyną stanową na rysunku 8.21. Chcemy przetestować aplikację, wykorzystując ten model.

Rysunek 8.21. Maszyna stanowa dla procesu opisującego pracę redakcji czasopisma Krok 1. Określenie przedmiotu i elementów testów. Mamy jeden przedmiot testów – aplikację Czasopismo: ET1: Czasopismo. Krok 2. Wyprowadzenie warunków testowych. Warunkami testowymi są stany oraz przejścia maszyny stanowej. Zauważmy, że ze stanu S3 do S2 biegną dwa przejścia – jedno wywołane zdarzeniem „odrzuć”, drugie zdarzeniem „akceptuj”. Analogicznie, mamy dwa przejścia ze stanu S2 do S4, wywołane odpowiednio zdarzeniami „publikuj” i „odrzuć”. Należy o tym pamiętać, ponieważ podczas pokrywania przejść trzeba będzie przejść przynajmniej dwa razy między stanem S3 i S2 (za każdym razem innym przejściem) oraz przynajmniej dwa razy między S2 i S4. Poniższa lista obejmuje wszystkie warunki testowe: WT1: S1 (pisanie); WT2: S2 (przegląd); WT3: S3 (recenzja); WT4: S4 (koniec); WT5: S1 → WY → S2 (wysłanie); WT6: S2 → PR → S3 (przekazanie); WT7: S3 → ZP → S1 (żądanie poprawy); WT8: S3 → AK → S2 (akceptuj); WT9: S3 → RN → S2 (recenzja negatywna); WT10: S2 → PU → S4 (publikuj i zakończ); WT11: S2 → OD → S4 (odrzuć i zakończ). Krok 3. Wyprowadzenie elementów pokrycia. Elementy pokrycia zależą od przyjętej metody testowania. Jeśli stosujemy kryterium pokrycia 0-przełączeń, to elementami pokrycia są wprost warunki testowe opisane w poprzednim kroku. Jeśli stosujemy kryterium 1-przełączeń, elementami pokrycia będą wszystkie ścieżki o długości 2 wypisane poniżej. Ścieżki te pokrywają jednocześnie wszystkie

przejścia i stany, więc nie musimy ich explicite dodawać do zbioru elementów pokryć. EP1: S1 → WY → S2 → PR → S3; EP2: S1 → WY → S2 → PU → S4; EP3: S1 → WY → S2 → OD → S4; EP4: S2 → PR → S3 → ZP → S1; EP5: S2 → PR → S3 → RN → S2; EP6: S2 → PR → S3 → AK → S2; EP7: S3 → ZP → S1 → WY → S2; EP8: S3 → RN → S2 → PR → S3; EP9: S3 → RN → S2 → PU → S4; EP10: S3 → RN → S2 → OD → S4; EP11: S3 → AK → S2 → PR → S3; EP12: S3 → AK → S2 → PU → S4; EP13: S3 → AK → S2 → OD → S4. Krok 4. Zdefiniowanie przypadków testowych. Dla kryterium pokrycia 0przełączeń przykładowy zestaw przypadków testowych jest pokazany w tabeli 8.26. Dla tego kryterium elementami pokrycia są wprost warunki testowe WT1– WT11. Pogrubioną czcionką zaznaczono elementy pokryte po raz pierwszy. Tabela 8.26. Przypadki testowe dla kryterium 0-przełączeń dla programu Czasopismo

Id

Ścieżka

Oczekiwany ciąg akcji

PT1

S1 → WY → S2 → PR → S3 → RN → S2 → OD → S4

Udos tRedaktorowi, Udos tRecencentowi, W T 1, W T 2, W T 3, W T 4, Udos tRedaktorowi, W T 5, W T 6, W T 9, W T 11 ZakończProces

S1 → WY → S2 → PR → S3 → ZP → S1 → WY →

Udos tRedaktorowi, Udos tRecenzentowi, WT1, WT2, WT3, WT4, Udos tAutorowi, WT5, WT6, W T 7, W T 8,

Pokryte elementy

PT2 S2 → PR → S3 → AK → S2 → PU → S4

Udos tRedaktorowi, W T 10 Udos tRecenzentowi, Udos tRedaktorowi OpublikujNaS tronie

Dla kryterium pokrycia 1-przełączeń możemy zdefiniować przypadki testowe tak, jak pokazano to w tabeli 8.27. Elementami pokrycia dla tego kryterium są ścieżki o długości 2, a więc elementy EP1–EP13 opisane wcześniej (elementy te w szczególności pokrywają wszystkie przejścia oraz stany, więc nie musimy ich dodawać do zbioru elementów pokryć). Tabela 8.27. Przypadki testowe dla kryterium 1-przełączeń dla programu Czasopismo

Id

Ścieżka

Oczekiwany ciąg akcji

Pokryte elementy

Udos tRedaktorowi, S1 → WY → S2 → PR → Udos tRecencentowi, S3 → PT1 Udos tAutorowi, EP1, EP2, EP4, EP7 ZP → S1 → WY → S2 → Udos tRedaktorowi, PU → S4 OpublikujNaS tronie PT2

S1 → WY → S2 → OD → S4

Udos tRedaktorowi, ZakończProces

EP3

Udos tRedaktorowi, Udos tRecenzentowi S1 → WY → S2 → PR → Udos tRedaktorowi, S3 → Udos tRecenzentowi, RN → S2 → PR → S3 → Udos tRedaktorowi AK → S2 → Udos tRecenzentowi EP1, EP4, EP5, EP6, PT3 R → S3 → ZP → S1 → EP7, EP8, EP9, EP11 Udos tAutorowi, WY → S2 → Udos tRedaktorowi, PR → S3 → RN → S2 → Udos tRecenzentowi, PU → S4

Udos tRedaktorowi, OpublikujNaS tronie Udos tRedaktorowi, S1 → WY → S2 → PR → Udos tRecenzentowi, PT4 S3 → EP1, EP6, EP13 Udos tRedaktorowi, AK → S2 → OD → S4 ZakończProces Udos tRedaktorowi, S1 → WY → S2 → PR → Udos tRecenzentowi, PT5 S3 → EP1, EP6, EP12 Udos tRedaktorowi, AK → S2 → PU → S4 OpublikujNaS tronie Udos tRedaktorowi, S1 → WY → S2 → PR → Udos tRecenzentowi, PT6 S3 → EP1, EP5, EP10 Udos tRedaktorowi, RN → S2 → OD → S4 ZakończProces Tester przy okazji tworzenia przypadków testowych może zwrócić uwagę na pewne problemy w logice biznesowej aplikacji Czytelnik: w PT1 publikacja artykułu następuje przed sprawdzeniem poprawek przez recenzenta (jest to możliwe, ale warto spytać analityka biznesowego, czy w każdej sytuacji jest to dopuszczalne); w PT3 występuje wiele problemów. Przy pokryciu EP8 następuje natychmiastowe odesłanie recenzentowi z powrotem artykułu, który przed chwilą recenzował. Przy pokryciu EP6 recenzent odrzuca artykuł, który wcześniej (przy EP5) zaakceptował. Przy pokryciu EP11 redaktor znów odsyła recenzentowi zaakceptowany już przez niego artykuł. Przy pokryciu EP4 recenzent żąda poprawek od autora, choć wcześniej zaakceptował artykuł. I na końcu ścieżki, przy pokryciu EP9, redaktor publikuje artykuł, choć ostatnia recenzja jest negatywna; w PT4 recenzent akceptuje artykuł, a redaktor go odrzuca (jest to dopuszczalne, choć należy upewnić się, czy każda tego typu sytuacja jest poprawna);

gdyby element pokrycia EP2 sam w sobie stanowił przypadek testowy (tzn. ścieżkę S1 → WY → S2 → PU → S4, oznaczałoby to, że artykuł jest opublikowany bez wysłania go do recenzji. Należy skonsultować się z analitykiem biznesowym, czy taka sytuacja jest dopuszczalna. Sama analiza ścieżek długości 2 może wykryć większość z wymienionych problemów. Część z nich ujawnia się dopiero przy konstrukcji większych (dłuższych) przypadków testowych.

8.6. Kategoria-podział (Category-Partition) 8.6.1. Opis metody Metoda kategoria-podział (K-P) została zaproponowana przez Ostranda i Blacera w 1988 roku [97]. Jest to wartościowa technika testowania opartego na specyfikacji, jeśli idzie o zarówno testy funkcjonalne, jak i testy API. Rozszerza ona znacznie możliwości takich technik, jak podział na klasy równoważności czy analiza wartości brzegowych. Omówione w kolejnych rozdziałach drzewa klasyfikacji oraz metody kombinacyjne można uznać za warianty metody K-P. Technika K-P umożliwia przekształcenie specyfikacji funkcjonalnej programu na specyfikację testów w systematyczny sposób. Polega na identyfikacji elementów wpływających na działanie funkcji programu, a następnie – generowaniu testów przez metodyczne układanie tych elementów w konfiguracje. Hipoteza błędu mówi, że błędy powstają na skutek złego obsługiwania kombinacji danych wejściowych. Metoda składa się z następujących kroków: 1. Zdekomponuj specyfikację funkcjonalną na funkcjonalne jednostki-moduły, które mogą być testowane niezależnie od siebie. 2. Zidentyfikuj parametry (wejścia do modułów) oraz warunki środowiskowe (tzn. stany systemu podczas jego działania), które wpływają na zachowanie się programu. 3. Wyodrębnij kategorie (główne właściwości lub charakterystyki) parametrów i warunków.

4. Podziel każdą kategorię na strefy wyboru, które są rozłączne i w sumie zawierają wszystkie możliwe wartości, jakie dana kategoria może przyjmować. 5. Określ ograniczenia i związki między strefami wyboru kategorii. 6. Napisz specyfikację testów (tzn. listę kategorii, ich podziały na strefy wyborów oraz ograniczenia między nimi), wykorzystując np. język specyfikacji testów (ang. Test Specification Language, TSL). 7. Wyprowadź elementy pokrycia (ręcznie lub np. przy użyciu generatora). Każdy element pokrycia to zbiór stref wyboru, przy czym jedna kategoria może być w nim reprezentowana co najwyżej jedną strefą wyboru. 8. Dla każdego elementu pokrycia stwórz przypadek testowy przez wybór odpowiednich danych wejściowych realizujących zdefiniowane wybory. Oryginalna metoda sugeruje użycie narzędzi automatyzujących generację elementów pokrycia (język typu TSL), ale można ten etap przeprowadzić również ręcznie, jeśli liczba kategorii i wyborów nie jest duża. Przy tworzeniu przypadków testowych można posłużyć się wyrocznią testową do określenia oczekiwanych wyjść. Krok 3 metody jest kluczowy i najbardziej twórczy. Zależnie od doświadczenia testera, jego znajomości obiektu testów oraz formy podstawy testów, wyniki tej fazy mogą się bardzo różnić. Każdy tester może dostarczyć odmienne kategorie, zwracając uwagę na różne aspekty działania systemu. Ważną cechą metody K-P jest stworzenie specyfikacji testów (p. 6) jako obiektu pośredniego między specyfikacją funkcjonalną a wyjściowym zbiorem przypadków testowych. Cel metody to generacja przypadków spełniających pokrycie kombinacji wyborów. Wadą metody jest to, że przy większej liczbie kategorii i wyborów liczba ich możliwych kombinacji (a więc i liczba przypadków testowych) rośnie bardzo szybko. Dlatego ważne jest stosowanie ograniczeń i związków między wyborami. Można również stosować mniej radykalne kryteria pokrycia, niż żądanie przetestowania wszystkich kombinacji. To podejście omówimy dokładniej w podrozdziale 8.8. W tabeli 8.28 podsumowano metodę kategoria-podział. Tabela 8.28. Podsumowanie metody kategoria-podział

8.6.2. Przykład Rozważmy

metodę getDisplayString klasy

timetableFacade

systemu

ELROJ

z Dodatku A. Chcemy ją przetestować, używając metody K-P. Krok 1. Dekompozycja specyfikacji funkcjonalnej na funkcjonalne moduły. Mamy tylko jedną testowaną metodę getDisplayString, która stanowi już osobną, funkcjonalną jednostkę dającą się testować niezależnie od innych elementów systemu. Krok 2. Identyfikacja parametrów i warunków. Bezpośrednimi parametrami, czyli wejściami do modułu (tzn. naszej metody) są zmienne actTimemm i displayLines oznaczające aktualny czas (w minutach) oraz liczbę linii ekranu. Za warunek środowiskowy uznajemy rozkład jazdy zapisany w bazie programu, ponieważ od jego postaci zależeć będzie wartość zwracana przez getDisplayString. Operowanie rozkładem jazdy jako takim nie byłoby zbyt wygodne, dokonajmy dalszej identyfikacji cech tego obiektu. Rozkład może cechować się tym, czy istnieje zdefiniowany w nim komunikat oraz liczbą zdefiniowanych linii autobusowych, co ma znaczenie z punktu widzenia wymagania R06 opisującego relację między ich liczbą a liczbą linii ekranu. Ważna może być liczba pojedynczych kursów (np. jak powinien zachować się program, jeśli w rozkładzie mamy tylko jedną linię i jeden kurs, a ekran ma 10 linii?). Krok 3. Wyodrębnienie kategorii parametrów i warunków. Dla parametru określającego liczbę linii autobusowych kategorią może być jej relacja do liczby linii ekranu oraz do ograniczeń nałożonych na liczbę linii autobusowych przez specyfikację. Dla zmiennej actTimemm kategorią może być jej ograniczenie zadane

przez specyfikację, ale też np. to, czy w rozkładzie istnieje kurs dla tej minuty (jest to

kategoria

odpowiadająca

wartościom

brzegowym).

W

tabeli

8.29

podsumowano wszystkie zidentyfikowane przez nas parametry wraz z ich kategoriami. Tabela 8.29. Parametry i kategorie metody getDisplayString

Parametr

Kategoria

aktualny czas w minutach (actTimemm)

wartoś ci brzeg owe i typowe is tnienie w rozkładzie kurs u o tej minucie

liczba linii ekranu (displayLines)

wartoś ci brzeg owe i typowe

komunikat

przypadki s pecjalne dług oś ć komunikatu

liczba linii autobus owych

wartoś ci brzeg owe i typowe relacja do liczby linii ekranu

liczba kurs ów

łączna liczba kurs ów a liczba linii ekranu wartoś ci brzeg owe i typowe

Krok 4. Podział kategorii na strefy wyboru. Przy podziale kategorii na strefy wyboru możemy wykorzystać techniki podziału na klasy równoważności oraz analizy wartości brzegowych, ale możemy również wprost posłużyć się specyfikacją, która w niektórych przypadkach bardzo ściśle określa pewne zależności między różnymi artefaktami. Podział zidentyfikowanych w poprzednim kroku kategorii na strefy wyboru jest pokazany w tabeli 8.30. Tabela 8.30. Parametry, kategorie i wybory dla metody getDisplayString

Parametr

Kategoria

W ybory

wartoś ci brzeg owe i typowe

actTimemm = 0 actTimemm = n, 1 ≤ n ≤ 58 actTimemm = 59

aktualny czas

liczba linii ekranu

is tnienie kurs u o tej minucie

actTimemm = n i is tnieje kurs o minucie n actTimemm = n i nie is tnieje kurs o minucie n

wartoś ci brzeg owe i typowe

displayLines displayLines displayLines displayLines displayLines

przypadki s pecjalne

brak komunikatu komunikat=null komunikat=” ”

dług oś ć komunikatu

0 1 typowa (między 2 a MAX – 1) MAX

wartoś ci brzeg owe i typowe

0 1 2 ≤ n ≤ 99 100

relacja do liczby linii ekranu

mniej niż linii ekranu – 1 dokładnie tyle ile linii ekranu –1 więcej niż linii ekranu – 1

komunikat

liczba linii autobus owych

łączna liczba kurs ów a l. linii ekranu

=2 =3 = n, 4 ≤ n ≤ 99 = 101 > 101

kurs ów mniej niż linii ekranu –1 kurs ów dokładnie tyle ile linii ekranu – 1

kurs ów więcej niż linii ekranu – 1

liczba kurs ów

wartoś ci brzeg owe i typowe

0 1 2 ≤ n ≤ 5999 6000

Maksymalna liczba kursów dla jednej linii to 60 (w minutach 0, 1, 2, …, 59). Maksymalna liczba linii autobusowych to 100, zatem maksymalna liczba kursów to 6000. Nie mamy górnego ograniczenia na liczbę linii ekranu, zatem nie możemy tu operować wartością maksymalną. Wartość MAX dla długości komunikatu może wynikać np. z typu zmiennej przechowującej go. Dla poszczególnych kategorii z tabeli mamy odpowiednio 3, 2, 5, 3, 4, 4, 3, 3, 4 wyborów. Jeśli chcielibyśmy testować konfiguracje wszystkich możliwych wyborów dla wszystkich możliwych kategorii, to musielibyśmy rozważyć 3 ⋅ 2 ⋅ 5 ⋅ 3 ⋅ 4 ⋅ 4 ⋅ 3 ⋅ 3 ⋅ 4 = 51 840 kombinacji. Jest to stanowczo zbyt dużo. Możemy ograniczyć liczbę testów, rozważając kombinacje nie kategorii, ale parametrów. Dla poszczególnych parametrów mamy odpowiednio 5, 5, 7, 7 i 7 wyborów, zatem liczba wszystkich możliwych kombinacji wyborów wszystkich parametrów wynosi 52 ⋅ 73 = 8575, co nadal jest zbyt dużą liczbą. Zobaczmy, jak liczba ta zredukuje się po zastosowaniu ograniczeń na wybory. Krok 5. Określenie ograniczeń wyborów. W naszej tabeli możemy wskazać kilka ograniczeń między wyborami. Na przykład, dla każdego z wyborów kategorii „przypadki specjalne” parametru komunikat jedynym możliwym wyborem długości komunikatu jest 0. Niestety ten warunek nie wpłynie na zmniejszenie liczby testów, gdy rozważamy kombinacje parametrów, a nie kategorii, ponieważ obie wspomniane kategorie wchodzą w skład tego samego parametru. Inne ograniczenie wiąże ze sobą kategorię „relacja do liczby linii ekranu” parametru „liczba linii autobusowych” oraz kategorię „łączna liczba kursów”. Niech K = liczba linii ekranu – 1. Kursów nie może być więcej niż linii, dlatego jeśli kursów jest mniej niż K, to liczba linii autobusowych musi być również mniejsza niż K. Jeśli kursów jest dokładnie K, to linii może być K lub mniej. To ograniczenie redukuje liczbę przypadków o 525 do 8050. Część ograniczeń wynika z przyjęcia

konkretnych wartości niektórych zmiennych. Na przykład przyjęcie liczby linii autobusowych = 0 implikuje, że liczba kursów musi być 0 lub mniejsza niż liczba linii ekranu – 1. Jeśli nadal mamy zbyt dużo przypadków, to możemy spróbować połączyć kategorie ze sobą. Na przykład jeśli dla parametru aktualny czas połączymy obie kategorie w jedną, to możemy otrzymać tylko 3 możliwe wybory, np.: n = 0 i istnieje taki kurs w rozkładzie, 1 ≤ n ≤ 58 i istnieje taki kurs w rozkładzie, n = 59 i nie istnieje taki kurs w rozkładzie. Gdy to też nie pomoże i nadal mamy zbyt dużo kombinacji, możemy traktować niektóre parametry oddzielnie. Na przykład, możemy testować osobno kombinacje liczby linii ekranu z liczbą linii autobusowych (nie dbając o wartości kategorii innych parametrów), osobno kombinacje komunikatów i liczby kursów oraz osobno aktualny czas. W takim układzie liczba wszystkich kombinacji, nie uwzględniając ograniczeń, wyniesie 5 ⋅ 7 + 7 ⋅ 7 + 5 = 89. To już jest sensowna liczba przypadków testowych. Tego typu ograniczenia omówimy dokładnie w podrozdziale 8.8. Jak widać, tzw. techniki kombinacyjne pozwalają istotnie ograniczyć liczbę możliwych testów. Krok 6. Specyfikacja testów. Specyfikacja będzie wyglądać podobnie do tabeli 8.30, z tym że może być ona wyrażona za pomocą języka specyfikacji testów. Krok 7. Wyprowadź elementy pokrycia. Elementy pokrycia zależą od przyjętego kryterium pokrycia. Jeśli jest nim kombinacja wartości wszystkich możliwych kategorii, to każdy element pokrycia będzie reprezentował jedną kombinację. Jeśli – tak jak w kroku 5 powyżej – osłabiamy kryterium pokrycia, elementów może być mniej, np. część z nich może reprezentować kombinacje wartości jednego podzbioru kategorii, a część innego. Krok 8. Stwórz przypadki testowe. Dla każdego elementu pokrycia należy dostarczyć dane wejściowe dla przypadków testowych tak, by pokryły te elementy. Oto przykładowy przypadek testowy dla metody getDisplayString. Załóżmy, że element pokrycia wygląda następująco: aktualny czas: n = 59 i istnieje kurs o tej minucie; liczba linii ekranu: 3; komunikat: brak komunikatu;

liczba linii autobusowych: dokładnie tyle, ile linii ekranu – 1 (czyli 2); liczba kursów: 2 ≤ n ≤ 5999, np. n = 4. Parametrami wejściowymi dla metody będą w tym przypadku actTimemm = 59 oraz displayLines = 3, ale musimy jeszcze skonfigurować środowisko testowe tak, by postać rozkładu jazdy czyniła zadość opisanym warunkom. Konfigurację możemy przeprowadzić np. w sposób podany na listingu 8.3.

TimetableFacade facade = new TimetableFacade(); facade.addBus(110, 12); facade.addBus(111, 59); facade.addBus(111, 10); facade.addBus(110, 13); Listing 8.3. Konfiguracja środowiska testowego dla wykonania testu metody getDisplayString Jest to fragment kodu pozwalający na stworzenie rozkładu jazdy spełniającego wymagane warunki. Dodaliśmy cztery kursy dla dwóch linii (o numerach 110 i 111). Czas przyjazdu jednego z kursów jest taki sam jak parametr wywołania actTimemm. Pełny opis przypadku testowego wygląda zatem następująco: konfiguracja: rozkład z 2 liniami: 100 (o przyjazdach w minutach 12, 13) i 111 (o przyjazdach w minutach (komunikat=null), data: 1 stycznia 2014;

13,

59),

dane wejściowe: actTimemm = 59, displayLines = 3; oczekiwane wyjście:

01.01.2014 LINIA

Czas przyjazdu

111

0 min

111

11 min

110

13 min

brak

komuniaktu

8.7. Drzewa klasyfikacji 8.7.1. Opis metody Metoda drzew klasyfikacji to kolejna technika czarnoskrzynkowa, będąca z jednej strony szczególnym przypadkiem techniki kategoria-podział, z drugiej zaś jej istotnym rozszerzeniem. Tak jak w przypadku metody kategoria-podział, podczas stosowania drzew klasyfikacji dziedzina wejściowa testowanego obiektu jest dzielona ze względu na różne aspekty istotne z punktu widzenia testowania. Dla każdego aspektu jest tworzona rozłączna i pełna klasyfikacja (tzn. klasy są rozłączne i obejmują wszystkie możliwości). W tym sensie metoda jest szczególnym przypadkiem techniki kategoria-podział, gdyż wymaga rozłączności aspektów, co nie występuje w przypadku kategorii i wyborów (np. w przykładzie z p. 8.6.2 kategorie „wartości brzegowe i typowe” oraz „istnienie kursu o danej minucie” dla parametru „aktualny czas” nie są rozłączne). Drzewa klasyfikacji rozszerzają jednak technikę kategoria-podział, ponieważ dopuszczają dalszą klasyfikację uzyskanych klas. Krokowy proces podziału dziedziny wejściowej ze względu na różne klasyfikacje jest przedstawiany w formie drzewa. Przypadki testowe są tworzone na podstawie wyboru co najwyżej jednej klasy z każdej klasyfikacji. Cały proces pozwala tworzyć przypadki w sposób prosty, systematyczny i formalny. Tak jak w przypadku metody kategoria-podział, w technice drzew klasyfikacji można zdefiniować różnego rodzaju kryteria pokrycia, aby uniknąć wykładniczego wzrostu generowanych przypadków. drzewo klasyfikacji (ang. classification tree) – drzewo obrazujące hierarchiczną zależność klas równoważności używane do projektowania przypadków testowych metodą drzewa klasyfikacji metoda drzewa klasyfikacji (ang. classification tree method) – czarnoskrzynkowa technika projektowania przypadków testowych, w której przypadki te są projektowane do wykonania kombinacji reprezentantów wejść i/lub przetworzonych wyjść [98]

8.7.2. Budowa drzewa klasyfikacji Opis budowy drzewa zilustrujemy za pomocą przykładu pochodzącego z [98]. Rozważmy System Komputerowego Rozpoznawania Obiektów (SKRO), który schematycznie jest pokazany na rysunku 8.22. Składa się on z pasa transmisyjnego oraz kamery podłączonej do komputera. Na pasie znajdują się obiekty różnego kształtu, koloru i rozmiaru. Zadaniem systemu jest rozpoznać typ obiektu. Zbudujemy drzewo klasyfikacji dla tego systemu, opisując przy okazji funkcje jego różnych elementów składowych. Do budowy drzewa klasyfikacji wykorzystuje się cztery typy wierzchołków: korzeń – reprezentuje testowany obiekt (w naszym przypadku SKRO); klasyfikacja – wierzchołek reprezentujący pojedynczy aspekt, w jakim rozważany jest testowany obiekt;

Rysunek 8.22. System komputerowego rozpoznawania obiektów klasa – charakterystyka (element) klasyfikacji; kompozycja – wierzchołek reprezentujący zbiór klasyfikacji.

Dziećmi korzenia, kompozycji i klas mogą być wyłącznie klasyfikacje i kompozycje. Dziećmi klasyfikacji mogą być wyłącznie klasy. Klasy są powiązane z przypadkami testowymi za pomocą tzw. macierzy testów, którą omówimy w dalszej części. Klasy różnych klasyfikacji wchodzących w skład kompozycji nie muszą być parami rozłączne. Na rysunku 8.23 przedstawiono notację wykorzystywaną do oznaczania poszczególnych typów wierzchołków drzewa.

Rysunek 8.23. Typy wierzchołków w drzewie klasyfikacji System SKRO ma rozpoznawać obiekty, dlatego z punktu widzenia testowania istotne mogą się okazać takie aspekty jak kolor czy wymiary. Kolor przyjmiemy jako klasyfikację, natomiast wymiar potraktujemy jak kompozycję dwóch klasyfikacji: rozmiaru oraz kształtu. Klasyfikacje dzielimy na klasy: kształt obiektów (możliwe klasy: kwadrat, koło, trójkąt); kolor obiektów (możliwe klasy: czarny, szary, biały); rozmiar obiektów (możliwe klasy: duży, mały). Możemy również w szczególny sposób potraktować trójkąty i dodatkowo zdefiniować dla tej klasy klasyfikację „typ” dzielącą trójkąty na 3 klasy: równoboczne, równoramienne i różnoboczne. Drzewo klasyfikacji dla systemu SKRO jest pokazane na rysunku 8.24. Pod drzewem klasyfikacji znajduje się tzw. macierz testów. Jej wiersze (oznaczone poziomymi liniami) odpowiadają poszczególnym przypadkom testowym. Kolumny (linie pionowe) odpowiadają wszystkim klasom występującym w drzewie. Czarna kropka na

Rysunek 8.24. Drzewo klasyfikacji dla systemu SKRO wraz z macierzą testową przecięciu przypadku i klasy oznacza, że w danym przypadku testowym musi wystąpić wartość wejściowa odpowiadająca tej klasie. Na przykład, w PT1 testowanym obiektem jest duży, czarny okrąg, w PT2 – mały, biały trójkąt równoramienny itd. Zauważmy, że w PT4 nie dokonaliśmy wyboru klasy dla klasyfikacji „kolor”. Jest to dopuszczalne, jeśli jakaś cecha jest opcjonalna. Możemy na przykład stwierdzić, że kolor jest cechą, która nie zawsze będzie nas interesować i pominąć ją w niektórych przypadkach testowych13. Przypadki testowe zwykle generuje się automatycznie, na podstawie określonego kryterium pokrycia. Automatyzacja pozwala na szybką generację testów oraz na uniknięcie pomyłek polegających na przeoczeniu jakichś kombinacji klas. Najbardziej oczywistym kryterium jest pokrycie wszystkich

kombinacji klas, jednak – tak jak w metodzie kategoria-podział – może to doprowadzić do bardzo dużej liczby przypadków testowych. Dla drzewa pokazanego na rysunku 8.24 mamy trzy niezależne klasyfikacje: rozmiar (2 klasy), kształt (5 klas) i kolor (3 klasy), co daje w sumie 2 ⋅ 5 ⋅ 3 = 30 kombinacji. W przypadku zbyt dużej liczby testów można zastosować techniki kombinacyjne z podrozdziale 8.8.

8.7.3. Asortyment produktów programowych i model cech Asortyment produktów programowych to podejście stosowane w celu ulepszenia ponownego użycia (ang. reusability) komponentów oprogramowania w większej liczbie produktów, które współdzielą ten sam zbiór cech [99], [100]. Przez cechę rozumiemy tu przyrost funkcjonalności oprogramowania. asortyment produktów programowych (ang. Software Product Line, SPL) – zbiór systemów silnie opartych na oprogramowaniu, współdzielących ten sam, zarządzany zbiór cech spełniający określone potrzeby danego segmentu rynku; systemy te są wytwarzane na podstawie wspólnego zbioru zasobów, zgodnie ze ściśle ustalonymi regułami Przy tworzeniu asortymentu produktów programowych często korzysta się z tzw. modeli cech (ang. feature models). Modele cech można traktować jak odmianę drzew klasyfikacji, w których każdy wierzchołek reprezentuje klasyfikację odpowiadającą określonej cesze. To specyficzne zastosowanie drzew klasyfikacji jest okazją do opisania ich rozszerzonej funkcjonalności, mianowicie ograniczeń i zależności między klasami oraz właściwości pojedynczych klasyfikacji. Modele cech służą do specyfikowania elementów asortymentu produktów programowych. Rozważmy na przykład linię produkcyjną samochodów określonej marki. Każdy samochód może mieć różny standard wyposażenia. Niektóre elementy wyposażenia wymagają obecności innych. Inne wzajemnie się wykluczają. Aby przetestować wszystkie konfiguracje cech z uwzględnieniem zależności i ograniczeń między nimi należy zbudować na podstawie modelu cech drzewo klasyfikacji i wygenerować przypadki testowe, uwzględniając te ograniczenia.

Model cech jest podobny do drzewa klasyfikacji, gdyż przedstawia hierarchicznie zorganizowany zbiór cech. Używa jednak dodatkowej notacji, pokazanej na rysunku 8.25.

Rysunek 8.25. Notacja dla modelu cech Oto znaczenie poszczególnych symboli: XOR: dokładnie jedna podcecha musi zostać wybrana; OR: jedna lub więcej podcech może zostać wybranych; obowiązkowe: cecha jest wymagana; opcjonalne: cecha jest opcjonalna; wymaga: wybór cechy A wymusza wybór cechy B; wyklucza: wybór cechy A wymusza pominięcie cechy B. Rozważmy przykład zaczerpnięty z [101]. Jest to asortyment produktu programowego smartfonu z systemem operacyjnym Android. Model cech dla tego asortymentu jest przedstawiony na rysunku 8.26.

Rysunek 8.26. Model cech dla asortymentu produktu programowego smartfonu Cechy „podstawowe funkcje”, „wiadomość”, „połączenie głosowe” i „SMS” są obowiązkowe i każdy model smartfonu musi je mieć. Cecha „MMS” jest opcjonalna. „Komunikacja” i jej podcechy: „WLAN”, „Bluetooth” i „UMTS” również są opcjonalne. Cecha „dodatki” jest obowiązkowa i użycie operatora OR nakazuje wybrać co najmniej jedną spośród cech „MP3” i „aparat”. Oczywiście można wybrać obie w ramach jednego produktu. Jeśli cecha „aparat” jest wybrana, to musimy wybrać dokładnie jedną z cech „3 megapiksele” oraz „8 megapikseli”. Ponadto wybór „MP3” wyklucza istnienie cechy „Bluetooth” i na odwrót. Wybór cechy „MMS” wymaga wyboru cechy „aparat”. Jak widać, istnieje kilka reguł, które muszą być spełnione podczas wyboru cech dla modelu smartfona: cecha w korzeniu musi być zawsze wybrana; wybór cech powinien mieć wartość logiczną true dla formuły opisującej cechy i związki między nimi; wszystkie ograniczenia („wymaga” i „wyklucza”) muszą być spełnione; dla każdej wybranej cechy, która nie jest korzeniem, jej rodzic również musi być wybrany. Dla naszego przykładu istnieje 61 możliwych wyborów zestawów cech spełniających wymienione kryteria, co daje łącznie 61 przypadków testowych. Tak jak poprzednio, możemy zredukować ich liczbę, stosując jedno z kombinatorycznych kryteriów pokrycia. W literaturze (np. [102]) zaproponowano wiele różnych strategii testowania przy użyciu drzew klasyfikacji i asortymentu produktów programowych, takich jak: product-by-product, gdzie każda instancja SPL jest testowana indywidualnie, np. przy użyciu drzew klasyfikacji (niezbyt praktyczna, gdyż zwykle generuje bardzo dużą liczbę przypadków testowych); testowanie inkrementacyjne, gdzie pierwszy produkt jest testowany indywidualnie (np. przy użyciu drzew klasyfikacji), a w każdym następnym – ze względu na dzielenie wspólnego zbioru cech – stosuje się techniki testów regresyjnych (patrz p. 4.4.4); problemem jest to, czy testowaniu podlegać mają wyłącznie cechy nowe i zmodyfikowane oraz

czy mogą być przetestowane w izolacji, czy też należy je testować w kombinacji z jakimiś nie zmienionymi komponentami; reużywalne testy, gdzie za pomocą inżynierii dziedzinowej tworzy się reużywalne przypadki testowe; podział odpowiedzialności, gdzie poszczególne typy testów wykonuje się w różnych fazach wytwórczych (np. w cyklu opisanym modelem V). W tabeli 8.31 podsumowano metodę drzew decyzyjnych. pokrycie drzewa klasyfikacji (ang. classification tree coverage) – odsetek pokrytych testami kombinacji wyborów klas występujących w drzewie, z uwzględnieniem zadanych ograniczeń Tabela 8.31. Podsumowanie metody drzew decyzyjnych

W arunki Klasyfikacje i klasy testowe Elementy Kombinacje wyborów klas (z uwzg lędnieniem og raniczeń) pokrycia Dla każdej zidentyfikowanej kombinacji wyborów is tnieje Kryterium tes t, któreg o dane wejś ciowe realizują te wybory (kryterium pokrycia można os łabić, redukując liczbę możliwych kombinacji – patrz podrozdział 8.8) Pokrycie

p=

liczba pokrytych tes tami kombinacji wyborów liczba ws zys tkich kombinacji wyborów

× 100%

8.7.4. Przykład Przetestujmy system SKRO na podstawie drzewa klasyfikacji z rysunku 8.24. Krok 1. Określenie przedmiotu i elementów testów. Mamy tylko jeden element testów, system SKRO:

ET1: SKRO. Krok

2.

Wyprowadzenie

warunków

testowych.

Warunki

testowe



identyfikowane przez wyprowadzenie klasyfikacji i klas dla każdego z parametrów. Załóżmy ponadto, że klasyfikacja „kolor” jest opcjonalna. WT1: rozmiar (mały, duży); WT2: kształt (okrąg, trójkąt, kwadrat); WT3: typ (równoboczny, równoramienny, różnoboczny); WT4: kolor (biały, szary, czarny); WT5: kolor = null. Wszystkie warunki testowe są wyprowadzone dla elementu testowego ET1. Krok 3. Wyprowadzenie elementów pokrycia. Elementy pokrycia określamy na podstawie przyjętego kombinacyjnego kryterium pokrycia, które określa, jakie kombinacje klas oraz których klasyfikacji są wymagane do przetestowania. Przyjmijmy podejście minimalistyczne, tzn. takie, które wymaga, aby każdy nowy element pokrycia pokrywał jak najwięcej elementów drzewa (tzn. liści, które oznaczają klasy). Drzewo klasyfikacji i macierz testów dla tego podejścia są pokazane na rysunku 8.27. EP1 pokrywa WT1, WT2 i WT4; EP2 pokrywa WT1, WT2, WT3 i WT4; EP3 pokrywa WT1, WT2, WT3 i WT4; EP4 pokrywa WT1, WT2, WT3 i WT5; EP5 pokrywa WT1, WT2 i WT5.

Rysunek 8.27. Drzewo klasyfikacji wraz z tabelą kombinacji dla kryterium minimalistycznego Tabela 8.32. Przypadki testowe dla programu SKRO

Id

W ejścia rozmiar kształt

PT1 mały

okrąg

PT2 duży

trójkąt równoboczny

PT3 mały

Oczekiwane wyjście

Pokryte EP

biały

mały biały okrąg

EP1

s zary

duży s zary trójkąt równoboczny

EP2

kolor

mały czarny trójkąt trójkąt równoramienny czarny równoramienny

EP3

PT4 duży

trójkąt różnoboczny

null

duży trójkąt różnoboczny

EP4

PT5 mały

kwadrat

null

mały kwadrat

EP5

Krok 4. Zdefiniowanie przypadków testowych. Każdy przypadek testowy pokrywa dokładnie jeden element pokrycia. Przypadki testowe są tworzone jeden po drugim przez wybór niepokrytego dotąd elementu pokrycia oraz wartości wejściowych pokrywających klasy wyznaczone przez kombinację odpowiadającą temu elementowi pokrycia. Procedura jest powtarzana dopóty, dopóki nie zostanie uzyskany założony poziom pokrycia. Uzyskanie koloru „null” można wyobrazić sobie np. jako zaprezentowanie systemowi obiektu w kolorze innym niż biały, szary i czarny. Zestaw przypadków pokrywających wszystkie elementy pokrycia jest przedstawiony w tabeli 8.32.

8.8. Metody kombinacyjne 8.8.1. Opis metody Celem metod kombinacyjnych jest redukcja rozmiaru suity testowej w przypadku, gdy testujemy jednocześnie kilka aspektów (charakterystyk) systemu i liczba wszystkich kombinacji do przetestowania jest zbyt duża. Są to metody, które można traktować jak niezależne techniki projektowania testów, ale wykorzystuje się je również podczas stosowania innych technik, takich jak podział na klasy równoważności, tablice decyzyjne, kategoria-podział czy drzewa klasyfikacji. Istnieje wiele odmian metody, różniących się tym, jak silne jest kryterium pokrycia. Im silniejsze, tym więcej generuje przypadków testowych. W metodach kombinacyjnych zakładamy, że dziedzina wejściowa, wyjściowa lub ich części może być podzielona na kilka sposobów. Innymi słowy, program może zostać opisany za pomocą wielu różnych charakterystyk. Formalnie, niech D oznacza dziedzinę testowanego programu. Dane są zbiory D1, D2, … Dk ⊆ D oraz relacje równoważności ρ1, ρ2, …, ρk takie, że ρi dzieli zbiór Di na d i klas równoważności. Przypadek testowy ma postać (x1, x2, …, xk ), gdzie xi ∈ Di. Relacje

ρi powodują, że elementy pozostające ze sobą w relacji traktujemy podobnie i tak, jak w przypadku metody podziału na klasy równoważności, aby przetestować klasę wystarczy skonstruować test dla jednej, dowolnej wartości z tej klasy. Mamy do czynienia z wieloma podziałami, dlatego sytuacja się komplikuje. Naszym celem może być przecież nie tylko przetestowanie każdej klasy z osobna, lecz także kilku klas różnych relacji w kombinacji ze sobą. Im więcej kombinacji chcemy przetestować, tym więcej przypadków testowych będziemy musieli stworzyć. W skrajnym przypadku, gdy chcemy przetestować kombinacje klas ze wszystkich podziałów D1, D2, …, Dk , musielibyśmy stworzyć d 1 ⋅ d 2 ⋅ … ⋅ d k testów. W kolejnych podrozdziałach opiszemy kilka technik kombinacyjnych. Wszystkie omówimy na podstawie następującego przykładu. Zespół deweloperski tworzy produkt komercyjny „z półki” (ang. Comercial Off The Shelf, COTS). To oznacza, że należy go przetestować w wielu środowiskach. Program współpracuje z przeglądarką, drukarką, systemem operacyjnym oraz bazą danych. Wymagania opisują następujące rodzaje wymienionych obiektów, z którymi program musi współpracować: rodziny przeglądarek (IE, Firefox, Chrome); rodziny drukarek (HP, Epson, Samsung, Canon); rodziny systemów operacyjnych (Windows, Linux, iOS); silniki baz danych (MySQL, PostgreSQL). Dla uproszczenia nie uwzględniamy tu konkretnych modeli wymienionych obiektów. W praktyce mogłaby zajść potrzeba rozróżniania tych obiektów na poziomie wersji. Wszystko zależy od analizy ryzyka, tego, co chcemy przetestować oraz jaką przyjmujemy hipotezę błędu. Nasz system może np. bardzo silnie zależeć od architektury systemu operacyjnego, a mniej od rodzaju podłączonej do niego drukarki. Dla każdej konfiguracji oczekiwanym wyjściem powinno być poprawne działanie programu, bez żadnych komunikatów o błędach. Opisaliśmy cztery charakterystyki programu, dotyczące rodzaju przeglądarki, drukarki, systemu operacyjnego oraz bazy, z jakimi może pracować nasz system: D1 = {IE, Firefox, Chrome}; D2 = {HP, Epson, Samsung, Canon};

D3 = {Windows, Linux, iOS}; D4 = {MySQL, PostgreSQL}. Dziedzina (charakterystyka) D1 została podzielona na 3 klasy równoważności, D2 na 4, D3 na 3 i D4 na 2. Każdy przypadek testowy dotyczy uruchomienia systemu w określonej konfiguracji środowiska, zatem jest charakteryzowany wejściem złożonym z czterech elementów: przeglądarka, drukarka, system operacyjny oraz baza danych. Elementem testowym jest nasz program. Mamy następujące warunki testowe (wszystkie dla tego elementu testowego): WT1: Przeglądarka = IE; WT2: Przeglądarka = Firefox; WT3: Przeglądarka = Chrome; WT4: Drukarka WT5: Drukarka WT6: Drukarka WT7: Drukarka

= HP; = Epson; = Samsung; = Canon;

WT8: System operacyjny = Windows; WT9: System operacyjny = Linux; WT10: System operacyjny = iOS; WT11: Baza danych = MySQL; WT12: Baza danych = PostgreSQL.

8.8.2. Each Choice Kryterium Each Choice wymaga, aby każda klasa każdej charakterystyki została przetestowana przynajmniej raz. Elementy pokrycia są wywodzone wprost z warunków testowych. EP1: Przeglądarka = IE (dla WT1); EP2: Przeglądarka = Firefox (dla WT2); EP3: Przeglądarka = Chrome (dla WT3); EP4: Drukarka = HP (dla WT4); EP5: Drukarka = Epson (dla WT5); EP6: Drukarka = Samsung (dla WT6);

EP7: Drukarka = Canon (dla WT7); EP8: System operacyjny = Windows (dla WT8); EP9: System operacyjny = Linux (dla WT9); EP10: System operacyjny = iOS (dla WT10); EP11: Baza danych = MySQL (dla WT11); EP12: Baza danych = PostgreSQL (dla WT12). Każdy przypadek testowy powinien pokrywać jeden lub więcej niepokrytych dotąd elementów pokrycia. W praktyce, stosując metodę Each Choice, testerzy zwykle starają się, aby każdy kolejny przypadek testowy pokrywał jak najwięcej niepokrytych dotąd elementów pokrycia. Czasami jest to niemożliwe ze względu na istniejące ograniczenia (np. jeśli przeglądarka IE nie współpracuje z systemem operacyjnym Linux, elementy te nie mogą współistnieć w ramach jednego przypadku testowego). W ogólności, dolne ograniczenie na minimalną liczbę przypadków testowych wynosi max i{d i}, gdzie d i jest liczbą klas i-tej charakterystyki. W naszym pokazane w tabeli 8.33.

przykładzie wystarczą

4 przypadki

testowe,

Tabela 8.33. Przypadki testowe spełniające kryterium Each Choice14

Id

W ejścia przeglądarka drukarka OS

PT1 IE

PT2 Firefox

HP

Eps on

baza danych

działa bez błędów14

EP1 EP8 EP1

działa bez Pos tg reS QL błędów

EP2 EP5 EP9 EP1

Windows MyS QL

Linux

Oczekiwane Pok wyjście EP

EP3 EP6 PT3 Chrome

S ams ung iOS

MyS QL

PT4 IE

Canon

Windows Pos tg reS QL

działa bez błędów

EP1 EP1

działa bez błędów

EP1 EP7 EP8 EP1

W przypadku PT4 musieliśmy wybrać drukarkę Canon, gdyż był to ostatni niepokryty element. Pozostałe trzy (przeglądarka, OS i baza danych) wybraliśmy arbitralnie. Należy jednak pamiętać, że dobre testowanie nie polega na ślepym podążaniu za regułami narzuconymi przez określoną technikę. Tester, znając system i wiedząc, gdzie może tkwić błąd, może wykorzystać taką sytuację, aby precyzyjnie określić pozostałe wejścia i zwiększyć szansę na wykrycie błędu.

8.8.3. Base Choice Czasami okazuje się, że pewne kombinacje są z jakichś powodów bardziej istotne od innych. Na przykład większość użytkowników testowanego systemu może używać systemu operacyjnego Windows i przeglądarki Internet Explorer. Tester może wziąć tego typu zależności pod uwagę i podczas tworzenia przypadków testowych w sposób szczególny potraktować określone kombinacje tak, by były testowane częściej od innych. W kryterium Base Choice określamy jedną kombinację charakterystyk jako podstawowy element pokrycia, a kolejne budujemy, wymieniając tylko jeden element na wszystkie pozostałe wartości. Załóżmy, że naszą podstawową konfiguracją będzie (IE, HP, Windows, MySQL). Otrzymujemy wtedy elementy pokrycia zebrane w tabeli 8.34. Tabela 8.34. Elementy pokrycia dla kryterium Base Choice

Id

Przeglądarka

Drukarka

OS

Baza danych

EP1 (bazowy)

IE

HP

W indows

MySQL

EP2

Firefox

HP

W indows

MySQL

EP3

Chrome

HP

W indows

MySQL

EP4

IE

Eps on

W indows

MySQL

EP5

IE

S ams ung

W indows

MySQL

EP6

IE

Canon

W indows

MySQL

EP7

IE

HP

Linux

MySQL

EP8

IE

HP

iOS

MySQL

EP9

IE

HP

W indows

Pos tg reS QL

Elementy należące do bloku bazowego (IE, HP, Windows i MySQL) zostały wypisane pogrubioną czcionką. Zauważmy, że są one testowane bardzo często. Każdy z pozostałych elementów wszystkich wejść występuje tylko w jednym elemencie pokrycia, np. iOS wchodzi w skład jedynie EP8, Canon – jedynie EP6 itd. Przypadki testowe odpowiadają elementom pokrycia. Każdy przypadek pokrywa jeden element pokrycia. Minimalna liczba przypadków testowych potrzebnych do spełnienia kryterium Base Choice to . Jeden przypadek to blok bazowy, a każda z pozostałych k grup to przypadki, w których wszystkie wejścia są ustawione na wartości bazowe oprócz jednego, które dla i-tej grupy może przyjąć d i – 1 możliwych wyborów (wszystkie poza wartością bazową).

8.8.4. Multiple Base Choice Kryterium Multiple Base Choice (wielu bloków bazowych) jest rozszerzeniem kryterium Base Choice. Polega ono na wyborze kilku bloków bazowych, a następnie na wymianie pojedynczych elementów w każdym z nich. Może się okazać, że podczas wymiany elementów w jednym bloku bazowym otrzymamy już istniejącą kombinację powstałą z wymiany elementów w innym bloku bazowym. W takim przypadku usuwamy z suity testowej powtarzające się przypadki. Kryterium umożliwia testerowi szczególne potraktowanie więcej niż jednej kombinacji elementów wejścia.

Załóżmy, że naszymi dwoma blokami bazowymi są bloki (IE, HP, Windows, MySQL) oraz (Firefox, HP, Linux, MySQL). W tabeli 8.35 zebrano elementy pokrycia. Tabela 8.35. Elementy pokrycia dla kryterium Multiple Base Choice

Id

Przeglądarka

Drukarka

OS

Baza danych

EP1 (bazowy)

IE

HP

W indows

MySQL

EP2 (bazowy)

Firefox

HP

Linux

MySQL

EP3

Firefox

HP

W indows

MySQL

EP4

Chrome

HP

W indows

MySQL

EP5

IE

Eps on

W indows

MySQL

EP6

IE

S ams ung

W indows

MySQL

EP7

IE

Canon

W indows

MySQL

EP8

IE

HP

Linux

MySQL

EP9

IE

HP

iOS

MySQL

EP10

IE

HP

W indows

Pos tg reS QL

EP11

IE

HP

Linux

MySQL

EP12

Chrome

HP

Linux

MySQL

EP13

Firefox

Eps on

Linux

MySQL

EP14

Firefox

S ams ung

Linux

MySQL

EP15

Firefox

Canon

Linux

MySQL

EP16

Firefox

HP

iOS

MySQL

EP17

Firefox

HP

Linux

Pos tg reS QL

EP1 i EP2 reprezentują bloki bazowe. EP3–EP10 to modyfikacje bloku EP1. EP11– EP17 to modyfikacje bloku EP2. Zauważmy, że EP3 i EP8 jest również modyfikacją bloku EP2. Przypadki testowe odpowiadają elementom pokrycia, tzn. każdy przypadek pokrywa jeden element.

8.8.5. Pair-wise testing Technika testowania par (ang. pair-wise testing) opiera się na założeniu, że najwięcej błędów jest powodowanych przez pojedynczy czynnik lub interakcję dwóch czynników [103]. Kuhn i Reilly [104] przestudiowali 171 błędów w oprogramowaniu dla serwera Apache i odkryli, że 70% z nich było spowodowanych przez nieprawidłową interakcję dwóch modułów. Istnieje wiele innych opracowań potwierdzających te wyniki. Technika testowania par pozwala na znaczną redukcję rozmiaru suity testowej. Kryterium pokrycia par wymaga, aby dla dowolnych dwóch elementów dowolnych dwóch charakterystyk istniał test zawierający tę kombinację. Każdy element pokrycia dotyczy więc kombinacji wartości dwóch charakterystyk. Dla naszego przykładu mamy następujące elementy pokrycia: Kombinacje przeglądarki i drukarki EP1: (IE, HP); EP2: (IE, Epson); EP3: (IE, Samsung); EP4: (IE, Canon); EP5: (Firefox, HP); EP6: (Firefox, Epson); EP7: (Firefox, Samsung); EP8: (Firefox, Canon); EP9: (Chrome, HP); EP10: (Chrome, Epson); EP11: (Chrome, Samsung); EP12: (Chrome, Canon). Kombinacje przeglądarki i systemu operacyjnego EP13: (IE, Windows); EP14: (IE, Linux); EP15: (IE, iOS);

EP16: (Firefox, Windows); EP17: (Firefox, Linux); EP18: (Firefox, iOS); EP19: (Chrome, Windows); EP20: (Chrome, Linux); EP21: (Chrome, iOS). Kombinacje przeglądarki i bazy danych EP22: (IE, MySQL); EP23: (IE, PostgreSQL); EP24: (Firefox, MySQL); EP25: (Firefox, PostgreSQL); EP26: (Chrome, MySQL); EP27: (Chrome, PostgreSQL). Kombinacje drukarki i systemu operacyjnego EP28: (HP, Windows); EP29: (Epson, Windows); EP30: (Samsung, Windows); EP31: (Canon, Windows); EP32: (HP, Linux); EP33: (Epson, Linux); EP34: (Samsung, Linux); EP35: (Canon, Linux); EP36: (HP, iOS); EP37: (Epson, iOS); EP38: (Samsung, iOS); EP39: (Canon, iOS). Kombinacje drukarki i bazy danych EP40: (HP, MySQL); EP41: (Epson, MySQL); EP42: (Samsung, MySQL); EP43: (Canon, MySQL); EP44: (HP, PostgreSQL);

EP45: (Epson, PostgreSQL); EP46: (Samsung, PostgreSQL); EP47: (Canon, PostgreSQL). Kombinacje systemu operacyjnego i bazy danych EP48: (Windows, MySQL); EP49: (Windows, PostgreSQL); EP50: (Linux, MySQL); EP51: (Linux, PostgreSQL); EP52: (iOS, MySQL); EP53: (iOS, PostgreSQL). Elementów pokrycia jest dosyć dużo (53), ale, jak za chwilę zobaczymy, można je będzie pokryć niewielką liczbą przypadków testowych. W ogólności elementów pokrycia dla kryterium pokrycia par jest czyli tyle, ile wynosi suma iloczynów możliwych kombinacji dla wszystkich par charakterystyk. W naszym przykładzie mamy 4 charakterystyki podzielone odpowiednio na 3, 4, 3 i 2 elementy, zatem elementów pokrycia powinno być n = 3 ⋅ 4 + 3 ⋅ 3 + 3 ⋅ 2 + 4 ⋅ 3 + 4 ⋅ 2 + 3 ⋅ 2 = 12 + 9 +6 +12 +8 +6 = 53, czyli dokładnie tyle, ile uzyskaliśmy. Po określeniu elementów pokrycia należy stworzyć przypadki, które je pokryją. Niestety problem (w wersji decyzyjnej) znalezienia minimalnego zestawu przypadków testowych spełniających kryterium pokrycia par jest NP-zupełny15. Jak zatem skonstruować sensowny (tzn. nieduży) zestaw testów dla tego pokrycia? Istnieją co najmniej trzy podejścia do problemu: 1) wypisać przypadki „ręcznie”, starając się nie generować zbyt wielu testów; 2) użyć jednego z algorytmów zachłannych; 3) wykorzystać tzw. tablice ortogonalne. Podejścia 2 i 3 są systematyczne i dobrze sformalizowane. Podejście 1 można wykorzystać przy małych problemach (np. gdy mamy do czynienia tylko z trzema charakterystykami i każda dzielona jest na dwie części). Opiszemy teraz po kolei wszystkie trzy podejścia. Podejście „ręczne”. Zauważmy najpierw, że przypadków testowych musi być co najmniej tyle, ile wynosi iloczyn kombinacji wartości dla dwóch największych charakterystyk, czyli 4 ⋅ 3 = 12. Pierwszy krok polegać będzie zatem na stworzeniu

dwunastu kombinacji charakterystyk dla drukarki i przeglądarki. Etap ten jest pokazany z lewej strony rysunku 8.28. Szare kolumny oznaczają elementy dodane w tym etapie. Każda kombinacja pokrywa jeden element pokrycia. W drugim kroku uzupełnimy tabelę o wartości charakterystyki systemu operacyjnego. Chcemy jednocześnie pokrywać pary drukarka/OS oraz przeglądarka/OS. Po dodaniu pierwszych trzech wartości: Win, Linux, iOS pokryliśmy pary (HP, Win), (HP, Linux), (HP, iOS) oraz (IE, Win), (Firefox, Linux) i (Chrome, iOS). Gdybyśmy w PT4–PT6 dodali znów wartości Win, Linux i iOS w takiej samej kolejności, pokrylibyśmy kombinacje dla drukarki Epson, ale nie pokrylibyśmy nowych kombinacji przeglądarka/OS. Dlatego dodajemy wartości Win, Linux i iOS w innej kolejności, tak, aby pokryć nie tylko trzy nowe pary drukarka/OS, lecz także trzy nowe pary przeglądarka/OS. Analogicznie postępujemy, dodając OS w przypadkach PT7–PT9. W ostatnich trzech przypadkach musimy dodać trzy różne OS (aby pokryć kombinacje drukarka/OS), ale możemy je wstawić w dowolnej kolejności, gdyż wszystkie pary przeglądarka/OS są już pokryte. W kroku trzecim uzupełniamy wartości w kolumnie dla bazy danych. Chcemy jednocześnie pokrywać pary drukarka/baza, przeglądarka/baza oraz OS/baza. Dla każdego rodzaju drukarki wpisujemy zarówno MySQL, jak i Postgres, uważając, aby przy okazji pokryć również możliwie jak najwięcej par przeglądarka/baza oraz OS/baza. Wynik tej czynności jest pokazany po prawej stronie na rysunku 8.28. Gwiazdka oznacza dowolną wartość. W pola nią oznaczone możemy wpisać dowolne wartości dla bazy danych, ponieważ wartości istniejące pokrywają już wszystkie możliwe kombinacje. Można to sprawdzić, analizując ostatnią kolumnę prawej tabeli. Występują w niej wszystkie elementy pokrycia, od 1 do 53. Zostawianie gwiazdek jest ważne, ponieważ w razie gdybyśmy musieli dodawać kolejne kolumny dla kolejnych charakterystyk, gwiazdki te ułatwią nam uzyskiwanie większej liczby kombinacji przez wybór tej wartości, która nam najbardziej w danej chwili pasuje. Dzięki temu minimalizujemy konieczność dodawania nowych wierszy w celu pokrycia jakichś kombinacji. Pogrubione liczby na rysunku 8.28 oznaczają pokrycie danego elementu po raz pierwszy. Pięćdziesiąt trzy elementy pokrycia udało się pokryć za pomocą jedynie 12 przypadków testowych. Oczywiście przy ich implementacji pola oznaczone w tabeli gwiazdkami należy zamienić na dowolne, ale ustalone wartości.

Id

Drukarka Przeg lądarka

Pokryte EP

Id

Drukarka Przeg lądarka

PT1

HP

IE

1

PT1

HP

IE

PT2

HP

Firefox

5

PT2

HP

Firefox

PT3

HP

Chrome

9

PT3

HP

Chrome

PT4

Eps on

IE

2

PT4

Eps on

IE

PT5

Eps on

Firefox

6

PT5

Eps on

Firefox

PT6

Eps on

Chrome

10

PT6

Eps on

Chrome

PT7

S ams ung

IE

3

PT7

S ams ung

IE

PT8

S ams ung

Firefox

7

PT8

S ams ung

Firefox

PT9

S ams ung

Chrome

11

PT9

S ams ung

Chrome

PT10

Canon

IE

4

PT10

Canon

IE

PT11

Canon

Firefox

8

PT11

Canon

Firefox

PT12

Canon

Chrome

12

PT12

Canon

Chrome

KROK 1. Kombinacje drukarka/przeg lądarka Id

Drukarka Przeg lądarka

KROK 2. Dodane kombinacje drukarka/OS i przeg lądarka/OS OS

Baza

Pokryte EP

Win

MyS QL

1, 13, 28, 22, 40, 48

PT1

HP

IE

PT2

HP

Firefox

PT3

HP

Chrome

PT4

Eps on

PT5

Eps on

PT6

Eps on

PT7

S ams ung

PT8

S ams ung

Firefox

iOS

MyS QL

77, 18, 38, 24, 42, 52

PT9

S ams ung

Chrome

Win

Pos tg res

11, 19, 30, 27, 46, 49

PT10

Canon

IE

Linux

*

4, 14, 35

PT11

Canon

Firefox

Win

MyS QL

8, 16, 31, 24, 43, 48

PT12

Canon

Chrome

iOS

Pos tg res

12, 21, 39, 27, 47, 53

Linux Pos tg res

5, 17 32, 25, 44, 51

iOS

*

9, 21, 36

IE

iOS

Pos tg res

2, 15, 37, 23, 45, 53

Firefox

Win

*

6, 16, 29

Chrome

Linux

MyS QL

10, 20, 33, 26, 41, 50

IE

Linux

*

3, 14, 34

KROK 3. Dodane kombinacje drukarka/baza, przeglądarka/baza i OS/baza Rysunek 8.28. Etapy ręcznego tworzenia przypadków dla kryterium pokrycia par Algorytmy zachłanne. W literaturze występuje wiele podejść algorytmicznych do znajdowania małych zestawów testów spełniających kryterium pokrycia par (zobacz np. przeglądowy artykuł [105]). Omówimy tu jedno z nich, InParameterOrder autorstwa Lei i Tai [106]. Ogólna idea ich algorytmu polega na rozpoczęciu od generacji kombinacji dwóch największych zbiorów

charakterystyk (bez utraty ogólności oznaczmy je jako D1 i D2), a następnie – w miarę potrzeb – rozszerzania tabeli wszerz (dodając nową charakterystykę) lub wzdłuż (dodając nowe przypadki testowe). Przez vi będziemy oznaczać wartości itej charakterystyki. Pseudokod dla tego generalnego podejścia jest podany na listingu 8.4.

InParameterOrder(D1, D2, …, Dk) T := {( v1, v2): v1 i v2 są wartościami charakterystyk D1 i D2 jeśli k = 2 to koniec dla każdej Di, i = 3, 4, …, k dla każdego testu (v1, …, vi–1) ∈ T //rozbudowa tablicy wszerz zastąp go przez (v1, …, vi–1, vi), gdzie vi jest wartością Di //rozbudowa tablicy wzdłuż dopóki T nie pokrywa wszystkich par między Di a każdym z D1, …, Di–1 dodaj nowy test dla D1, D2, …, Di do T koniec dopóki koniec dla koniec dla Listing 8.4. Procedura InParameterOrder dla pokrycia par Lei i Tai w swojej pracy zaproponowali kilka algorytmów opartych na tym schemacie. Pokazali również, że dla k charakterystyk, z których największa ma rozmiar d max rozmiar zestawu testowego wygenerowanego przez algorytm IPO

wyniesie . W naszym przypadku byłoby to 42 ⋅ (4 – 1) = 16 ⋅ 3 = 48. W metodzie „ręcznej” udało nam się zbudować zestaw trzy razy mniejszy. Istnieją oczywiście inne algorytmy, które mogą dać lepsze rezultaty niż IPO. Wykorzystanie tablic ortogonalnych. Tablice ortogonalne to obiekty matematyczne, którymi zajmuje się dział matematyki zwany projektowaniem eksperymentów (ang. experimental design). Są to tablice podobne do tej z tabeli 8.35, z tą różnicą, że zamiast wartości konkretnych zmiennych występują w nich liczby.

Tablice ortogonalne mają ponadto dodatkową, silniejszą właściwość, którą omówimy za chwilę. Istnieje cała rodzina tablic ortogonalnych, w zależności od liczby zmiennych oraz zakresu ich wartości, jakie mają być ujęte w tablicy. Tablica ortogonalna w szczególności spełnia właściwość pokrycia par, to znaczy dla dowolnych dwóch jej kolumn (odpowiadającym charakterystykom) i dla dowolnych dwóch wartości z odpowiadających im zakresów istnieje wiersz w tablicy ortogonalnej zawierający te wartości w zadanych kolumnach. Dlatego mając tablicę ortogonalną, możemy przekształcić wartości zmiennych poszczególnych kolumn na wartości naszych charakterystyk, otrzymując w ten sposób zbiór przypadków testowych spełniających kryterium pokrycia par. Z formalnego, matematycznego punktu widzenia, tablicę ortogonalną można zdefiniować następująco: t – (v, k, λ) – tablica ortogonalna (ang. orthogonal array), gdzie t ≤ k, to tablica o wymiarach λvt × k, której elementy należą do v-elementowego zbioru X i która ma następującą właściwość: w każdym t-elementowym podzbiorze kolumn każda t-krotka elementów z Xt pojawia się dokładnie w λ wierszach Jeśli w definicji tablicy ortogonalnej opuścimy warunek o tym, aby każda krotka pojawiała się dokładnie w λ wierszach, to takie obiekty nazywa się tablicami pokryć (ang. covering array). W internecie (np. [107]) można znaleźć przykłady minimalnych (lub najmniejszych znalezionych do tej pory) tablic pokryć dla różnych parametrów t, v, k. Definicja tablicy ortogonalnej jest zbyt ogólna dla naszych potrzeb. Po pierwsze, w testowaniu par interesują nas tylko podzbiory dwóch kolumn, zatem możemy wziąć t = 2. Po drugie, chcemy mieć jak najmniejszy zbiór testów, a więc tablicę ortogonalną o minimalnej liczbie wierszy. Dlatego przyjmiemy λ = 1, bo wystarczy, że każda kombinacja pojawi się w przynajmniej jednym przypadku testowym. Od tej pory dla kryterium pokrycia par rozważać będziemy jedynie 2 – (v, k, 1)-tablice ortogonalne. Przy tworzeniu tablicy ortogonalnej według tej definicji zakłada się, że każda zmienna przyjmuje taką samą liczbę możliwych wartości, co w przypadku testowanych systemów nie musi być prawdą. Na przykład testowany przez nas system ma jedną zmienną o 4 wartościach, dwie o 3 wartościach i jedną o 2 wartościach. Można rozszerzyć pojęcie tablicy ortogonalnej i dopuścić, aby jej kolumny reprezentowały zbiory o różnych licznościach. Jeśli jednak dla określonej

konfiguracji parametrów trudno jest nam taką tablicę stworzyć, to musimy założyć, że wszystkie zmienne przyjmują wartość równą maksymalnej charakterystyce. W naszym przypadku jest to 4. Po stworzeniu tablicy ortogonalnej będziemy mogli zamienić „nadmiarowe” liczby na arbitralne wartości z poprawnego zakresu odpowiednich zmiennych (nie wpłynie to na naruszenie kryterium pokrycia) i być może zredukować liczbę wierszy tablicy. Wróćmy zatem do naszego przykładu. Mamy 4 charakterystyki o maksymalnym rozmiarze 4. Potrzebna nam będzie zatem 2 – (4, 4, 1)-tablica ortogonalna. Jest ona pokazana na rysunku 8.29a. Istnieje cała matematyczna teoria mówiąca, jak generować tablice ortogonalne [108]. Wiele narzędzi wspomagających testowanie umożliwia generację w sposób automatyczny.

Rysunek 8.29. 2-(4, 4, 1)-tablica ortogonalna i jej przekształcenie na zbiór przypadków testowych Po pierwsze, zauważmy, że tablica z rysunku 8.29a jest rzeczywiście 2 – (4, 4, 1)tablicą ortogonalną. W każdej kolumnie występuje jedna z 4 liczb oraz każda para wartości w dowolnych dwóch kolumnach występuje dokładnie raz. Kolumny mogą teraz reprezentować nasze charakterystyki, odpowiednio: drukarkę, przeglądarkę, system operacyjny i bazę. Wprowadźmy następujące mapowanie wartości tablicy ortogonalnej na wartości charakterystyk: drukarki: HP=1, Epson=2, Samsung=3, Canon=4;

przeglądarki: IE=1, Firefox=2, Chrome=3; systemy operacyjne: Windows=1, Linux=2, iOS=3; bazy danych: MySQL=1, PostgreSQL=2. Tablica ortogonalna w każdej kolumnie wykorzystuje 4 liczby, dlatego niektóre z nich są dla nas nieistotne: wartość 4 dla przeglądarki i systemu operacyjnego oraz 3 i 4 dla baz danych. W miejsca tych liczb wpisujemy gwiazdki, które symbolizują dowolną wartość z zakresu zmiennej (rys. 8.29b). Zauważmy, że w tak powstałej tablicy możemy, wykorzystując możliwość użycia wartości dowolnych, połączyć niektóre wiersze. Na przykład, wstawiając wartości 1, 1, 1 w miejsce gwiazdek wierszu B = (1,*,*,*), otrzymamy wiersz A. Możemy zatem połączyć oba wiersze w wiersz AB = (1,1,1,1). Wstawiając 1 w C = (2,1,*,2) oraz 1 i 2 w D = (2,*,1,*), otrzymamy taki sam wiersz CD = (2,1,1,2). Wstawiając 2 i 1 w E = (4,2,*,*) oraz 2 w F = (4,*,2,1), dostajemy wiersz EF = (4,2,2,1). 16 wierszy udało nam się zredukować do 13 (rys. 8.29c). Wystarczy już tylko zastąpić liczby odpowiadającymi im wartościami charakterystyk (rys. 8.29d). Pozostałe gwiazdki możemy uzupełnić dowolnymi wartościami – nie mają one żadnego wpływu na spełnienie kryterium. Czytelnik może sprawdzić, że tak powstała suita spełnia kryterium pokrycia par. Typy konfiguracji oznacza się, stosując specjalną notację. Jeśli mamy αi zmiennych o i możliwych wartościach dla 2 ≤ i ≤ N, to taki typ zapisuje się jako Konfigurację z naszego przykładu (jedna zmienna o 4 wartościach, dwie zmienne o 3 wartościach i jedna zmienna o 2 wartościach) można zatem zapisać jako 21 32 41. W sieci można znaleźć repozytoria zawierające minimalne (lub najmniejsze do tej pory znalezione) zestawy testów dla wielu typów konfiguracji, np. [109].

8.8.6. Pokrycie n-tupletów (n-wise) Kryterium pokrycia par można uogólnić na dowolne tuplety. Na przykład pokrycie trójek (3-wise) wymaga, by każda kombinacja dowolnych wartości trzech dowolnych charakterystyk była pokryta przynajmniej przez jeden przypadek testowy. Szczególne dwa przypadki graniczne to pokrycie 1-wise, które jest dokładnie tym samym, co kryterium Each Choice, oraz pokrycie k-wise dla k równego liczbie wszystkich charakterystyk, które jest dokładnie tym samym, co pełne pokrycie kombinatoryczne omówione w kolejnym podrozdziale.

Można w łatwy sposób pokazać, że kryterium n-wise (pokrycie n-tupletów) subsumuje pokrycie k-wise (k-tupletów) dla każdego k < n. Oczywiście im większe n tym więcej przypadków testowych będzie potrzebnych, aby spełnić kryterium. Przy tworzeniu testów dla pokrycia n-tupletów można, tak jak w przypadku kryterium pokrycia par, stosować trzy wymienione wcześniej podejścia. Aby zastosować tablice ortogonalne dla kryterium n-tupletów, musimy wykorzystać n – (v, k, 1)-tablice ortogonalne, gdzie k jest liczbą charakterystyk, a v jest rozmiarem maksymalnej charakterystyki.

8.8.7. Pełne pokrycie kombinatoryczne Ostatnim z omówionych pokryć kombinatorycznych jest pełne pokrycie kombinatoryczne, które subsumuje wszystkie wymienione kryteria kombinacyjne. Wymaga ono przetestowania wszystkich kombinacji wartości wszystkich charakterystyk. Dla naszego przykładu musielibyśmy zbudować 4 ⋅ 3 ⋅ 3 ⋅ 2 = 72 przypadki testowe. Kryterium to można stosować, gdy liczba charakterystyk oraz ich możliwych wartości jest niewielka. Rozmiar wymaganej do spełnienia kryterium suity testowej rośnie bowiem wykładniczo wraz ze wzrostem liczby charakterystyk oraz liczby wartości tych charakterystyk. Kryteria kombinacyjne najczęściej wykorzystuje się przy interakcji co najwyżej dwóch czynników. Wyższe rzędy kombinacji zwykle nie pozwalają na wykrycie większej liczby błędów niż w przypadku kryterium pokrycia par. W tabeli 8.36 przedstawiono podsumowanie metod kombinacyjnych. pokrycie kombinacyjne (ang. combinatorial coverage) – odesetek pokrytych testami kombinacji warunków testowych. Warunki testowe są definiowane w zależności od typu przyjętej metody kombinacyjnej, np. Each Choice, Base Choice, Multiple Base Choice, Pair-wise Tabela 8.36. Podsumowanie metod kombinacyjnych

Warunki tes towe

Parametry z okreś lonymi wartoś ciami (jeden parametr z jedną wartoś cią odpowiada jednemu warunkowi tes towemu)

Elementy Kombinacje warunków tes towych, zależnie od metody (np. pokrycia w Each Choice elementy pokrycia s ą wpros t warunkami tes towymi) Dla każdej zidentyfikowanej kombinacji warunków Kryterium tes towych is tnieje tes t, któreg o dane wejś ciowe realizują tę pokrycia kombinację Pokrycie

p=

liczba pokrytych tes tami elementów pokrycia liczba ws zys tkich elementów pokrycia

× 100%

8.8.8. Subsumpcja kryteriów kombinacyjnych Na rysunku 8.30 przedstawiono relacje między kombinacyjnymi kryteriami pokrycia. Lewa gałąź wykresu tak naprawdę powinna przedstawiać cały łańcuch subsumpcji, ponieważ kryterium 2-wise (pokrycia par) subsumowane jest przez pokrycie 3-wise, to z kolei przez pokrycie 4-wise itd. Podobna sytuacja dotyczy gałęzi prawej: pokrycie wyborów bazowych jest subsumowane przez kryterium pokrycia wyboru dla dwóch warunków bazowych, to z kolei – przez kryterium wyboru dla trzech warunków bazowych itd.

Rysunek 8.30. Hierarchia subsumpcji dla kombinacyjnych kryteriów pokrycia

8.9. Testowanie dziedziny

8.9.1. Opis metody Testowanie dziedziny jest uogólnieniem technik podziału na klasy równoważności oraz

wartości

granicznych.

Podejście

to

stosuje

się

wtedy,

gdy

klasy

równoważności dziedziny wejściowej są wielowymiarowe, tzn. gdy granica klasy równoważności zależy od kombinacji więcej niż jednej zmiennej. Gdy zmiennych jest n, mówimy o n-wymiarowej dziedzinie wejściowej. Zanim przejdziemy do dokładnego omówienia metody, wprowadzimy niezbędne pojęcia z zakresu geometrii euklidesowej i algebry liniowej, aby dobrze zrozumieć ideę stojącą za kryterium pokrycia punktami ON i OFF omówionym w punkcie 8.9.6. Rozważymy najpierw

przypadek

ograniczeń

liniowych,

ponieważ



one

najczęściej

spotykanym typem ograniczeń. Na zakończenie przedyskutujemy przypadek nieliniowy. analiza dziedziny (ang. domain analysis) – czarnoskrzynkowa technika projektowania testów używana do identyfikacji skutecznych i efektywnych przypadków testowych, kiedy wiele zmiennych może lub powinno być testowanych razem; bazuje na metodach: podziału na klasy równoważności oraz analizie wartości brzegowej i uogólnia je do przypadku wielowymiarowego

8.9.2. Hiperpłaszczyzny i podprzestrzenie Równania i nierówności zawierające wiele zmiennych wyznaczają różnego rodzaju obiekty geometryczne. Warunek (liniowy) postaci a 1 x1 + a 2 x2 + ··· + a n xn = b, gdzie a i oraz b są ustalonymi liczbami, wyznacza w n-wymiarowej przestrzeni

tzw. hiperpłaszczyznę, czyli podzbiór wyjściowej przestrzeni o wymiarze n – 1. Na przykład na dwuwymiarowej płaszczyźnie równanie a 1 x1 + a 2 x2 = b wyznacza prostą (czarna prosta na rys. 8.31 z lewej strony), czyli obiekt jednowymiarowy. W trójwymiarowej przestrzeni warunek a 1 x1 + a 2 x2 + a 3 x3 = b wyznacza dwuwymiarową płaszczyznę (na rys. 8.31 z prawej oznaczona szarym kolorem), czyli obiekt dwuwymiarowy.

Rysunek 8.31. Hiperpłaszczyzny i podprzestrzenie w przestrzeni dwui trójwymiarowej Gdybyśmy zamiast równości wzięli nierówność, to takie warunki dzielą przestrzeń wyjściową na dwie podprzestrzenie o tym samym wymiarze. Na przykład nierówność a 1 x1 + a 2 x2 ≥ b dzieli dwuwymiarową przestrzeń na dwie części, leżące po obu stronach prostej a 1 x1 + a 2 x2 = b. Na rysunku 8.31 jedna z nich

oznaczona jest kolorem szarym. Nierówność a 1 x1 + a 2 x2 + a 3 x3 ≥ b dzieli trójwymiarową przestrzeń na dwie podprzestrzenie, leżące po różnych stronach płaszczyzny a 1 x1 + a 2 x2 + a 3 x3 = b. Metoda punktów ON-OFF którą opiszemy dalej dotyczy – tak jak analiza wartości brzegowych – punktów brzegowych. Musimy zatem dobrze zdefiniować pojęcie brzegu obszaru. Dla dziedzin wyznaczanych nierównościami nieostrymi

sprawa jest prosta: brzegiem obszaru wyznaczonego warunkiem a 1 x1 + ··· + a n xn ≥ b jest zbiór B punktów spełniających warunek Jeśli jednak zamienimy nierówność na ostrą, to zbiór B wyznacza brzeg przestrzeni dopełniającej. Na przykład, dla warunku x1 – x2 > 0 zbiór punktów spełniających go jest oznaczony kolorem szarym na rysunku 8.32. Brzeg, czyli zbiór punktów spełniających warunek x1 = x2, nie należy jednak do tego obszaru, ale do obszaru dopełniającego, czyli zbioru punktów spełniających warunek x1 – x2 ≤ 0. Konwencja, którą zwykle stosuje się przy oznaczaniu brzegów jest następująca: jeśli obszar jest „domknięty” i zbiór B do niego należy, to oznacza się go linią ciągłą. Jeśli obszar jest „otwarty” i B do niego nie należy, to oznacza się go linią przerywaną, tak jak na rysunku 8.32. W przestrzeni co najmniej dwuwymiarowej brzeg nie jest już pojedynczą liczbą, lecz jakąś podprzestrzenią przestrzeni wyjściowej. Powstaje następujący problem:

jeśli obszar O jest „otwarty”, to które punkty są jego własnym brzegiem, skoro B jest brzegiem przestrzeni dopełniającej? Intuicyjnie, będą to punkty należące do O i leżące możliwie blisko punktów z B. Jak jednak zdefiniować pojęcie bliskości w więcej niż jednym wymiarze, w dodatku w sytuacji, gdy przestrzeń reprezentowana w komputerze jest dyskretna (najmniejsza zmiana współrzędnej punktu to epsilon maszynowy)? Można tu oczywiście wykorzystać pojęcia z zakresu geometrii analitycznej, wprowadzać formalnie metryki na przestrzeniach wielowymiarowych itd., ale byłoby to zbyt skomplikowane. Tester powinien znajdować możliwie najprostsze działające i rozsądne rozwiązania. Jeśli precyzja reprezentacji liczb nie jest jakoś specjalnie istotna przy testowaniu danego ograniczenia, to można za brzeg obszaru wyznaczonego warunkiem ∑ i a i xi > b przyjąć zbiór punktów spełniających równanie ∑ i a i xi = b + ε, gdzie ε jest odpowiednio małą liczbą, wyznaczającą poziom precyzji, poniżej którego tester nie musi schodzić. Zakładamy wtedy, że każdy punkt spełniający ten warunek leży na brzegu rozważanego obszaru.

Rysunek 8.32. Obszar z brzegiem „otwartym” Jeśli precyzja reprezentacji jest istotna, to za brzeg możemy przyjąć wszystkie takie punkty (x1, x2, … xn) spełniające a 1 x1 + … a n xn > b, że zmiana któregokolwiek z nich powoduje, że nowy punkt już do obszaru O nie należy.

Formalnie, brzegiem będzie taki zbiór punktów (x1, x2, … xn), że dla każdego i ∈ {1, …, n} zachodzi jedno z dwóch poniższych równań: a 1 x1 + … + a i–1 xi–1 + a i (xi + ε) + a i+1 xi+1 + … + a nxn ≤ b lub a 1 x1 + … + a i–1 xi–1 + a i (xi – ε) + a i+1 xi+1 + … + a nxn ≤ b gdzie ε jest epsilonem maszynowym, czyli dokładnością maszynową reprezentacji liczb w danym systemie.

8.9.3. Wyznaczanie hiperpłaszczyzny przez punkty Ile punktów potrzeba, by wyznaczyć prostą w przestrzeni dwuwymiarowej? Odpowiedź jest oczywista: wystarczą dwa punkty. Reguła ta działa także dla wyższych wymiarów: aby w przestrzeni n-wymiarowej wyznaczyć hiperpłaszczyznę, czyli podprzestrzeń o wymiarze n – 1, wystarczy n punktów, które nie leżą w podprzestrzeni o wymiarze n – 2. Czyli np. płaszczyznę w przestrzeni 3-wymiarowej można jednoznacznie wyznaczyć za pomocą 3 punktów nieleżących na jednej prostej (to dlatego najbardziej stabilnymi krzesłami są te o trzech, a nie czterech nogach!). Rozważania te przydadzą się w zrozumieniu idei metody punktów ON-OFF. Dzięki tej obserwacji będziemy w stanie – przy sensownych założeniach co do testowanego warunku – wykazać, że można przetestować poprawność tego warunku przy odpowiednio niewielkiej liczbie przypadków testowych.

8.9.4. Punkty IN, OUT, ON i OFF Wprowadzimy teraz cztery kategorie punktów względem rozważanego obszaru O. Punkty te będą wykorzystane w technikach IN-OUT oraz ON-OFF. Posłużymy się przykładem z rysunku 8.33. Załóżmy, że naszym obszarem jest szary trójkąt „bez jednego brzegu”, wyznaczony warunkami:

(1) –4/5a + b < –2/5 (2) a ≤ 3 (3) b ≥ –2

Rysunek 8.33. Przykłady punktów IN, OUT, ON i OFF dla testowanego obszaru Definiujemy następujące kategorie punktów: punkt ON – to punkt leżący na brzegu rozważanego obszaru O; punkt OFF – to punkt leżący na brzegu obszaru sąsiadującego z O; punkt IN – to punkt leżący wewnątrz O i nieleżący na brzegu O; punkt OUT – to punkt leżący poza O i nieleżący na brzegu obszaru sąsiadującego z O. Brzeg wyznaczony warunkiem –4/5a + b = –2/5, nie należy do trójkąta, zatem punkt leżący na tej prostej jest punktem OFF. Punkt (3, –2) leżący w prawym dolnym rogu jest częścią wspólną dwóch brzegów trójkąta, zatem jest punktem ON. Punkt (3, 2) w prawym górnym rogu jest punktem granicznym, ale nie leży na

brzegu trójkąta, ponieważ nie spełnia warunku (1) – jest zatem punktem OFF. Linia ciągła tuż poniżej prostej –4/5a + b = –2/5 oznacza brzeg trójkąta. Punkt na niej leżący jest punktem ON. Punkt w środku trójkąta nie należy do jego brzegu, jest więc punktem IN. Punkt w lewej górnej ćwiartce układu leży poza trójkątem, ale nie na brzegu obszaru z nim graniczącym – jest zatem punktem OUT. Punkty IN oraz OUT posłużą nam do konstrukcji techniki uogólniającej metodę podziału na klasy równoważności na przypadek wielowymiarowy. Punkty ON i OFF – do konstrukcji techniki uogólniającej metodę analizy wartości brzegowych. Warunki opisujące ograniczenia stają się warunkami testowymi. Punkty IN/OUT (odpowiednio ON/OFF) stają się elementami pokrycia dla testowania dziedziny oraz wartości brzegowych w przestrzeniach wielowymiarowych.

8.9.5. Strategia IN-OUT dla testowania dziedziny Strategia testowania dziedziny jest bardzo prosta. Dla każdego z ograniczeń definiujemy jeden punkt IN oraz jeden punkt OUT. Następnie, dla każdego punktu tworzymy przypadek testowy. Ten sam punkt może być wykorzystany dla dwóch lub więcej ograniczeń.

8.9.6. Strategia N-ON × M-OFF dla testowania wartości brzegowych Strategia testowania wartości granicznych dziedziny polega na przetestowaniu każdego z ograniczeń osobno przez wykorzystanie N punktów ON i M punktów OFF dla każdego ograniczenia. Rozmiar suity testowej będzie zależał więc od wyboru N oraz M. Dla K ograniczeń liczba przypadków testowych będzie wynosić K(N + M). Najprostsza strategia to 1 × 1 – każde ograniczenie testujemy za pomocą jednego punktu ON i jednego punktu OFF. Możemy również wykorzystać tylko same punkty ON. Zastanówmy się, jak powinna wyglądać optymalna suita testowa, aby z jednej strony nie generować zbyt dużej liczby przypadków, a z drugiej – aby dobrze przetestować warunki.

Rysunek 8.34. Zdolność punktów ON-OFF do wykrywania błędów dziedziny Na rysunku 8.34 jest pokazana dziedzina wyznaczona równaniem x1 – x2 ≥ 0. Jej brzeg jest oznaczony czarną linią. Załóżmy, że pomyłka programisty polega na zaimplementowaniu błędnego warunku, którego brzeg – pokazany linią kropkowaną – lekko odbiega od poprawnego. Strategia 1 × 1 nie zawsze doprowadzi do wykrycia błędu – wybierając punkty ON i OFF tak jak na rysunku 8.34a, nie wykryjemy naruszenia warunku. Zarówno w przypadku poprawnego, jak i błędnie zaimplementowanego ograniczenia punkt 1 (ON) zawsze leży w szarym obszarze, a punkt 2 (OFF) zawsze leży poza nim. Gdyby jednak punkt 1 leżał blisko punktu 2, wtedy wykrylibyśmy błąd, bo byłby on punktem granicznym poprawnego brzegu, ale leżałby poza obszarem wyznaczonym brzegiem błędnym. Taka sytuacja jest pokazana na rysunku 8.34b. Zauważmy, że pozycja punktu ON nie ma większego znaczenia, jeśli tylko punkt OFF jest blisko niego. Błąd zostanie wykryty w przypadku użycia zarówno pary punktów 3, 4, jak i 5, 6. Jedyna problematyczna sytuacja powstałaby, gdyby punkt ON leżał blisko przecięcia się obu brzegów. Wtedy punkt OFF dla poprawnej dziedziny mógłby również być punktem OFF dla niepoprawnej. Jednak prawdopodobieństwo, że trafimy punktem ON blisko punktu przecięcia, jest znikome. Zakładając, że błędne ograniczenie jest zaimplementowane jako warunek liniowy, można wręcz udowodnić, że podejście 1 × 1 z dużym prawdopodobieństwem wykryje błąd, jeśli tylko punkty ON i OFF będą leżały blisko siebie. Ile punktów potrzeba w przypadku przestrzeni o większym wymiarze, n > 2? Z pomocą przychodzą nam obserwacje poczynione w punkcie 8.9.3. Brzeg zwykle jest hiperpłaszczyzną, a ta jest wyznaczana przez n punktów. Jeśli zatem ustawimy n punktów ON, to powinny one dokładnie wyznaczyć brzeg.

Mogą one wszystkie również należeć do obszaru wyznaczonego przez błędnie zaimplementowany warunek. Ale jeśli tylko punkty ON będą leżeć blisko siebie i przesuniemy jeden z nich o niewielką wartość tak, aby stał się punktem OFF, to przyjmując strategię (n – 1) × 1, będziemy w stanie wykryć błąd dziedziny, w analogiczny sposób, jak to opisaliśmy w przypadku dwuwymiarowym. Jeśli chcemy mieć swobodę w wyborze punktów ON i OFF (np. z jakichś powodów nie chcemy ich definiować blisko siebie), to przedstawiona strategia nie zawsze się sprawdzi (patrz problem z rys. 8.34a). Jeżeli brzeg jest n – 2-wymiarową hiperpłaszczyzną w przestrzeni n-wymiarowej, to można przyjąć strategię n × 1, tzn. wybrać n punktów ON leżących na brzegu tak, aby wyznaczały go jednoznacznie16, oraz jeden punkt OFF. Taka sytuacja jest pokazana na rysunku 8.43c. Ponieważ n = 2, wybieramy w dowolny sposób 2 punkty ON tak, aby jednoznacznie definiowały brzeg. Następnie dodajemy, znów w dowolny sposób, 1 punkt OFF. Jeśli błędna implementacja warunku ma postać liniową, to strategia n × 1 zawsze wykryje błąd. Jeśli testowany warunek ma postać równości, to sprawa jest jeszcze prostsza – do przetestowania warunku k-wymiarowego w przestrzeni n-wymiarowej, n > k wystarczy k punktów ON, rozmieszczonych w sposób dowolny na brzegu określonym testowaną równością. Wyznaczają one bowiem jednoznacznie hiperpłaszczyznę w przestrzeni k + 1-wymiarowej. Każda inna hiperpłaszczyzna, w szczególności błędnie zaimplementowana – o ile została zaimplementowana przy użyciu operatora równości – nie będzie zawierać wszystkich k punktów ON. Programista mógłby jednak omyłkowo zastosować w warunku operator relacyjny zamiast równości. Wtedy same punkty ON mogą nie wystarczyć, bo kwymiarowy brzeg może być po prostu zawarty w błędnie zdefiniowanej podprzestrzeni k + 1-wymiarowej. W takiej sytuacji można ponownie zastosować strategię (k – 1) × 1, czyli wziąć k punktów ON leżących blisko siebie i przesunąć minimalnie jeden z nich tak, aby stał się punktem OFF.

Rysunek 8.35. Redukcja liczby punktów ON i OFF dla kombinacji ograniczeń Jeżeli mamy do czynienia z większą liczbą ograniczeń, to możemy zredukować liczbę punktów ON i OFF, a zatem i liczbę testów, w następujący sposób. Jeśli dwa ograniczenia mają punkt wspólny jak na rysunku 8.35, to tworzymy punkt w miejscu ich przecięcia, który jest ON dla obu warunków. W jego pobliżu drugi punkt tak, by był punktem OFF dla obu warunków. Taka metoda pozwala nam zredukować liczbę testów z czterech (po dwa punkty na każde ograniczenie) do dwóch. pokrycie dziedziny (ang. domain coverage) – odsetek pokrytych testami punktów IN/OUT lub ON/OFF, zdefiniowanych w zależności od typu metody. Tabela 8.37. Podsumowanie metod analizy dziedziny

W arunki testowe

Podzbiory dziedziny (dla strategii IN- OUT ) lub ograniczenia (dla strategii ON- OFF)

Elementy pokrycia

Punkty IN i OUT (dla s trateg ii IN-OUT) lub punkty ON i OFF (dla s trateg ii ON-OFF)

Kryterium pokrycia

Dla każdeg o punktu IN i OUT (odpowiednio ON i OFF) is tnieje tes t wykorzys tujący tę wartoś ć

Pokrycie

liczba wykorzys tanych w tes tach punktów p IN/OUT (lub ON/OFF) × = 100% liczba ws zys tkich punktów IN/OUT (lub ON/OFF)

W tabeli 8.37 podsumowano metodę analizy dziedziny. Podczas liczenia pokrycia nie powinno się wliczać ani do licznika, ani do mianownika wzoru zduplikowanych punktów, które są wykorzystywane dla więcej niż jednego ograniczenia.

8.9.7. Ograniczenia nieliniowe Ograniczenia nieliniowe to takie, których nie da się wyrazić jako kombinacja liniowa poszczególnych zmiennych. Ograniczenia te łatwo poznać po tym, że wyznaczają hiperpłaszczyzny lub podprzestrzenie, które nie są „liniowe”. Na przykład równanie x2 + y2 = 16 wyznacza granicę w kształcie okręgu o promieniu 4. Dla ograniczeń nieliniowych nie ma uniwersalnej teorii, która mówiłaby, ile co najmniej punktów ON i OFF należy użyć w testowaniu. Sprawa jest prostsza, jeśli zawęzimy nasze rozważania do pewnych klas ograniczeń. Jeśli na przykład wiemy, że ograniczenia są implementowane jako równania okręgów, to wystarczą trzy punkty ON, aby zweryfikować, czy ograniczenie rzeczywiście jest poprawnym okręgiem (bo 3 punkty jednoznacznie wyznaczają okrąg). Jeśli używanym ograniczeniem jest wielomian stopnia n, to wystarczy n + 1 punktów ON, gdyż z algebry wiadomo, że jest on jednoznacznie identyfikowany przez n + 1 punktów. Generalnie im „większa nieliniowość”, tym więcej powinniśmy użyć punktów ON i OFF. Oczywiście, tak jak w przypadku innych technik, na decyzję o wyborze strategii wpływają poziom przyjętego i akceptowanego ryzyka, czas, zasoby itd. Nie ma tu jednego, uniwersalnego rozwiązania.

8.9.8. Przykład

Rozporządzenie ministra gospodarki z dnia 21 grudnia 2005 roku w sprawie zasadniczych wymagań dla urządzeń ciśnieniowych i zespołów urządzeń ciśnieniowych opisuje kategorię, jaką należy przypisać zbiornikowi na podstawie dwóch parametrów: objętości V (w litrach) oraz ciśnienia PS (w barach). Kategoria jest przyznawana na podstawie relacji, jakie zachodzą między parametrami. Są one przedstawione w tabeli 8.38. Tabela 8.38. Kategoryzacja urządzeń ciśnieniowych w zależności od dopuszczalnego ciśnienia i pojemności

Kategoria Zakres parametrów wg §11

0.5 bar < PS ≤ 200 bar ∧ V ≤ 1 l lub PS > 0.5 bar ∧ V > 1.0 l ∧ PS ⋅ V ≤ 25 bar ⋅ l

I

PS > 0.5 bar ∧ V > 1.0 l ∧ 25 bar ⋅ l< PS ⋅ V ≤ 50 bar ⋅ l

II

PS > 0.5 bar ∧ V > 1.0 l ∧ 50 bar ⋅ l< PS ⋅ V ≤ 200 bar ⋅ l

III

PS > 0.5 bar ∧ V > 1.0 l ∧ 200 bar ⋅ l< PS ⋅ V ≤ 1000 bar ⋅ l lub 200 bar< PS ≤ 1000 bar ∧ V ≤ 1.0 l

IV

0.5 bar< PS ≤ 1000 bar ∧ PS ⋅ V > 1000 bar ⋅ l lub PS > 1000 bar

Graficzna reprezentacja tych zależności jest pokazana na rysunku 8.36 z lewej strony. Brzegi należą do regionów leżących pod nimi lub po ich lewej stronie. Chcemy przetestować program Kategoryzacja, który przyznaje kategorię na podstawie dwóch parametrów wejściowych: V oraz PS. Ze względu na specyfikę problemu, zastosowanie analizy dziedziny jest niewystarczające – musimy przetestować również warunki brzegowe. Dziedzina jest dwuwymiarowa, a ograniczenia mają postać liniową, dlatego decydujemy się na strategię 1 × 1 oraz definiowanie punktów ON i OFF blisko siebie. Krok 1. Określenie przedmiotu i elementów testów. Mamy tylko jeden element testów – program Kategoryzacja.

ET1: Kategoryzacja.

Rysunek 8.36. Graficzna reprezentacja warunków dla kategoryzacji urządzeń ciśnieniowych Krok 2. Wyprowadzenie warunków testowych. Mamy do przetestowania pięć obszarów (§11, I, II, III, IV), z których każdy jest zdefiniowany przez kilka warunków ograniczających. Gdybyśmy mieli pewność, że każdy warunek zaimplementowany jest w programie w jednym miejscu, to wystarczyłoby sprawdzić 8 ograniczeń zebranych w tabeli 8.38. Nie mamy jednak takiej pewności. Dlatego dla każdego obszaru i każdego ograniczenia musimy przeprowadzić oddzielną analizę. Mamy 13 takich ograniczeń wyznaczających brzegi poszczególnych obszarów. Każde z nich jest oznaczone czarną kropką na rysunku 8.36 z prawej strony. Będziemy się do nich odwoływać przez stosowanie notacji WT1, WT2, …, WT13. Krok 3. Wyprowadzenie elementów pokrycia. Dla każdego warunku testowego musimy stworzyć dwa elementy pokrycia, odpowiadające granicom sąsiadujących ze sobą obszarów. Każdy element pokrycia będzie się składać z jednego punktu ON i jednego OFF. Wydaje się więc, że dla jednego warunku musimy stworzyć 4 punkty – po 2 dla każdej z sąsiadujących ze sobą granic. Ale właśnie ze względu na sąsiedztwo obszarów, wykorzystamy punkt ON jednego z nich jako punkt OFF obszaru sąsiadującego. W ten sposób zredukujemy liczbę

potrzebnych punktów z 52 do 26. Na rysunku 8.36 z prawej strony każda kropka odpowiada dwóm punktom. Jeden z nich zawsze leży na odpowiedniej granicy, drugi – tuż obok – należy do brzegu obszaru sąsiadującego z tą granicą. Jest 13 warunków testowych, mamy więc 26 punktów. Będziemy się do nich odwoływać, używając numeru warunku oraz litery A lub B. Na przykład para punktów odpowiadających warunkowi WT6 to 6A i 6B. Literą A oznaczymy punkty leżące na granicach pokazanych na rysunku. Literą B – punkty leżące na granicach sąsiadujących obszarów. Zauważmy, że dla przetestowania granicy V = 1 obszaru §11 wystarczy nam tylko jedna spośród par punktów odpowiadających WT3 i WT4. Oba te wymagania są nam jednak potrzebne – jedno dla przetestowania jednej z granic obszaru I, a drugie – obszaru II. Mamy ostatecznie następujących 26 elementów pokrycia EP1A, EP1B, EP2A, EP2B, …, EP13A, EP13B. Należy pamiętać, że testowane warunki mają nałożone ograniczenie na wartości zmiennych. Na przykład warunek nr 8 możemy sprawdzać dla 1 ≤ V ≤ 100 i 0.5 ≤ PS ≤ 50. Krok 4. Zdefiniowanie przypadków testowych. Przyjmijmy

akceptowaną

dokładność pomiaru jako ε = 0.1, zarówno w stosunku do ciśnienia, jak i pojemności. Tabela 8.39 w poszczególnych kolumnach zawiera przypadki testowe. Dla każdego z nich są podane wartości PS oraz V, a także informacja o tym, który punkt jest ON, a który OFF dla poszczególnych ograniczeń. Ze względu na oszczędność miejsca pominięto niektóre wiersze i kolumny. Zauważmy, że każde ograniczenie zostało pokryte punktem ON i punktem OFF. Podczas analizy tego przykładu tester powinien zwrócić również uwagę na brak jakichkolwiek informacji o przypadkach, w których V < 0.1 l, mimo wyraźnego oznaczenia tego faktu w specyfikacji na wykresie. Prawdopodobnie zbiorniki o pojemności mniejszej niż 1/10 litra są zbyt małe na to, aby podlegały kategoryzacji. Niemniej jednak tester powinien się co do tego upewnić i potwierdzić swoje domysły. Dodatkowo, tester może również skupić się na przetestowaniu punktów leżących na przecięciu dwóch lub większej liczby prostych opisujących warunki liniowe. Powoduje to powstanie dodatkowych warunków testowych, WT14–WT24, odpowiadających punktom 14–24 przedstawionym na rysunku 8.37. Dla każdego

z nich można stworzyć jeden przypadek testowy, odpowiadający dokładnie współrzędnym danego punktu. Tabela 8.39. Przypadki testowe dla programu Kategoryzacja

PT 1 PT 2

... PT 15 PT 16 ... PT 25

PT 26

V

0.5

... 10

PS

1000 1000.1 ... 5

1A PS ≤ 1000

ON

1B PS > 1000

OFF ON

0.5

OFF

10.1

... 10000

10000

5

... 0.5

0.6

...

...

...

...

... 8A PS · V ≤ 50

... ON

OFF

...

8B PS · V > 50

... OFF

ON

...

... 13A PS ≤ 0.5

...

... ON

OFF

13B PS > 0.5

...

... OFF

ON

oczekiwane wyjś cie III

IV

... I

II

... niezdef. IV

Rysunek 8.37. Warunki testowe na połączeniach linii

8.10. Testowanie oparte na przypadkach użycia 8.10.1. Opis metody Testerzy lubią pracować z przypadkami użycia, ponieważ są one gotowym opisem testów funkcjonalnych. W najprostszym przypadku tester otrzymuje przypadek użycia i po prostu go przeprowadza, sprawdzając, czy wszystko odbywa się według założonego w przypadku scenariusza. Często rola testerów nie

sprowadza się jedynie do funkcji biernego odbiorcy gotowego przypadku użycia – tester może (i powinien) pracować nad jego powstawaniem, aby już na etapie prac projektowych wykrywać usterki, nieścisłości oraz złą formę utrudniającą testowalność przypadku użycia. przypadek użycia (ang. use-case) – ciąg transakcji w dialogu między uczestnikami (użytkownikami bądź systemami) skutkujących osiągnięciem założonego celu wszystkich uczestników Przypadek użycia określa umowę między uczestnikami systemu względem jego zachowania [110]. Każdy uczestnik ma cel, jaki chce osiągnąć w danym przypadku użycia. Uczestnicy są nazywani aktorami. Aktor główny to ten uczestnik, z którego perspektywy jest opisywany przypadek użycia. Cel aktora głównego jest nazywany celem głównym. Najczęściej przypadki użycia przedstawia się w formie tekstowej, choć można używać innych form graficznych, takich jak diagramy czy sieci Petriego. Hipoteza błędu w tej metodzie mówi, że błędy powstają na skutek nieprawidłowej implementacji kodu obsługującego sekwencje interakcji między aktorami. aktor (ang. actor) – użytkownik, inna osoba lub system wchodzący w określony sposób w interakcję z testowanym systemem Przypadek użycia musi określać tzw. scenariusz główny (czyli typowy ciąg akcji, prowadzący do szczęśliwego zakończenia interakcji między aktorami). Mogą również występować tzw. scenariusze alternatywne, opisujące interakcję w przypadku wystąpienia jakichś problemów. Poprawnie napisany przypadek użycia, to taki, w którym: występują jasno zdefiniowani aktorzy (np. użytkownik i system); aktorzy mają dobrze sprecyzowane cele; uwzględniono fakt, że cele mogą się nie powieść; występuje nie więcej niż kilkanaście kroków głównego scenariusza; podano minimalną gwarancję, tzn. najmniejsze obietnice, które system składa uczestnikom (istotne zwłaszcza w przypadku niepowodzenia celu aktora głównego).

Wszystkie te cechy przypadku użycia mogą podlegać testowaniu, przyjmującemu najczęściej formę testowania statycznego – przeglądu lub inspekcji. Testowanie samego przypadku użycia polega na przetestowaniu scenariusza głównego oraz wszystkich scenariuszy alternatywnych. Zarówno warunkami pokrycia, jak i elementami pokrycia są scenariusze (główny oraz alternatywne). Każdy przypadek testowy powinien pokrywać jeden scenariusz. Podczas przeprowadzania testów można również mierzyć pokrycie kroków scenariuszy.

8.10.2. Przykład Rozważmy bankomat, którego jedną z funkcji jest tzw. szybka wypłata pieniędzy (50 PLN) przez użytkownika. Polega ona na tym, że klient po autoryzacji w systemie może wybrać opcję szybkiej wypłaty, nie przechodząc przez ekran wpisywania kwoty oraz pytania o wydrukowanie potwierdzenia transakcji. Przypadek użycia SzybkaWypłata jest opisany w tabeli 8.40. Zauważmy, że każdy krok każdego scenariusza jest identyfikowany inną liczbą. Pozwala to na ścisłe odwoływanie się do poszczególnych kroków. Gdybyśmy kroki scenariuszy alternatywnych numerowali tak, jak kroki scenariusza głównego (1, 2, 3 …), wprowadzilibyśmy niejednoznaczność, a co za tym idzie – zamieszanie. Przypadek nie opisuje scenariusza alternatywnego dla kroku 6 (wypłata innego rodzaju), ponieważ skupimy się na funkcjonalności szybkiej wypłaty. Wszystkie rozszerzenia przypadku użycia są opisane w końcowej sekcji z dodaną informacją, który przypadek użycia omawia dany wariant. Zanim tester przeprowadzi testowanie na podstawie dostarczonego przypadku użycia, może dokonać przeglądu przypadku i zwrócić uwagę na kilka kwestii, na przykład: nie uwzględniono, że klient może włożyć kartę do bankomatu w chwili, gdy nie wyświetla on ekranu powitalnego, tylko jakiś inny ekran; między krokami 8.2 a 8.3 nie ma żadnej informacji dla klienta; upewnić się, czy po nieodebraniu gotówki stan konta się nie zmienia (tzn. czy spełniona jest minimalna gwarancja) – być może powinno to być napisane wyraźnie w przypadku użycia; Tabela 8.40. Przypadek użycia dla szybkiej wypłaty z bankomatu

Przypadek użycia

PU004 Sz ybkaWypłata

Cel

Umożliwienie s zybkiej wypłaty 50 PLN z bankomatu

Aktorzy

klient, bankomat

W yzwalacz

Włożenie karty do bankomatu

W arunek wstępny

Pos iadanie karty bankomatowej

Minimalna gwarancja

Zapis anie ws zys tkich akcji w dzienniku (log u). W przypadku nieudanej trans akcji s tan konta s ię nie zmieni.

Nazwa scenariusza

S cenarius z g łówny – s zybka wypłata

Krok Akcja 1

Klient wkłada kartę do bankomatu

2

S ys tem identyfikuje klienta

3

S ys tem pros i o podanie numeru PIN

4

Użytkownik wprowadza poprawny PIN

5

S ys tem wyś wietla ekran opcji

6

Użytkownik wybiera opcję „ S zybka wypłata 50 PLN”

7

S ys tem s prawdza s tan konta, wypłaca 50 PLN, uaktualnia s tan konta i wyś wietla komunikat „ pros zę odebrać g otówkę”

8

Użytkownik odbiera g otówkę

S cenarius z alternatywny – zła karta

9

S ys tem zwraca kartę i wyś wietla komunikat „ pros zę odebrać kartę”

10

Użytkownik odbiera kartę

11

S ys tem zamyka trans akcję i wyś wietla ekran powitalny s przed kroku 1

2.1

S ys tem rozpoznaje błędną kartę i wyś wietla komunikat „ zła karta”

2.2

S ys tem zwraca kartę klientowi i wyś wietla ekran powitalny

4.1.1 Użytkownik wprowadza niepoprawny PIN S cenarius z alternatywny – zły PIN

S cenarius z alternatywny – brak akcji klienta

4.1.2

S ys tem wyś wietla komunikat o złym numerze PIN

Jeś li użytkownik wpis ał błędny PIN po raz 3, s ys tem zwraca kartę, zamyka trans akcję 4.1.3 i wyś wietla ekran powitalny; w przeciwnym razie powrót do kroku 3 4.2.1

Użytkownik nie podejmuje akcji przez 30 s ekund

4.2.2

S ys tem zatrzymuje kartę i wyś wietla s tos owny komunikat

4.2.3

S ys tem zamyka trans akcję i wyś wietla ekran powitalny

Przypadek użycia

PU004 Sz ybkaWypłata 7.1

S ys tem neg atywnie weryfikuje iloś ć ś rodków na koncie

S cenarius z alternatywny – brak ś rodków na koncie

S cenarius z alternatywny – g otówka nieodebrana S cenarius z alternatywny – klient nie odbiera karty Rozszerzenia

7.2

S ys tem wyś wietla komunikat o braku ś rodków i zwraca kartę

7.3

Klient odbiera kartę

7.4

S ys tem zamyka trans akcję i wyś wietla ekran powitalny

8.1

Użytkownik nie podejmuje akcji przez 20 s ekund

8.2

S ys tem zatrzymuje g otówkę i zg łas za incydent nieodebrania g otówki do banku

8.3

S ys tem wyś wietla ekran powitalny

10.1

Przypadek jak w s cenarius zu 4.2.

6′ Klient wybiera wypłatę inneg o rodzaju (PU005) 7′ Utrata połączenia z bankiem (PU011)

czy krok 7 nie jest zbyt obszerny? Może warto podzielić go na cztery kroki: weryfikacja środków na koncie, wypłata, uaktualnienie stanu konta oraz wyświetlenie komunikatu? Pozostawienie go w obecnej formie może utrudnić testowanie; co się stanie, jeśli zajdzie nieprzewidziane zdarzenie między wypłatą a uaktualnieniem stanu konta (np. utrata łączności z bankiem, brak prądu)? czy nie powinno się zdefiniować podprzypadku dla kroku 7.3 dotyczącego braku akcji klienta (nieodebranie karty)? Analogiczne

podprzypadki są opisane w scenariuszach alternatywnych dla kroków 4 (scenariusz 4.2), 8 i 10; bankomat potrzebuje komunikować się z bankiem, jednak w przypadku użycia taki aktor nie występuje. Tego typu uwagi powinny być tak naprawdę poczynione wcześniej, na etapie projektowania przypadku użycia. Jeśli jednak tester widzi niejednoznaczności, niekonsekwencje, nie rozumie sformułowań użytych w treści przypadku lub po prostu czuje, że w jakimś miejscu scenariusza mogą pojawić się problemy nieopisane w przypadku, to zawsze powinien zgłaszać swoje wątpliwości, czy to formalnie (jako incydent), czy też nieformalnie (przez rozmowę z projektantem lub analitykiem). Jak widać, nawet w dosyć prostym i – wydawałoby się – dobrze skonstruowanym przypadku można odkryć dużą liczbę problemów bądź niejasności. Zostawmy jednak kwestię analizy statycznej i przejdźmy do tworzenia przypadków testowych. Krok 1. Określenie przedmiotu i elementów testów. Przedmiotem (elementem) testów jest funkcjonalność szybkiej wypłaty opisana przypadkiem SzybkaWypłata. ET1: SzybkaWypłata. Krok 2, 3. Wyprowadzenie warunków testowych i elementów pokrycia. Warunki testowe, będące jednocześnie elementami pokrycia, to wszystkie scenariusze opisane w przypadku: WT1=EP1 Scenariusz główny – szybka wypłata; WT2=EP2 Scenariusz alternatywny WT3=EP3 Scenariusz alternatywny WT4=EP4 Scenariusz alternatywny WT5=EP5 Scenariusz alternatywny WT6=EP6 Scenariusz alternatywny WT7=EP7 Scenariusz alternatywny

2 – zła karta; 4.1 – zły PIN; 4.2 – brak akcji klienta; 7 – brak środków na koncie; 8 – gotówka nieodebrana; 10 – brak akcji klienta.

Zauważmy, że chociaż scenariusz alternatywny 10 wygląda dokładnie tak samo jak scenariusz alternatywny 4.2, stanowią one dwa osobne elementy pokrycia. Występują one bowiem w różnych momentach interakcji użytkownika z systemem i dlatego nie mogą być opisane jednym, wspólnym warunkiem testowym. Krok 4. Zdefiniowanie przypadków testowych. Dalej szczegółowo opiszemy tylko dwa przypadki – jeden pokrywający scenariusz główny (czyli EP1) i drugi – pokrywający scenariusz alternatywny 2 (zła karta). Kroki zdefiniowane w przypadku testowym odpowiadają krokom scenariusza, który jest testowany. Przypadki te są opisane w tabelach 8.41 oraz 8.42. Tabela 8.41. Przypadek testowy dla scenariusza głównego przypadku użycia SzybkaWypłata

Nazwa przypadku użycia PU004 – Sz ybkaWypłata Nazwa przypadku testowego

PT004.0.0 S cenarius z g łówny – s zybka wypłata

Opis

Klient dokonuje udanej s zybkiej wypłaty 50 PLN z bankomatu

Aktorzy

Klient, bankomat

Pokryte elementy pokrycia

EP1

Pokryte Kroki przypadku użycia

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

W arunki wstępne

Klient włożył kartę do bankomatu wyś wietlająceg o ekran powitalny

# Krok

Oczekiwany wynik

1

Klient wkłada kartę do bankomatu

S ys tem pros i o podanie numeru PIN

2

Klient wprowadza poprawny PIN

Klient wybiera opcję 3 „ S zybka wypłata 50 PLN” 4

Klient odbiera g otówkę

S ys tem wyś wietla ekran opcji Pieniądze zos tają wypłacone, s ys tem wyś wietla komunikat „ pros zę odebrać g otówkę” S ys tem zwraca kartę i wyś wietla komunikat „ pros zę odebrać kartę”

5 Klient odbiera kartę

S ys tem wyś wietla ten s am ekran powitalny, który wyś wietlał przed wykonaniem kroku 1

Kroki opisują akcje podejmowane przez testera, który symuluje zachowanie użytkownika. Oczekiwane wyniki to kroki wykonywane przez system. Są one opisane „wysokopoziomowo” – badamy w końcu obiekt testów na poziomie funkcjonalnym. Nie interesuje nas wewnętrzny stan systemu, ale to, w jaki sposób objawia on swoje działanie na zewnątrz. Dlatego na przykład w kroku 3 przypadku testowego z tabeli 8.41 nie piszemy, że np. system łączy się z bankiem. Na tym poziomie testów funkcjonalnych nie jesteśmy w stanie tego zweryfikować. Powinno to zostać przetestowane wcześniej, na etapie testów integracyjnych oprogramowania bankomatu z systemem bankowym. W tabeli 8.43 podsumowano metodę testowania opartego na przypadkach użycia. Tabela 8.42. Przypadek testowy dla scenariusza alternatywnego przypadku użycia SzybkaWypłata

Nazwa przypadku użycia

PU004 – Sz ybkaWypłata

Nazwa przypadku testowego

PT004.2.0 S cenarius z alternatywny – zła karta

Opis

Nieudana s zybka wypłata z powodu użycia złej karty

Aktorzy

Klient, bankomat

Pokryte elementy pokrycia

EP2

Pokryte Kroki przypadku użycia

1, 2.1, 2.2

W arunki wstępne

Klient włożył kartę niewłaś ciweg o banku do bankomatu wyś wietlająceg o ekran powitalny

# Krok

Oczekiwany wynik

Klient wkłada Bankomat wyś wietla komunikat „ zła karta” , zwraca 1 złą kartę do kartę i wyś wietla ten s am ekran powitalny, który bankomatu wyś wietlał przed wykonaniem kroku 1 Tabela 8.43. Podsumowanie metody testowania opartego na przypadkach użycia

Warunki tes towe S cenarius ze: g łówny oraz alternatywne Elementy pokrycia

S cenarius ze: g łówny oraz alternatywne

Kryterium pokrycia

Dla każdeg o s cenarius za is tnieje tes t złożony z jeg o kroków

Pokrycie

Nie definiuje s ię metryki pokrycia

Zarówno w technice testowania opartej na przypadkach użycia, jak i w opisanych dalej technikach opartych na scenariuszach i historyjkach użytkownika, metryka pokrycia nie jest definiowana. Wynika to stąd, że metryka ta ma zastosowanie w sytuacjach, w których jest możliwe istnienie nieosiągalnych elementów pokrycia w ramach jakiegoś wymagania lub w których przypadki

testowe nie pokrywają wszystkich tych elementów. W przypadkach użycia testy wprost odnoszą się do scenariuszy (=wymagań!), więc w tym sensie pokrycie zawsze wyniesie 100%. Można zdefiniować miarę wyrażającą stosunek pokrytych do wszystkich możliwych scenariuszy, ale taka miara, w przypadku, gdy przyjmuje wartość mniejszą od 100%, będzie wyrażać po prostu fakt nieprzetestowania jakiegoś wymagania. W tym sensie różnica w wartościach tej metryki między np. 50% czy 75% nie ma żadnego znaczenia i nie niesie ze sobą żadnej sensownej informacji.

8.11. Testowanie oparte na scenariuszach 8.11.1. Opis metody Celem testowania opartego na scenariuszach jest dostarczenie przypadków testowych pokrywających na ustalonym poziomie scenariusze obiektu testów. Testowanie scenariuszy jest oparte na analizie bazy testów dla testowanego obiektu i wyprowadzeniu na jej podstawie modelu zachowania tego obiektu. Model zachowania opisuje sekwencje zdarzeń tworzących przepływ pracy (ang. workflow) w danym obiekcie. Hipoteza błędu mówi, że błędy powstają na skutek nieprawidłowego oprogramowania procesów biznesowych. Testowanie oparte na scenariuszach jest podobne do testowania opartego na przypadkach użycia. Zwykle jednak techniki tej używa się na wyższym poziomie ogólności niż przypadki, które dotyczą pojedynczych, niewielkich interakcji między użytkownikiem a systemem. Testowanie oparte na scenariuszach może swoim zasięgiem obejmować testowanie całego przepływu zdarzeń w systemie. Metodę opiszemy na przykładzie działania bankomatu, którego fragment funkcjonalności posłużył nam jako ilustracja techniki testowania opartego na przypadkach użycia. W tabeli 8.44 podsumowano metodę testowania opartego na scenariuszach. Tabela 8.44. Podsumowanie metody testowania opartego na scenariuszach

W arunki testowe

Scenariusze: główny oraz alternatywne

Elementy pokrycia

S cenarius ze: g łówny oraz alternatywne

Kryterium pokrycia

Dla każdeg o s cenarius za is tnieje tes t złożony z jeg o kroków

Pokrycie

Nie definiuje s ię metryki pokrycia

8.11.2. Przykład Krok 1. Określenie przedmiotu i elementów testów. Przedmiotem testów i jedynym elementem testów jest oprogramowanie bankomatu. ET1: oprogramowanie bankomatu. Krok 2. Wyprowadzenie warunków testowych. Klient zidentyfikował następujące scenariusze dla oprogramowania bankomatu: Scenariusz główny: udana wypłata gotówki z bankomatu. Scenariusze alternatywne: odmowa transakcji z powodu użycia złej karty; wpisanie przez klienta 10. Wzorzec blokowy wystąpi wtedy, gdy we fragmencie programu postaci: if (x>=4 and x=2 and y 0) then y:=y+1

nie stanowi niepodzielnej instrukcji, ponieważ zwiększenie y o jeden nie nastąpi zawsze z chwilą wejścia do instrukcji if, ale tylko wtedy, gdy warunek tej instrukcji będzie

spełniony.

Dlatego

ten

fragment

kodu

powinniśmy

przedstawić

następująco: if (x>0) then y:=y+1 Teraz każda z dwóch linii kodu stanowi jednocześnie osobną instrukcję. Kryterium pokrycia instrukcji wymaga, aby każda instrukcja została wykonana przynajmniej raz podczas wykonywania przypadków testowych. W terminologii grafu przepływu sterowania (patrz p. 5.4.1) oznacza to, że wymagamy, aby każdy wierzchołek tego grafu został odwiedzony przynajmniej raz. Hipoteza błędu mówi, że błąd występuje w konkretnej linii programu, zatem każdą z nich należy przynajmniej raz wykonać. testowanie instrukcji (ang. statement testing) – białoskrzynkowa technika projektowania przypadków testowych, w których przypadki projektuje się tak, aby wykonały instrukcje W testowaniu instrukcji warunkami testowymi – i jednocześnie elementami pokrycia – są poszczególne instrukcje programu. Kryterium pokrycia to odsetek wykorzystanych elementów pokrycia. Jeśli naszym modelem nie jest bezpośrednio kod, lecz jego graf przepływu sterowania, to elementami pokrycia mogą być nie pojedyncze instrukcje, ale bloki podstawowe1. Wtedy, w przypadku pokrycia mniejszego od 100%, te dwie miary (pokrycie instrukcji i pokrycie blokówwierzchołków grafu) mogą się od siebie różnić. pokrycie instrukcji kodu (ang. statement coverage) – odsetek instrukcji wykonywalnych, które zostały przetestowane przez zestaw testowy Osiągnięcie 100% pokrycia może być niemożliwe ze względu na istnienie martwego kodu. Model wykorzystujący graf przepływu sterowania zawsze może dostarczyć zbiór ścieżek wykonania pokrywających wszystkie instrukcje, jednak pokrycie to ma charakter syntaktyczny. Aby rzeczywiście instrukcje były pokryte, należy uruchomić program z konkretnymi danymi wejściowymi. Może się okazać, że syntaktyczne pokrycie kodu jest niemożliwe, bo nie da się wymusić

sterowania przechodzącego określoną ścieżką. Ilustruje to fragment kodu z listingu 9.1.

x:=0; (B1) if (x>0) then

(B2)

P (B3) else Q. (B4) Listing 9.1. Fragment programu z martwym kodem Niezależnie od instrukcji poprzedzających ten fragment, predykat w warunku if nigdy nie będzie spełniony, zatem fragment P programu nigdy się nie wykona. Z syntaktycznego punktu widzenia, dwie ścieżki: B1 → B2 → B3 oraz B1 → B2 → B4 zapewniają 100% pokrycie instrukcji. Problem polega na tym, że ścieżki pierwszej nie da się wykonać dla żadnych danych wejściowych. Pokrycie tego fragmentu może zatem maksymalnie wynieść 75% (pokryte 3 spośród 4 instrukcji lub bloków podstawowych). Już w 1987 roku standard IEEE ANSI 87B stwierdzał, że pokrycie instrukcji jest minimalnym akceptowalnym poziomem pokrycia. Boris Beizer w swojej książce [14] poszedł jeszcze dalej, stwierdzając radykalnie, iż „testing less than this for new software is unconscionable and should be criminalized” (przetestowanie w nowym oprogramowaniu mniej niż pokrycia instrukcji jest nierozsądne i powinno być karalne). Beizer podaje również kilka dobrych praktyk dotyczących tego kryterium: 1. Nieprzetestowanie fragmentu kodu stwarza zagrożenie niewykrycia usterki, proporcjonalne do rozmiaru niepokrytego kodu oraz do prawdopodobieństwa wystąpienia błędu. 2. Ścieżki o wysokim prawdopodobieństwie wykonania zawsze są dokładnie testowane, jeśli chcemy zademonstrować poprawne typowe działanie systemu. Jeśli nie można z jakichś względów pokryć testami wszystkich instrukcji, to rozsądniej jest opuścić właśnie te typowe ścieżki, gdyż na pewno będą one wykonane podczas testów integracyjnych lub systemowych.

3. Błędy logiczne i mętne myślenie są odwrotnie proporcjonalne do prawdopodobieństwa wykonania ścieżki (im bardziej udziwniony i podatny na błędy kod, tym mniejsza szansa, że zostanie on wykonany w typowym przebiegu). 4. Subiektywne prawdopodobieństwo wykonania ścieżki szacowane przez projektanta bądź programistę jest zwykle zupełnie inne od prawdziwego. Jedynie analiza działania programu (np. przez profilowanie, patrz podrozdz. 7.4) może dać obiektywne szacowanie. W tabeli 9.1 przedstawiono podsumowanie metody testowania instrukcji. Tabela 9.1. Podsumowanie metody testowania instrukcji

9.1.2. Przykład Rozważmy program sortowania metodą bąbelkową, opisany w rozdziale 2. Jego kod (tym razem z poprawną instrukcją w linii 4.) oraz odpowiadający mu graf przepływu sterowania są pokazane na rysunku 9.1. Chcemy przetestować tę funkcję przy użyciu kryterium pokrycia linii kodu. Krok 1. Określenie przedmiotu i elementów testów. Jedynym elementem testu jest funkcja SortBąbelkowe. ET1: SortBąbelkowe.

Rysunek 9.1. Kod i CFG dla programu SortBąbelkowe Krok 2. Wyprowadzenie warunków testowych. Warunkami testowymi są wykonywalne instrukcje. Zauważmy, że w naszym kodzie jedna linia programu odpowiada pojedynczej instrukcji. Wszystkie warunki testowe są dla ET1. WT1: instrukcja WT2: instrukcja WT3: instrukcja WT4: instrukcja

nr 2; WT5: instrukcja nr 3; WT6: instrukcja nr 4; WT7: instrukcja nr 5; WT8: instrukcja

nr 6; nr 9; nr 10; nr 11.

Krok 3. Wyprowadzenie elementów pokrycia. Elementy pokrycia tworzą bezpośrednio warunki testowe: EP1: instrukcja EP2: instrukcja EP3: instrukcja EP4: instrukcja

nr 2; EP5: instrukcja nr 3; EP6: instrukcja nr 4; EP7: instrukcja nr 5; EP8: instrukcja

nr 6; nr 9; nr 10; nr 11.

Wymóg przejścia przez wszystkie elementy pokrycia jest równoważny wymogowi pokrycia wszystkich wierzchołków CFG z rysunku 9.1. Zauważmy, że istnieje pojedyncza ścieżka sterowania przechodząca przez wszystkie wierzchołki: B1 → B2 → B3 → B5 → B6 → B4 → B3 → B7 → B8. Oznacza to, że wystarczy tylko jeden przypadek testowy realizujący tę ścieżkę, aby spełnić 100% pokrycia instrukcji. Należy znaleźć dla niego takie wartości wejścia (tablicę T), aby pętla for, ciało instrukcji if oraz pętla do-while wykonały się przynajmniej raz. Krok 4. Zdefiniowanie przypadków testowych. Dla kryterium pokrycia instrukcji stosuje się zwykle podejście maksymalizujące, tzn. każdym nowym przypadkiem staramy się pokryć jak najwięcej elementów pokrycia. W przypadku programu SortBąbelkowe wystarczy tylko jeden przypadek, pokazany w tabeli 9.2. Tabela 9.2. Przypadki testowe dla pokrycia instrukcji

Id

W ejście

PT1 T = [5, 3]

Oczekiwane wyjście

Pokryte EP

T = [3, 5]

EP1, EP2, EP3, EP4, EP5, EP6, EP7, EP8

Instrukcje 2, 3 i 4 (odpowiadające EP1, EP2 i EP3) wykonają się zawsze, niezależnie od postaci wejścia. Ciało pętli for wykona się przynajmniej raz, ponieważ 0 < 2 – 1. Najpierw zostanie wykonana instrukcja if w linii 5., pokrywając EP4. Warunek tej instrukcji będzie spełniony, bo 5 = T[0] > T[1] = 3, zatem nastąpi wykonanie linii 6. (EP5). Po zwiększeniu i o 1 i powrocie do początku pętli for jej warunek nie będzie już spełniony (bo 1 < 1 jest fałszem), w związku z czym pętla for zakończy swoje działanie i program przejdzie do linii 9 (EP6). Następnie zostanie sprawdzony warunek pętli do-while w linii 10 (EP7). Nie jest on spełniony (bo mamy n = 1, a 1 > 1 jest fałszem), dlatego sterowanie wyjdzie z pętli do-while i przejdzie do linii 11., pokrywając EP8 i kończąc działanie funkcji. Kryterium pokrycia instrukcji jest dosyć słabe. Na przykład nie przetestowaliśmy sytuacji, w której pętla for nie wykona się ani razu albo

sytuacji, w której warunek instrukcji if nie byłby spełniony. W terminologii grafu przepływu sterowania oznacza to, że nie pokryliśmy wszystkich krawędzi CFG.

9.2. Testowanie gałęzi 9.2.1. Opis metody Kryterium pokrycia gałęzi (zwane też pokryciem krawędzi) wymaga, aby testy pokryły

wszystkie

możliwe

przepływy

sterowania

między

blokami

podstawowymi. W terminologii CFG oznacza to po prostu pokrycie wszystkich krawędzi grafu. W definicji pokrycia gałęzi dodatkowo będziemy wymagać spełnienia kryterium pokrycia instrukcji. Zagwarantuje to subsumpcję między tymi kryteriami. Gdybyśmy nie dodali tego wymogu, dla programu złożonego z jednego bloku podstawowego (np. z pojedynczej instrukcji) jego graf przepływu sterowania nie miałby żadnych krawędzi i dlatego kryterium pokrycia gałęzi byłoby spełnialne przez pusty zbiór testów, który w oczywisty sposób nie pokrywałby instrukcji. Jeśli w CFG istnieje przynajmniej jedna krawędź, to oczywiście pokrycie krawędzi implikuje pokrycie wierzchołków. gałąź (ang. branch) podstawowymi testowanie

gałęzi,



(ang.

przepływ

branch

sterowania

testing)



między

dwoma

blokami

białoskrzynkowa

technika

projektowania przypadków testowych, w której przypadki te są projektowane w celu wykonania gałęzi; w metodologii TMap [28] testowanie gałęzi nosi nazwę testowania algorytmu (ang. algorithm test) Warunkami testowymi są wszystkie przepływy sterowania między blokami podstawowymi, a więc wszystkie krawędzie CFG. Elementy pokrycia są tożsame z warunkami testowymi. Jeśli program nie ma rozgałęzień, to warunkami testowymi oraz elementami pokrycia są instrukcje kodu. W tabeli 9.3 podsumowano metodę testowania gałęzi. pokrycie gałęzi (ang. branch coverage) – odsetek gałęzi sprawdzonych przez zestaw przypadków testowych. 100% pokrycia gałęzi implikuje 100% pokrycia instrukcji oraz decyzji

Tabela 9.3. Podsumowanie metody testowania gałęzi

9.2.2. Przykład Rozważmy ponownie funkcję SortBąbelkowe z poprzedniego rozdziału i stwórzmy dla niej przypadki testowe spełniające kryterium pokrycia gałęzi. Krok 1. Określenie przedmiotu i elementów testów. Elementem testu jest funkcja SortBąbelkowe. ET1: SortBąbelkowe. Krok 2 i 3. Wyprowadzenie warunków testowych i elementów pokrycia. Warunkami testowymi i jednocześnie elementami pokrycia są krawędzie grafu przepływu sterowania. WT1=EP1: (B1, B2); WT5=EP5: (B5, B4); WT9=EP9: (B7, B2); WT2=EP2: (B2, B3); WT6=EP6: (B6, B4); WT10=EP10: (B7, B8). WT3=EP3: (B3, B5); WT7=EP7: (B4, B3); WT4=EP4: (B5, B6); WT8=EP8: (B3, B7); Graficznie te elementy są pokazane na rysunku 9.2.

Rysunek 9.2. Krawędzie CFG jako elementy pokrycia w testowaniu gałęzi

Krok 4. Zdefiniowanie przypadków testowych. Musimy dostarczyć takie dane wejściowe, aby wszystkie gałęzie zostały pokryte. Rozważmy przypadek wejścia T = [4, 3]. Dla tej tablicy program przejdzie ścieżką p = B1 → B2 → B3 → B5 → B6 → B4 → B3 → B7 → B8 (zarówno pętla zewnętrzna do-while, jak i pętla for wykonają się tylko raz). Warunek w instrukcji if będzie spełniony, zatem sterowanie przejdzie przez wierzchołek B6. Wykonanie ścieżki p spowoduje pokrycie EP1, EP2, EP3, EP4, EP6, EP7, EP8, EP10. Zostały nam zatem do pokrycia jeszcze EP5 i EP9. EP5 zostanie pokryte, gdy warunek w instrukcji if nie będzie spełniony, tzn. gdy porównywane elementy będą posortowane. EP9 z kolei zostanie pokryte, gdy warunek pętli zewnętrznej do-while będzie prawdziwy, co nastąpi, gdy tablica będzie miała więcej niż 2 elementy. Jeśli więc podamy na wejście np. tablicę T = [3, 4, 5], to sterowanie przejdzie ścieżką q = B1 → B2 → B3 → B5 → B4 → B3 → B5 → B4 → B3 → B7 → B2 → B3 → B5 → B4 → B3 → B7 → B8. Ścieżka ta, ze względu na istnienie w niej krawędzi (B5, B4) oraz (B7, B2) pokrywa elementy EP5 i EP9. Skonstruowaliśmy dwa przypadki testowe pokrywające 100% gałęzi, jednak można ten zbiór zredukować do jednego przypadku. Zauważmy, że przechodząc ścieżką testową r = B1 → B2 → B3 → B5 → B6 → B4 → B3 → B5 → B4 → B3 → B7 → B2 → B3 → B5 → B4 → B3 → B7 → B8, przejdziemy po wszystkich gałęziach. Przypadek wymuszający takie przejście to np. T = [3, 2, 5]. Generalnie im dłuższa ścieżka testowa, tym trudniej dobrać wartości wejściowe tak, by wymusić przejście programu dokładnie po tej ścieżce. Dlatego czasami można zrezygnować z chęci uzyskania możliwie jak najmniejszego zbioru przypadków testowych na rzecz stworzenia nieco większej ich liczby, ale za to łatwiej podlegających wymuszeniu określonego sterowania. Skonstruowaliśmy dwa zbiory testowe, z których każdy spełnia kryterium pokrycia gałęzi. Zbiory te przedstawione są w tabelach 9.4 i 9.5 (pogrubiona czcionka oznacza pokrycie danego elementu po raz pierwszy). Tabela 9.4. Przypadki testowe spełniające kryterium pokrycia gałęzi – wariant I

Id

W ejście

PT1 T = [4, 3]

Oczekiwane wyjście

Pokryte elementy

T = [3, 4]

EP1, EP2, EP3, EP4, EP6, EP7, EP8, EP10

PT2 T = [3, 4, 5]

T = [3, 4, 5]

EP1, EP2, EP3, EP5, EP7, EP8, EP9, EP10

Tabela 9.5. Przypadki testowe spełniające kryterium pokrycia gałęzi – wariant II

Id

W ejście

Oczekiwane wyjście

Pokryte elementy

PT1

T = [3, 2, 5]

T = [3, 4, 5]

EP1, EP2, EP3, EP4, EP5, EP6, EP7, EP8, EP9, EP10

Gdybyśmy w wariancie I ograniczyli się tylko do przypadku PT1, stopień pokrycia wyniósłby 8/10 = 80%.

9.3. Testowanie decyzji 9.3.1. Opis metody Testowanie decyzji należy do rodziny kryteriów pokryć logicznych, gdyż dotyczy wartości logicznych predykatów występujących w instrukcjach decyzyjnych2. Stąd zresztą inna nazwa techniki: pokrycie predykatów. Decyzja jest więc po prostu wyrażeniem logicznym, które może przyjąć wartość logiczną prawdy lub fałszu. wynik decyzji (ang. decision outcome) – rezultat decyzji, określający gałąź do podążania Przykładami instrukcji decyzyjnych są: instrukcja warunkowa if: if (decyzja) then ...; warunek w instrukcji while: while (decyzja) do ...; warunek w instrukcji do-while: do ... while (decyzja); instrukcja switch-case; warunek pętli w instrukcji for: for (inicjalizacja; decyzja; inkrementacja) do ....

Testowanie decyzji wymaga, aby każda decyzja w programie przynajmniej raz przyjęła wartość logiczną prawdy i przynajmniej raz – wartość logiczną fałszu. Na przykład, jeśli w kodzie występuje instrukcja if (x>0 and y==0) then ... to przynajmniej raz warunek (x > 0 ∧ y = 0) powinien być prawdziwy i przynajmniej raz fałszywy. Prawdziwość można wymusić, przyjmując np. x = 1, y = 0, natomiast fałszywość przyjmując np. x = 1, y = 1. W przypadku instrukcji switch-case wymaga się przyjęcia przez zmienną w instrukcji switch wszystkich możliwych wartości ujętych we fragmentach case. Konstrukcja switch(x) case y1: ... break; case y2: ... break; ... case yn: ... break; da się bowiem wyrazić jako wielokrotna instrukcja if-then-else: if (x==y1) then ... else if (x==y2) then ... else if(x==yn) then ... endif Przyjęcie przez jeden z warunków instrukcji switch-case wartości logicznej prawdy oznacza automatycznie przyjęcie przez pozostałe warunki wartości logicznej fałszu. decyzja (ang. decision) – punkt w programie, w którym przepływ sterowania ma dwie lub więcej alternatywnych dróg; węzeł grafu przepływu sterowania, z którego wychodzą dwie lub więcej gałęzi

Tak jak w przypadku pokrycia gałęzi, do kryterium pokrycia decyzji dodajemy formalny warunek o pokryciu instrukcji, aby kryterium subsumowało pokrycie instrukcyjne dla

programów złożonych z pojedynczych bloków

podstawowych, niezawierających żadnych rozgałęzień. Hipoteza błędu mówi, że błędy mogą powstać na skutek określonego przepływu sterowania, wyznaczonego przez decyzje podejmowane w instrukcjach decyzyjnych. Rozważmy następujący prosty przykład z listingu 9.2.

1 x:=0; 2 if (a>b) then 3 x:=x+1 4 endif 5 y:=100/x Listing 9.2. Prosty program Przyjęcie wartości a i b takich, że a > b spowoduje wykonanie instrukcji 1, 2, 3, 4, 5 i skutkować będzie pełnym pokryciem instrukcyjnym tego fragmentu. Zauważmy jednak, że jeśli instrukcja 3 się nie wykona, to zmienna x cały czas będzie mieć wartość 0, a w linii 5. dzielimy przez x. Dzielenie przez 0 jest niedozwolone i wykonanie takiej operacji może skutkować awarią lub nieprzewidzianym działaniem programu bądź systemu operacyjnego. Stosując kryterium pokrycia instrukcji, możemy nie przewidzieć przetestowania programu w ten sposób. Sytuację tę możemy natomiast sprawdzić, przyjmując kryterium pokrycia decyzji. Wymusi ono bowiem istnienie przynajmniej jednego przypadku testowego, w którym predykat linii 2. ma wartość fałszu. To spowoduje dzielenie przez 0 w linii 5 i wykrycie awarii. Warunkami testowymi w tej metodzie są bloki podstawowe zawierające decyzje, czyli instrukcje if, for, while, switch-case itd. Dla danego warunku testowego elementami pokrycia są wszystkie możliwe wartości logiczne (prawda oraz fałsz), jakie może przyjąć decyzja odpowiadająca temu warunkowi. Jeśli program nie zawiera rozgałęzień, warunkami testowymi i elementami pokrycia są instrukcje, tak jak w kryterium pokrycia instrukcji. W tabeli 9.6 podsumowano metodę testowania decyzji.

pokrycie decyzji (ang. decision coverage) – odsetek możliwych wyników decyzji, które zostały przetestowane przez zestaw testowy. 100% pokrycia decyzji implikuje 100% pokrycia instrukcji Tabela 9.6. Podsumowanie metody testowania decyzji

W arunki W ierzchołki CFG reprezentujące decyzje testowe Elementy Krawędzie CFG odpowiadające wynikom decyzji pokrycia Kryterium Każda decyzja przynajmniej raz powinna przyjąć wartoś ć pokrycia prawdy i przynajmniej raz wartoś ć fałs zu

Pokrycie

p =

liczba pokrytych tes tami krawędzi decyzyjnych CFG liczba ws zys tkich krawędzi decyzyjnych CFG

× 100%;

w przypadku CFG z 1 blokiem pods tawowym 0% lub 100% w zależnoś ci od s pełnienia kryterium ins trukcyjneg o

9.3.2. Testowanie decyzji a testowanie gałęzi Testowanie decyzji i testowanie gałęzi są podobnymi kryteriami. Tak naprawdę w przypadku pełnego, stuprocentowego pokrycia są sobie równoważne3, tzn. 100% pokrycia gałęzi jest równoważne 100% pokrycia decyzji. Innymi słowy oznacza to, że jeśli jakiś zbiór przypadków testowych spełnia jedno z tych kryteriów, to automatycznie spełnia też drugie. Różnice pojawiają się przy mniejszych stopniach pokrycia, ponieważ – z punktu widzenia grafu przepływu sterowania – testowanie decyzji jest równoważne pokryciu pewnego podzbioru gałęzi (odpowiadających za realizację decyzji). Różnicę tę omówimy na przykładzie z kolejnego podrozdziału.

9.3.3. Przykład

Rozważmy funkcję Bisekcja znajdującą pierwiastek4 ciągłej funkcji f(x) w zadanym przedziale metodą bisekcji. Funkcja na wejściu przyjmuje granice l i r przedziału (l, r) na którym będzie szukać pierwiastka. Jeśli funkcja jest ciągła, a granice przedziału mają różne znaki, to przedział ten na pewno zawiera co najmniej jedno miejsce zerowe. Algorytm, przedstawiony na listingu 9.3 kończy działanie, gdy aktualnie przeszukiwany przedział ma długość mniejszą niż pewna ustalona wartość ε lub gdy trafi dokładnie w miejsce zerowe f. W przypadku, gdy parametry wejściowe mają ten sam znak, algorytm od razu zwraca komunikat o niepoprawnym przedziale wejściowym.

function Bisekcja(double l, double r) 1 double eps := 0.0001 2 double left := l 3 4

double right := r double mid

5 6 7

if (left*right > 0) then return "Niepoprawny przedział wejściowy" while (right-left > eps) do

8 9 10 11

mid = (left+right)/2 if (f(mid)==0) then return "Dokładny pierwiastek = "+mid if (f(left)*f(mid)>0) then

12 13

left := mid else if (f(left)*f(mid) 0.0001, m := 0.0000625), pokrywa EP3; B4=fałsz, pokrywa EP6; B6=fałsz (bo f(l) ⋅ f(m) = l ⋅ m < 0), pokrywa EP8; B8= prawda r := 0.0000625, pokrywa EP9; B3=prawda (l = –0.000125, r = 0.0000625, r – l = 0.0001875, m := –0.00003125; B4=fałsz; B6=prawda (bo f(l) ⋅ f(m) = l ⋅ m > 0), l := –0.00003125, pokrywa EP7; B3=fałsz (bo r – l = 0.0000625 – (–0.00003125) = 0.00009375 < 0.0001, pokrywa EP4. Jak zauważyliśmy wcześniej, pokrycie EP10 jest nieosiągalne. Uzyskaliśmy zatem 9/10 = 90% pokrycia decyzji. Sprawdźmy, ile dla tego samego zbioru testów wynosi pokrycie gałęzi. W grafie przepływu sterowania występuje 13 krawędzi. Jedyną niepokrytą krawędzią (ze względu na nieosiągalność EP10) jest krawędź (B8, B10). Stopień pokrycia gałęzi wynosi zatem 12/13 ≈ 92% – nieco więcej, niż stopień pokrycia decyzji.

9.4. Testowanie warunków 9.4.1. Opis metody Często zdarza się, że decyzje w instrukcjach warunkowych mają dosyć skomplikowaną postać, w szczególności mogą wykorzystywać spójniki logiczne

takie jak alternatywę (logiczne „lub”) czy koniunkcję (logiczne „i”). Warunek jest szczególnym przypadkiem decyzji, w której nie występują spójniki logiczne. Na przykład w decyzji D: x > y ∧ (z == 0 ∨ y > 0) występują trzy warunki: x > y, z == 0 oraz y > 0. W języku logiki decyzje są nazywane predykatami, a warunki – klauzulami. warunek, warunek rozgałęzienia (ang. condition, branch condition) – wyrażenie logiczne, którego wartością może być prawda lub fałsz warunek atomowy (ang. atomic condition) – wyrażenie logiczne niezawierające spójników logicznych, mogące przyjmować warunek prawdy lub fałszu wartość warunku (ang. condition outcome) – wyliczenie wartości logicznej warunku jako prawdy lub fałszu Kryterium pokrycia warunków jest analogiczne do kryterium pokrycia decyzji, z tym że – jak sama nazwa wskazuje – dotyczy warunków, a nie decyzji. Stanowi ono, że każdy warunek w każdej decyzji powinien przynajmniej raz przyjąć wartość logiczną prawdy i przynajmniej raz wartość logiczną fałszu. Tak jak poprzednio, dodajemy do kryterium formalnie wymóg pokrycia instrukcji. pokrycie warunków (ang. condition coverage) – odsetek wyników warunków, jaki został uzyskany przez zestaw testowy Tabela 9.8. Przypadki testowe dla decyzji złożonej z 3 warunków

Id

x y

z x >y

z == 0

PT1 1 3

0 fałs z

prawda prawda fałs z

PT2 1 –2 1 prawda fałs z

y >0

fałs z

D := (x > y ∧ (z == 0 ∨ y > 0))

fałs z

Aby spełnić pokrycie warunków dla decyzji D, musimy dostarczyć takie przypadki testowe, które spowodują prawdziwość oraz fałszywość każdego z trzech warunków. Przypadki te muszą zatem wymusić 6 następujących sytuacji: x > y, x ≤ y, z = 0, z ≠ 0, y > 0 oraz y ≤ 0. Przypadków testowych zwykle jest o wiele

mniej niż warunków, gdyż dla decyzji złożonej z k warunków jeden zbiór wartości wejściowych pokrywa k różnych wartości logicznych, po jednej dla każdego warunku. Rozważmy dla zdefiniowanej decyzji D dwa przypadki testowe opisane w tabeli 9.8. Kolumny x, y, z zawierają wartości odpowiednich zmiennych. W kolejnych trzech kolumnach są podane wartości logiczne, jakie przyjmują poszczególne warunki decyzji D dla zadanych x, y, z. W ostatniej kolumnie jest podana wartość logiczna całej decyzji. Zauważmy, że kryterium pokrycia warunków jest spełnione przez dwa przypadki testowe: (1, 3, 0) oraz (1, –2, 1), ponieważ każdy warunek przyjął przynajmniej raz wartość logiczną prawdy i przynajmniej raz wartość logiczną fałszu. Jednak przypadki te nie spełniają kryterium pokrycia decyzji – w obu przypadkach D jest fałszywe! Przykład ten pokazuje, że kryterium pokrycia warunków nie subsumuje pokrycia decyzji, choć intuicyjnie wydawałoby się, że tak musi być, skoro kryterium „rozbija” decyzję na mniejsze kawałki i dla każdego z nich stosuje te same wymagania, co dla całej decyzji. Brak subsumpcji wynika stąd, że przyjmowanie wartości logicznych przez warunki nie ma nic wspólnego ze strukturą decyzji, tzn. nie bierze pod uwagę tego, w jaki sposób warunki te są połączone spójnikami logicznymi. Warunkami testowymi dla kryterium pokrycia warunków są decyzje. Dla każdego warunku testowego elementami pokrycia są ewaluacje zarówno na wartość prawdy, jak i fałszu wszystkich warunków wchodzących w skład decyzji. Na przykład, jeśli warunkiem testowym jest decyzja D = x > y ∧ (z == 0 ∨ y > 0), to odpowiada jej sześć elementów pokrycia: x > y jest prawdą, z == 0 jest prawdą, y > 0 jest prawdą; x > y jest fałszem, z == 0 jest fałszem, y > 0 jest fałszem. Jeśli program nie zawiera decyzji, to warunkami testowymi i elementami pokrycia są instrukcje. W tabeli 9.9 przedstawiono podsumowanie metody testowania warunków. Tabela 9.9. Podsumowanie metody testowania warunków

9.4.2. Przykład Rozważmy kod z listingu 9.4 implementujący metodę isLeapYear sprawdzającą, czy podany rok jest przestępny. Przypomnijmy, że rok jest przestępny, jeśli dzieli się przez 4, chyba że dzieli się przez 100, ale nie przez 400.

int isLeapYear(int year) { leapYear=0; if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) { leapYear=1; } return leapYear } Listing 9.4. Funkcja sprawdzająca przestępność roku Znak „%” oznacza w Javie dzielenie modulo. Na przykład wyrażenie year % 4 == 0 jest prawdziwe, gdy wartość year jest podzielna przez 4, tzn. gdy daje przy dzieleniu przez 4 resztę 0. Operatory „&&” i „||” oznaczają w Javie odpowiednio koniunkcję i alternatywę, a „!=” to operator „różne od”. Chcemy przetestować ten fragment kodu za pomocą kryterium pokrycia warunków.

Krok 1. Określenie przedmiotu i elementów testów. Mamy tylko jeden przedmiot testów – funkcję leapYear. ET1: leapYear. Krok 2. Wyprowadzenie warunków testowych. Warunkiem testowym jest (jedyna) decyzja w instrukcji if. WT1: ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0) (dla ET1). Krok 3. Wyprowadzenie elementów pokrycia. Elementami pokrycia są wszystkie możliwe ewaluacje wszystkich warunków dla każdej decyzji. W naszym przypadku mamy tylko jedną decyzję i 3 warunki, co daje 6 następujących elementów pokrycia odpowiadających WT1: EP1: year % 4 = 0 jest prawdą; EP4: year % 100 ≠ 0 jest fałszem; EP2: year % 4 = 0 jest fałszem; EP5: year % 400 = 0 jest prawdą; EP3: year % 100 ≠ 0 jest prawdą; EP6: year % 400 = 0 jest fałszem. Krok 4. Zdefiniowanie przypadków testowych. Zauważmy, że wartości logiczne warunków mogą zależeć od wartości innych warunków. Na przykład, jeśli rok jest podzielny przez 400, to w szczególności zawsze będzie podzielny przez 4 i przez 100. W przypadku metody testowania warunków nie wpływa to na możliwość pełnego pokrycia (może co najwyżej spowodować zwiększenie liczby potrzebnych przypadków testowych), ale jest problematyczne przy bardziej skomplikowanych kryteriach, takich jak niektóre warianty testowania warunków znaczących opisane w podrozdziale 9.7. Rozważmy następujące przypadki (tab. 9.10). Tabela 9.10. Przypadki testowe dla testowania warunków

Id

year

year % year % 4=0 100 ≠ 0

PT1 2000 prawda fałs z

year % 400 = 0

Oczekiwane Pokryte Decyzja wyjście EP WT1 (leapYear)

prawda 1

EP1, EP4,

prawda

EP5 PT2 1999 fałs z

prawda fałs z

0

EP2, EP3, EP6

fałs z

Każdy z warunków jest co najmniej raz ewaluowany na prawdę i co najmniej raz na fałsz, zatem osiągnęliśmy 100% pokrycie warunków. Przy okazji uzyskaliśmy również 100% pokrycia decyzji.

9.5. Testowanie warunków/decyzji 9.5.1. Opis metody Jak wspomnieliśmy w poprzednim rozdziale, kryterium pokrycia warunków nie subsumuje kryterium pokrycia decyzji. Metoda testowania warunków/decyzji (ang. Conditon/Decision testing, C/D) jest formą techniki testowania warunków zmodyfikowaną tak, aby oprócz pokrycia warunków wymusić również pokrycie decyzji. Kryterium pokrycia warunków/decyzji zakłada, że każdy warunek oraz każda decyzja w programie przynajmniej raz przyjmie wartość prawdy oraz fałszu. Tak jak zwykle, z przyczyn formalnych dodajemy do kryterium jeszcze wymóg pokrycia instrukcji. Warunkami testowymi są decyzje, a elementami pokrycia – wszystkie wartości logiczne każdego warunku oraz każdej decyzji. Jeśli w programie nie ma decyzji, to warunkami testowymi i elementami pokrycia są instrukcje. W tabeli 9.11 przedstawiono podsumowanie metody testowania warunków/decyzji. pokrycie warunków/decyzji (ang. decision/condition coverage) – odsetek wszystkich możliwych wyników warunków oraz decyzji, które zostały sprawdzone przez zestaw testowy. 100% pokrycia warunków/decyzji implikuje 100% pokrycia instrukcji, 100% pokrycia warunków oraz 100% pokrycia decyzji Tabela 9.11. Podsumowanie metody testowania warunków/decyzji

9.5.2. Przykład Rozważmy ponownie zbiór testów z tabeli 9.8 dla decyzji D = (x > y ∧ (z == 0 ∨ y > 0)). Aby spełnić kryterium pokrycia warunków, skonstruowaliśmy dwa przypadki testowe, (x, y, z) = (1, 3, 1) oraz (x, y, z) = (1, –2, 1). Zauważyliśmy, że choć pokrywają one w 100% warunki, nie pokrywają w 100% decyzji, gdyż w obu przypadkach D jest fałszem. Teraz, dla kryterium warunków/decyzji mamy następujące elementy pokrycia: EP1: x > y jest prawdą; EP5: y > 0 jest prawdą; EP2: x > y jest fałszem; EP6: y > 0 jest fałszem; EP3: z == 0 jest prawdą; EP7: D jest prawdą; EP4: z == 0 jest fałszem; EP8: D jest fałszem. Aby spełnić dla D kryterium pokrycia warunków/decyzji możemy dodać trzeci przypadek, wymuszający prawdziwość decyzji (np. dla (x, y, z) = (1, 0, 0)). Możemy też spróbować uzyskać pełne pokrycie, używając nadal tylko dwóch przypadków testowych. Aby D była prawdziwa, musi być x > y oraz z = 0 lub y > 0. W drugim przypadku x > y musi być fałszem, co automatycznie spowoduje ewaluację D na fałsz i pozostałe dwa warunki dobierzemy tak, aby pokryły brakujące elementy pokrycia. Weźmy zatem jako pierwszy test (x, y, z) = (1, 0, 0). Pokrywa on EP1, EP3, EP6 oraz EP7. Aby pokryć pozostałe elementy, musi zachodzić x ≤ y, z ≠ 0, y > 0 oraz ~(x > y ∧ (z == 0 ∨ y > 0)). Możemy na przykład przyjąć (x, y, z) = (2, 2, 2).

Ostatecznie, uzyskaliśmy 100% pokrycie warunków/decyzji przypadków testowych. Są one przedstawione w tabeli 9.12.

dla

dwóch

Tabela 9.12. Przypadki testowe dla pokrycia warunków/decyzji

Id

x y z x >y

z == 0

y >0

PT1 1 0 0 prawda prawda fałs z PT2 2 2 2 fałs z

fałs z

D := (x > y ∧ (z == 0 ∨ y > 0)) prawda

prawda fałs z

9.6. Testowanie wielokrotnych warunków 9.6.1. Opis metody Testowanie wielokrotnych warunków jest wzmocnieniem techniki testowania warunków oraz warunków/decyzji. Kryterium to zakłada, że pokryte będą wszystkie możliwe kombinacje wartości logicznych warunków w każdej decyzji (tradycyjnie, dodajemy również formalny wymóg pokrycia instrukcji). Na przykład dla decyzji (x > y ∨ z == 0) musimy przetestować cztery kombinacje wartości logicznych warunków x > y oraz z == 0: (1) x > y jest prawdą i z == 0 jest prawdą; (3) x > y jest fałszem i z == 0 jest prawdą; (2) x > y jest prawdą i z == 0 jest fałszem; (4) x > y jest fałszem i z == 0 jest fałszem. warunek wielokrotny, warunek złożony (ang. multiple condition, compound condition) – dwa lub więcej warunków połączonych spójnikami logicznymi „and”, „or” lub „xor” Warunek może przyjmować dwie wartości logiczne, zatem dla decyzji zbudowanej z n warunków musimy przetestować 2n kombinacji. Ich wykładnicza liczba sprawia, że kryterium to może być trudne do pełnego pokrycia zwłaszcza w przypadku programów, których decyzje są złożone z wielu warunków. W oczywisty sposób pokrycie wielokrotnych warunków subsumuje zarówno pokrycie warunków, jak i pokrycie decyzji. Warunkami testowymi są decyzje, a elementami pokrycia – wszystkie możliwe kombinacje logiczne warunków dla

każdej decyzji. Jeśli w programie nie ma decyzji, to warunkami testowymi i elementami pokrycia są instrukcje. W tabeli 9.13 podsumowano metodę testowania warunków wielokrotnych. pokrycie

warunków

wielokrotnych,

pokrycie

kombinacji

warunków

w decyzjach, pokrycie kombinacji warunków (ang. multiple condition coverage, branch condition combination coverage, condition combination coverage) – odsetek kombinacji wszystkich wyjść warunków prostych w jednej instrukcji, które zostały sprawdzone przez zestaw testowy. 100% pokrycia warunków wielokrotnych oznacza 100% pokrycia warunków znaczących Tabela 9.13. Podsumowanie metody testowania warunków wielokrotnych

W arunki W ierzchołki CFG reprezentujące decyzje testowe Elementy Ws zys tkie kombinacje wartoś ci log icznych warunków pokrycia w każdej z decyzji Kryterium Każda kombinacja wartoś ci log icznych warunków w każdej pokrycia decyzji powinna wys tąpić przynajmniej raz Pokrycie

p = (liczba pokrytych kombinacji wartoś ci warunków/liczba ws zys tkich kombinacji wartoś ci warunków) × 100%

9.6.2. Zwarcie (semantyka short-circuit) W praktyce, w wielu przypadkach nie ma potrzeby tworzenia wykładniczej liczby testów, ze względu na tzw. zwarcia (ang. short-circuit evaluation). Jest to stosowana przez kompilatory technika optymalizacji obliczania wartości logicznych decyzji. Czasami struktura logiczna predykatu powoduje, że obliczenie wartości logicznej jednej jej części determinuje wartość logiczną całej decyzji. Zobaczmy, jak technika short-circuit działa w praktyce. Załóżmy, że kompilator musi obliczyć wartość logiczną decyzji D = x ∧ y ∧ (w ∨ z). Najpierw jest obliczana wartość logiczna warunku znajdującego się na początku decyzji, czyli x. Załóżmy, że x jest fałszem. Kompilator, znając strukturę decyzji (koniunkcja trzech składników), wie, że skoro x jest fałszem, to niezależnie od

wartości pozostałych warunków, decyzja i tak będzie miała wartość fałszu. Przypadek, gdy x jest fałszem determinuje zatem wartość całej decyzji. Kompilator zaoszczędza czas, nie musi bowiem wyliczać wartości logicznej y, w ani z. Z praktycznego punktu widzenia wszystkie przypadki testowe wartościujące x na fałsz są sobie równoważne, bo wartości y, w i z w ogóle nie będą brane pod uwagę. Zamiast tworzyć 8 przypadków testowych tak jak w tabeli 9.14, wystarczy jeden, w którym x = fałsz, a pozostałe warunki mogą przyjmować dowolne wartości logiczne. Na takiej samej zasadzie, gdy x = prawda, kompilator jeszcze nie może zdeterminować wartości D. Jeśli jednak y = fałsz, wiadomo, że cała decyzja będzie miała wartość fałszu, zatem wartościowania w i z nie mają znaczenia. Ostatecznie, dla spełnienia kryterium pokrycia wielokrotnych warunków w sytuacji stosowania short-circuit evaluation zamiast 16 przypadków wystarczy tylko 5. Są one pokazane w tabeli 9.15. Gwiazdka oznacza dowolną wartość. Tabela 9.14. Przypadki testowe nieuwzględniające zwarcia

x

y

w

z

x

y

w

z

1 fałs z prawda prawda prawda 5 fałs z prawda prawda prawda 2 fałs z prawda prawda fałs z

6 fałs z prawda prawda fałs z

3 fałs z prawda fałs z

prawda 7 fałs z prawda fałs z

prawda

4 fałs z prawda fałs z

fałs z

fałs z

8 fałs z prawda fałs z

Tabela 9.15. Testy dla pokrycia wielokrotnych warunków z short-circuit

Id

x

y

w

z

D

PT1

fałs z

*

*

*

fałs z

PT2

prawda

fałs z

*

*

fałs z

PT3

prawda

prawda

prawda

*

prawda

PT4

prawda

prawda

fałs z

prawda

prawda

PT5

prawda

prawda

fałs z

fałs z

fałs z

Zauważmy, że chociaż formalnie zbiór ten nie spełnia kryterium pokrycia wielokrotnych warunków, to praktycznie tak jest – np. PT1 teoretycznie jest równoważny wszystkim ośmiu przypadkom, w których x jest fałszem. Skoro i tak nie wymusimy ewaluacji pozostałych warunków, to – w sensie przyjętego kryterium pokrycia – nie ma sensu tworzyć przypadków z wszystkimi możliwymi ich kombinacjami. Liczba przypadków w zredukowanym zbiorze testowym będzie oczywiście zależeć od struktury logicznej decyzji oraz od szczegółów technicznych optymalizacji stosowanej przez kompilator. Na przykład ewaluacja wartości logicznej wcale nie musi przebiegać od skrajnie lewego do skrajnie prawego warunku. Kompilator może przeprowadzać ewaluację w innej kolejności tak, aby zminimalizować średni czas potrzebny na określenie wartości logicznej decyzji. Znajomość tych technicznych szczegółów jest kluczowa dla testera, jeśli chce wykorzystać technikę short-circuit do redukcji przypadków testowych. Z semantyką short-circuit jest związany jeszcze jeden, być może poważniejszy, problem. Jeśli mamy na przykład decyzję D postaci A or B(), gdzie B jest wywoływaną funkcją, to w przypadku, gdy A przyjmuje wartość prawdy, B może w ogóle nie być wywołane! Jeśli ciało funkcji B inicjalizuje np. ważną część danych, to programista może być przekonany o tym, że ewaluacja wartości logicznej D musiała spowodować wykonanie funkcji B, a więc dane zostały zainicjalizowane. W przypadku stosowania short-circuit nie musi to być prawdą! zwarcie (ang. short-circuiting) – technika języków programowania/interpreterów przy obliczaniu warunków złożonych, w której wyliczanie warunku po jednej stronie operatora logicznego może być pominięte, jeśli ewaluacja warunku po drugiej stronie tego operatora jest wystarczająca do określenia wyniku końcowego

9.6.3. Nieosiągalne kombinacje warunków Czasami może się zdarzyć, że pewnych kombinacji warunków w obrębie jednej decyzji nie da się uzyskać. Może to wynikać ze stosowania short-circuit evaluation lub z faktu istnienia zależności między warunkami (mówi się wtedy, że zmienne są powiązane, ang. coupled). Rozważmy typowy warunek pętli, zakładając semantykę short-circuit:

while (iy && y>z || x y ∧ y > z), to w szczególności oznacza to, że musi być x > z, zatem trzeci warunek, x ≤ z, nie może być spełniony. Jeśli dwa pierwsze warunki są fałszem, implikuje to prawdziwość warunku trzeciego. Wartość logiczna warunku x ≤ z zależy od wartości logicznej dwóch pozostałych warunków. Gdy pewne elementy pokrycia lub warunki testowe są nieosiągalne dla pewnego kryterium pokrycia C, można je po prostu zignorować, gdyż zwykle ich wymuszony brak nie wpływa na jakość suity testowej. Lepszym rozwiązaniem jest jednak rozważenie spełnienia pokrycia dla innego, subsumowanego przez C kryterium. Na przykład, jeśli warunek testowy X jest nieosiągalny dla C, ale ten sam warunek da się pokryć w słabszym, subsumowanym przez C kryterium C′, to można wymienić nieosiągalne wymaganie w C na osiągalne wymaganie z C′. W przypadku grafowych kryteriów pokryć można też wykorzystać objazdy lub ścieżki poboczne opisane w podrozdziale 9.14.

9.6.4. Przykład Rozważmy funkcję RównanieKwadratowe z listingu 9.5 obliczającą pierwiastki równania ax2 + bx + c = 0. Chcemy skonstruować dla niej testy spełniające kryterium pokrycia warunków wielokrotnych. Zakładamy, że kompilator nie używa semantyki short-circuit.

RównanieKwadratowe(double a, double b, double c) 1 2 3

if (a==0 && b==0) then if (c==0) then return("Równanie nieoznaczone")

4 5 6

else return("Równanie sprzeczne") if (a==0) then

7 8 9 10 11

return "x = "+(-c/b) delta=b*b-4*a*c double x1=(-b+sqrt(delta))/(2*a) double x2=(-b-sqrt(delta))/(2*a) return "x1 = "+x1+", x2 = "+x2

Listing 9.5. Funkcja RównanieKwadratowe Krok 1. Określenie przedmiotu i elementów testów. Mamy tylko jeden element testów – testowaną funkcję: ET1: RównanieKwadratowe. Krok 2. Wyprowadzenie warunków testowych. Warunkami testowymi są wszystkie decyzje występujące w programie: WT1: (a = 0 ∧ b = 0) w linii 1. (dla ET1); WT2: (c = 0) w linii 2. (dla ET1); WT3: (a = 0) w linii 6. (dla ET1). Krok 3. Wyprowadzenie elementów pokrycia. WT2 i WT3 składają się z pojedynczych warunków, więc odpowiadające im elementy pokrycia będą dokładnie takie same, jak w kryterium pokrycia decyzji. Dla WT1 musimy natomiast rozważyć wszystkie 4 możliwe kombinacje prawdy i fałszu dla dwóch warunków stanowiących ten waruntek testowy. EP1: a = 0 jest prawdą, b = 0 jest prawdą (dla WT1);

EP2: a = 0 jest prawdą, b = 0 jest fałszem (dla WT1); EP3: a = 0 jest fałszem, b = 0 jest prawdą (dla WT1); EP4: a = 0 jest fałszem, b = 0 jest fałszem (dla WT1); EP5: a = 0 jest prawdą (dla WT2); EP6: a = 0 jest fałszem (dla WT2); EP7: a = 0 jest prawdą (dla WT3); EP8: a = 0 jest fałszem (dla WT3). Krok 4. Zdefiniowanie przypadków testowych. Zauważmy, że warunek a = 0 odpowiadający WT3 występuje również w WT1. Aby pokryć elementy dla WT3, musimy wymusić, aby sterowanie osiągnęło linię 6. kodu. Należy więc uważnie dobierać wartości dla a i b dla WT1 tak, aby działanie programu nie zakończyło się w linii 3. lub 5. Jeśli np. chcemy, by w linii 6. a = 0 było prawdą, to w warunku a = 0 ∧ b = 0 w linii 1. b = 0 musi być fałszem – w przeciwnym razie decyzja w linii 1. będzie prawdziwa i program wejdzie w blok linii 2.–5., kończąc swoje działanie przed osiągnięciem linii 6. Kompletny zbiór przypadków testowych jest podany w tabeli 9.16. Tabela 9.16. Przypadki testowe dla kryterium pokrycia wielokrotnych warunków

Id

a b

c a=0

b=0

c=0

Oczekiwany wynik

Pokryte EP

PT1 0 0

0 prawda prawda prawda

r. nieoznaczone

EP1, EP5

PT2 0 0

1 prawda prawda fałs z

r. s przeczne

EP1, EP6

PT3 0 1

4 prawda fałs z

x=4

EP2, EP7

PT4 1 0

9 fałsz

prawda (fałs z)

x1 = 3, x2 = –3

EP3, EP8

– 1

0 fałsz

fałs z

PT5 1

(fałs z)

(prawda) x1 = 0, x2 = 1

EP4, EP8

Elementy pokryte po raz pierwszy są oznaczone pogrubioną czcionką. Wartości logiczne ujęte w nawias oznaczają wartości nie wykorzystywane przy obliczaniu elementów pokrycia. Pogrubione wartości logiczne dotyczą warunku testowego WT3 (tylko dla tych przypadków sterowanie dochodzi do decyzji z WT3).

Jak widać warunek ten przyjmuje zarówno wartość prawdy (w PT3), jak i fałszu (w PT4 oraz w PT5). Kolumny a = 0 oraz b = 0 dotyczą warunku testowego WT1. Warunki w decyzji WT1 przyjmują wszystkie możliwe kombinacje: prawda/prawda (PT1 i PT2), prawda/fałsz (PT3), fałsz/prawda (PT4) oraz fałsz/fałsz (PT5). Warunek w decyzji WT2 również przyjmuje zarówno wartość prawdy (w PT1), jak i fałszu (w PT2). Wartości dla c = 0 w PT3, PT4 i PT5 nic nie pokrywają, bo w tych przypadkach sterowanie nie dochodzi do warunku WT2. Powyższe 5 przypadków wykorzystało wszystkie elementy pokrycia. Spełniliśmy zatem w 100% kryterium pokrycia wielokrotnych warunków dla testowanej funkcji.

9.7. Testowanie warunków znaczących (MC/DC) 9.7.1. Opis metody Pokrycie warunków wielokrotnych może skutkować dużym rozmiarem suity testowej. Technika pokrycia warunków/decyzji stanowi pewne remedium, jest bowiem czymś więcej niż pokrycie decyzji oraz warunków z osobna, a jednocześnie nie dopuszcza do wykładniczego wzrostu liczby przypadków testowych. Nie jest jednak kryterium idealnym. Gdy tworzymy testy na poziomie warunków, chcielibyśmy, aby wartość logiczna równocześnie wpływ na wartość logiczną decyzji.

danego

warunku

miała

Kryterium MC/DC pozwala na spełnienie tego wymagania, wymagając mniej testów niż w przypadku pokrycia warunków wielokrotnych jednocześnie będąc silniejsze niż pokrycie warunków/decyzji. Kryterium MC/DC jest popularne w przemyśle oprogramowania awioniki. Jest rówież wprost wymagane przez normę RTCA/DO-178C [8] dla oprogramowania systemów lotniczych o znaczeniu krytycznym. Właściwość, mówiąca o warunkach, jakie muszą być spełnione, aby zmiana wartości logicznej ustalonego warunku zmieniała wartość logiczną całej decyzji, jest nazywana determinacją decyzji przez warunek. Ten ustalony warunek (na którym aktualnie się skupiamy) nazywać będziemy warunkiem znaczącym. Pozostałe warunki decyzji nazywać będziemy warunkami pobocznymi. Należy pamiętać, że fakt ustalenia warunku jako znaczącego oznacza określenie wartości logicznych wszystkich pozostałych, pobocznych warunków w decyzji.

determinacja (ang. determination) decyzji D przez warunek W zawarty w tej decyzji oznacza, że wartość logiczna D zmieni się, jeśli zmieni się wartość logiczna W Rozważmy decyzję a ∨ b złożoną z dwóch warunków. Chcemy, aby a było warunkiem znaczącym. Aby zmiana wartości a spowodowała zmianę wartości a ∨ b, warunek b musi przyjąć wartość 0 (fałszu), bo wtedy a ∨ 0 przyjmie wartość fałszu dla a = 0 i prawdy dla a = 1, czyli wartość logiczna decyzji zmieni się przy zmianie wartości warunku a. Analogicznie, w przypadku, gdy b jest warunkiem znaczącym a a pobocznym, wartość a musi być ustawiona na 0. Kryterium pokrycia warunków znaczących (MC/DC, od ang. modified condition decision coverage) wymaga spełnienia kryterium pokrycia warunków/decyzji oraz dodatkowo tego, aby dla każdej decyzji każdy warunek znaczący przynajmniej raz przyjął wartość logiczną prawdy oraz przynajmniej raz wartość fałszu. testowanie warunków znaczących, zmodyfikowane testowanie warunków/decyzji (ang. modified condition/decision testing) – białoskrzynkowa technika projektowania testów, w której przypadki testowe są projektowane tak, aby wykonywać pojedyncze warunki wejścia, które niezależnie wpływają na wyjście decyzji Zdefiniowane powyżej oryginalne kryterium MC/DC stwarza niestety kilka problemów. Pierwszy, podobnie jak w przypadku innych kryteriów logicznych, dotyczy semantyki short-circuit oraz powiązania zmiennych. Kwestię tę przedyskutowaliśmy w punktach 9.6.2 i 9.6.3. Problem drugi jest związany ze statusem warunków pobocznych – przez wiele lat w środowisku testerów były prowadzone dyskusje, czy przy zmianie wartości logicznej warunku znaczącego wartości warunków pobocznych muszą pozostać niezmienione, czy też zmiany te są dopuszczalne. Nie są to rozważania czysto teoretyczne, gdyż w wielu przypadkach wymuszenie niezmienniczości wartości warunków pobocznych prowadzi do nieosiągalnych (ang. infeasible) wymagań. Jeśli chcemy tego uniknąć, musimy w jakiś sposób osłabić kryterium. Te rozważania doprowadziły do zdefiniowania różnych wersji kryterium MC/DC. Omówimy teraz dwa z nich.

9.7.2. Dwa warianty kryterium MC/DC

Kryterium skorelowanego pokrycia warunków znaczących. Kryterium to wymaga, aby w każdej decyzji każdy warunek znaczący przyjął przynajmniej raz wartość prawdy i przynajmniej raz fałszu, przy czym wartości warunków pobocznych muszą wymuszać prawdziwość decyzji w jednym przypadku i fałszywość w drugim. Zauważmy, że tak zdefiniowane kryterium subsumuje kryterium pokrycia decyzji6, bo wymuszamy, aby każda decyzja przyjęła obie wartości logiczne. Subsumuje również kryterium pokrycia warunków, gdyż z definicji wymaga, aby każdy warunek (w momencie, gdy jest traktowany jako warunek znaczący) przyjął obie wartości logiczne. Wartość decyzji nie musi być taka sama, jak wartość warunku znaczącego. Ważne jest tylko, aby przy zmianie wartości warunku znaczącego decyzja również zmieniła wartość logiczną. Kryterium to dopuszcza inne wartościowania dla warunków pobocznych dla obu wartościowań warunku znaczącego. Rozważmy na przykład decyzję D = p ∧ (q ∨ r). Aby p zdeterminowało D, q ∨ r musi mieć wartość logiczną 1 (prawdy). Możemy to osiągnąć na trzy sposoby: (q, r) ∈ {(1, 0), (0, 1), (1, 1)}. Aby spełnić kryterium względem warunku znaczącego p, możemy np. wybrać testy (p, q, r) = (1, 1, 0) oraz (p, q, r) = (0, 0, 1). Aby q zdeterminowało D, musi być p = 1 ∧ r = 0. Zatem, aby spełnić kryterium względem warunku znaczącego q, mamy tylko jeden możliwy zestaw testów do wyboru: (p, q, r) = (1, 1, 0) oraz (p, q, r) = (1, 0, 0). Aby q zdeterminowało D, musi być p = 1 ∧ q = 0, zatem znów mamy tylko jeden możliwy zestaw testów: (p, q, r) = (1, 0, 1) oraz (p, q, r) = (1, 0, 0). W tabeli 9.17 są zebrane wszystkie kombinacje wartości logicznych warunków potrzebne do spełnienia kryterium skorelowanego pokrycia warunków znaczących. Tabela 9.17. Wartościowanie warunków dla skorelowanego pokrycia warunków znaczących

W arunek znaczący p

q

p

q

r

p ∧ (q ∨ r)

1

1

0

1

0

0

1

0

1

1

0

1

1

0

0

0

r

1

0

1

1

1

0

0

0

Szare pola oznaczają wartości warunku znaczącego. Jak widać, dla każdego z nich zmiana wartości powoduje zmianę wartości całej decyzji. W przypadku warunków znaczących q i r wartości warunków pobocznych nie zmieniają się przy zmianie wartości warunku znaczącego. W przypadku p wartości warunków pobocznych się zmieniają, ale jest to dopuszczalne przez kryterium pokrycia skorelowanego. Zauważmy, że niektóre przypadki występują więcej niż raz. Możemy zatem zredukować suitę testową, eliminując powtórzenia (patrz tab. 9.18). Takie duplikaty będą występowały zawsze. Okazuje się, że dla decyzji złożonej z n warunków potrzebnych jest nie 2 n, ale n + 1 testów, aby spełnić kryterium MC/DC. Tabela 9.18. Skorelowane pokrycie warunków znaczących – po usunięciu duplikatów

p

q

r

p ∧ (q ∨ r)

1

1

0

1

0

0

1

0

1

0

1

1

1

0

0

0

Kryterium ścisłego pokrycia warunków znaczących. Jest to oryginalna postać kryterium MC/DC. Wymaga ono, aby w każdej decyzji każdy warunek znaczący przyjął przynajmniej raz wartość prawdy i przynajmniej raz fałszu, przy czym wartości warunków pobocznych muszą być takie same dla obu wartościowań warunku znaczącego. Zauważmy, że chociaż nie jest to powiedziane wprost, kryterium to subsumuje pokrycie decyzji. Wynika to z własności warunku znaczącego – zmiana jego wartości logicznej powoduje zmianę wartości logicznej decyzji, zatem spełniając

kryterium ścisłego pokrycia warunków znaczących, wymusimy, aby każda decyzja przyjęła zarówno wartość prawdy, jak i fałszu. Rozważmy ponownie decyzję D = p ∧ (q ∨ r). W kryterium skorelowanego pokrycia, przyjmując p jako warunek znaczący, zauważyliśmy, że wartości q i r możemy wybrać na trzy sposoby, niezależnie dla każdej ewaluacji p. W kryterium ścisłego pokrycia wartości q i r również możemy wybrać na trzy sposoby, ale wybrane wartościowanie musi być takie samo dla obu ewaluacji p. Możemy wybrać na przykład wartości (p, q, r) = (1, 1, 0) oraz (p, q, r) = (0, 1, 0). Dla warunków znaczących q i r mamy tylko jedną możliwość wyboru wartości logicznych wszystkich warunków, dokładnie tak samo jak w kryterium skorelowanego

pokrycia. Ostatecznie, zbiór

przypadków testowych może

wyglądać tak jak w tabeli 9.19. Po usunięciu duplikatów, zbiór testowy zredukuje się do czterech przypadków (patrz tab. 9.20). Tabela 9.19. Wartościowania warunków dla ścisłego pokrycia warunków znaczących

Id P1 P2 Q1 Q2 R1 R2

W arunek znaczący

p

p

q

r

q

r

p ∧ (q ∨ r)

1

1

0

1

0

1

0

0

1

1

0

1

1

0

0

0

1

0

1

1

1

0

0

0

Tabela 9.20. Ścisłe pokrycie warunków znaczących – przypadki po usunięciu duplikatów

Id

p

q

r

p ∧ (q ∨ r)

1 (P1, Q1)

1

1

0

1

2 (P2)

0

1

0

0

3 (R1)

1

0

1

1

4 (Q2, R2)

1

0

0

0

9.7.3. Skorelowane a ścisłe pokrycie warunków znaczących Istnieje nieoczywista na pierwszy rzut oka, ale znacząca ze względów praktycznych, różnica między kryterium pokrycia skorelowanego a ścisłego. Okazuje się, że w sytuacji, gdy między warunkami w ramach jednej decyzji zachodzą pewne zależności, kryterium skorelowanego pokrycia może być spełnione w 100%, natomiast kryterium ścisłego pokrycia będzie miało wymagania nieosiągalne. Rozważmy następujący przykład: system, mogący pracować w trybie administratora lub użytkownika zwykłego, operuje na pliku, który może być otwarty do zapisu lub do odczytu. Załóżmy, że zachodzą następujące dwa ograniczenia: (1) Użytkownik musi być administratorem dla pliku w trybie zapisu oraz zwykłym użytkownikiem dla pliku w trybie odczytu; (2) Plik nie może być w tym samym czasie otwarty w trybie zapisu i odczytu. Zdefiniujmy następujące warunki: p = system pracuje w trybie zwykłego użytkownika; q = plik jest otwarty w trybie do zapisu; r = plik jest otwarty w trybie do odczytu. Załóżmy, że pewna akcja w systemie może być podjęta tylko wtedy, gdy operatorem jest zwykły użytkownik, a plik jest w trybie odczytu lub zapisu: D = p ∧ (q ∨ r). Jest to dokładnie ta sama decyzja, którą rozważaliśmy w punkcie 9.7.2. Ograniczenia (1) i (2) możemy wyrazić następującymi formułami logicznymi: (1) ~p ⇔ q (2) ~(q ∧ r) Ograniczenia sprawiają, że niektóre kombinacje warunków stają się nieosiągalne. Tablica prawdy7 dla predykatu D jest pokazana w tabeli 9.21 wraz

z komentarzem dotyczącym nieosiągalności. Tabela 9.21. Tablica prawdy dla predykatu D = p ∧ (q ∨ r)

Id

p

q

r

p ∧ (q ∨ c)

Uwagi

1

1

1

1 1

narus za og raniczenia (1) i (2)

2

1

1

0 1

narus za og raniczenie (1)

3

1

0

1 1

4

1

0

0 0

5

0

1

1 0

6

0

1

0 0

7

0

0

1 0

narus za og raniczenie (1)

8

0

0

0 0

narus za og raniczenie (1)

narus za og raniczenie (2)

Jedynie wiersze 3, 4 i 6 opisują poprawne kombinacje. Załóżmy, że chcemy spełnić oba kryteria dla p jako warunku znaczącego. Kryterium ścisłego pokrycia wymaga wyboru jednej z par: 2 i 6, 3 i 7 lub 1 i 5. W każdej z tych par jedna z kombinacji jest jednak niedopuszczalna. Kryterium skorelowanego pokrycia dla p można natomiast spełnić, wybierając np. kombinacje 3 i 6. W tabeli 9.22 podsumowano metodę testowania warunków znaczących. Tabela 9.22. Podsumowanie metody testowania warunków znaczących

W arunki W ierzchołki CFG reprezentujące decyzje testowe Elementy Warunki znaczące pokrycia Każdy warunek znaczący mus i przynajmniej raz przyjąć Kryterium wartoś ć prawdy i przynajmniej raz wartoś ć fałs zu; warunki pokrycia poboczne nie mog ą s ię zmieniać (ś cis łe pokrycie) lub

zmiana wartoś ci warunku znacząceg o mus i powodować zmianę wartoś ci decyzji (s korelowane pokrycie)

Pokrycie

p =

liczba pokrytych wartoś ci warunków znaczących liczba ws zys tkich wartoś ci warunków znaczących

× 100%

9.7.4. Wyznaczanie wartości warunków pobocznych dla warunku znaczącego W przykładach z poprzednich rozdziałów nie uzasadniliśmy formalnie, skąd braliśmy wartości warunków pobocznych tak, aby ustalony warunek stał się rzeczywiście warunkiem znaczącym. Opiszemy teraz metodę, która rozwiązuje ten problem. Wykorzystuje ona operator logiczny XOR, czyli alternatywę rozłączną, oznaczaną symbolem ⊕. Alternatywa rozłączna p ⊕ q jest zdaniem prawdziwym wtedy i tylko wtedy, gdy dokładnie jedno spośród p i q jest prawdziwe. Tablica prawdy dla operatora XOR podana jest w tabeli 9.23. Tabela 9.23. Tablica prawdy dla operatora XOR

p

q

p⊕ q

1

1

0

1

0

1

0

1

1

0

0

0

Niech D będzie decyzją zawierającą warunek W. Przez DW=1 (odp. DW=0) będziemy oznaczać decyzję D, w której warunek W przyjął odpowiednio wartość prawdy lub fałszu. Przez DW będziemy oznaczać formułę logiczną opisującą warunki, jakie muszą zajść, aby W było warunkiem znaczącym. Dla wszystkich wartościowań warunków takich, że DW jest prawdą, warunek W jest znaczący. Na przykład, jeśli DW = U ∧ X, gdzie U, X są warunkami wchodzącymi w skład D, aby

W był znaczący, U ∧ X musi być prawdą, tzn. zarówno warunek U, jak i X muszą być prawdziwe. Formuła DW jest wyznaczona przez połączenie operatorem XOR dwóch wersji D: w jednej W jest prawdą, a w drugiej – fałszem: DW = DW=1 ⊕ DW=0 Rozważmy teraz 4 przykłady, analizując cztery możliwe postaci, jakie może przyjąć DW. W każdym z nich chcemy określić warunki, jakie muszą być spełnione, aby p był warunkiem znaczącym. Przykład 1. Niech D = p ∨ q. Wtedy Dp = Dp=1 ⊕ Dp=0 = (1 ∨ q) ⊕ (0 ∨ q) = 1 ⊕ q = ~q Ostatnia równość wynika stąd, że XOR jest prawdą tylko wtedy, gdy dokładnie jeden z jego warunków jest prawdziwy, zatem 1 ⊕ q będzie prawdą, gdy q będzie fałszem i na odwrót – 1 ⊕ q będzie fałszem, gdy q będzie prawdą. Aby p było warunkiem znaczącym ~q, musi być prawdą, zatem q musi przyjąć wartość 0. Przykład 2. Niech D = p ⇔ q8. Wtedy Dp = Dp=1 ⊕ Dp=0 = (1 ⇔ q) ⊕ (0 ⇔ q) = q ⊕ ~q = 1 Formuła Dp jest tautologią, tzn. zawsze przyjmuje wartość logiczną prawdy, niezależnie od wartości jej klauzul. Oznacza to, że p jest warunkiem znaczącym w D niezależnie od tego, jaka jest wartość q. Przykład 3. Niech D = q ∧ p ∨ q ∧ ~p. Tak naprawdę ta decyzja to D = q, warunek p jest tu irrelewantny. Wtedy Dp = Dp=1 ⊕ Dp=0 = (q ∧ 1 ∨ q ∧ 0) ⊕ (q ∧ 0 ∨ q ∧ 1) = (q ∨ 0) ⊕ (0 ∨ q) = q ⊕ q = 0 Otrzymaliśmy wynik przeciwny niż w Przykładzie 2 – wartość Dp jest zawsze fałszywa. Oznacza to, że nie istnieje takie wartościowanie pozostałych warunków, aby p był warunkiem znaczącym. Przykład 4. Niech D = q ∧ (p ∨ r). Wtedy Dp = Dp=1 ⊕ Dp=0 = (q ∧ (1 ∨ r)) ⊕ (q ∧ (0 ∨ r)) = (q ∧ 1) ⊕ (q ∧ r) = q ⊕ (q ∧ r) = q ∧ ~r Ostatni krok może nie być oczywisty. Aby XOR miał wartość 1, q oraz q ∧ r muszą mieć odmienne wartości logiczne. Jeśli q = 1, to r musi być 0, aby q ∧ r = 1 ∧

r było fałszem. Jeśli q = 0, to każda wartość r da w rezultacie fałsz XORa, bo oba argumenty będą równe 0. Aby p był warunkiem znaczącym, wartość q ∧ ~r musi być prawdą, a to zachodzi tylko dla q = 1 oraz r = 0.

9.7.5. Przykład Rozważmy ponownie funkcję leapYear z listingu 9.4. Występuje tam predykat ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0) Oznaczmy poszczególne warunki tej decyzji następująco: p := year%4 = 0 q := year%100 ≠ 0 r := year%400 = 0 Chcemy przetestować decyzję (p ∧ q) ∨ r za pomocą kryterium MC/DC w wersji ścisłej. Załóżmy, że kompilator nie stosuje semantyki short-circuit. Występują natomiast pewne ograniczenia między zmiennymi: (1) jeśli r = 1, to p = 1 i q = 0; (2) jeśli q = 0, to p = 1; (3) jeśli q = 1, to r = 0; (4) jeśli p = 0, to q = 1 i r = 0. Krok 1. Określenie przedmiotu i elementów testów. Przedmiotem (i jednocześnie elementem) testów jest funkcja leapYear. ET1: leapYear. Krok 2. Wyprowadzenie warunków testowych. Warunkiem testowym jest decyzja występująca w instrukcji warunkowej. WT1: ((year % 4 = 0) ∧ (year % 100 ≠ 0)) ∨ (year % 400 = 0) (dla ET1).

Krok 3. Wyprowadzenie elementów pokrycia. Zanim wyprowadzimy elementy pokrycia zobaczmy, które kombinacje warunków są w ogóle dopuszczalne. W tabeli 9.24 pokazano tablicę prawdy dla decyzji WT1 z uwagami o naruszeniu podanych wcześniej czterech ograniczeń. Dozwolone są jedynie kombinacje nr 2, 3, 4 i 6. Elementy pokrycia są następujące (wszystkie dla WT1): EP1: p = 0 i p jest warunkiem znaczącym; EP4: q = 1 i q jest warunkiem znaczącym; EP2: p = 1 i p jest warunkiem znaczącym; EP5: r = 0 i r jest warunkiem znaczącym; EP3: q = 0 i q jest warunkiem znaczącym; EP6: r = 1 i r jest warunkiem znaczącym. Obliczmy

formuły

opisujące

wartości

warunków

pobocznych

poszczególnych warunków znaczących: DP = (1 ∧ q) ∨ r ⊕ (0 ∧ q) ∨ r = (q ∨ r) ⊕ r = q ∧ ~r Dq = (p ∧ 1) ∨ r ⊕ (p ∧ 0) ∨ r = (p ∨ r) ⊕ r = p ∧ ~r

Dr = (p ∧ q) ∨ 1 ⊕ (p ∧ q) ∨ 0 = 1 ⊕ (p ∧ q) = ~(p ∧ q) Tabela 9.24. Tablica prawdy dla predykatu funkcji leapYear

Lp.

p q r (p ∧ q) ∨ r

Uwagi

1

1 1 1 1

narus za og raniczenia (1) i (3)

2

1 1 0 1

3

1 0 1 1

4

1 0 0 0

5

0 1 1 1

6

0 1 0 0

7

0 0 1 1

narus za og raniczenia (1), (2) i (4)

8

0 0 0 0

narus za og raniczenia (2) i (4)

narus za og raniczenia (1), (3) i (4)

dla

Aby p było warunkiem znaczącym (EP1 i EP2), musi być q = 1 ∧ r = 0. Dla EP1 i EP2 możemy wybrać tylko kombinacje nr 6 i 2. Pokrywają one kryterium MC/DC w wersji ścisłej dla p jako warunku znaczącego. Aby q było warunkiem znaczącym (EP3 i EP4), musi być p = 1 ∧ r = 0. Dla EP3 i EP4 możemy wybrać tylko kombinacje nr 4 i 2. Pokrywają one kryterium MC/DC w wersji ścisłej dla q jako warunku znaczącego. Aby r było warunkiem znaczącym (EP5 i EP6), są dopuszczalne wszystkie kombinacje p i q za wyjątkiem p = q = 1. Wybór kombinacji nr 4 i 3 spełnia kryterium MC/DC w wersji ścisłej dla r jako warunku znaczącego. Krok 4. Zdefiniowanie przypadków testowych. Z analizy przeprowadzonej w Kroku 3. wynika, że musimy wykorzystać wszystkie cztery dopuszczalne kombinacje warunków, tak jak to pokazano w tabeli 9.25. Tabela 9.25. Przypadki testowe spełniające kryterium MC/DC dla funkcji leapYear

q := year%100 ≠0

r := year%400 =0

D

PT1 2008 1

1

0

1 EP2, EP4

PT2 1600 1

0

1

1 EP6

PT3 1500 1

0

0

0 EP3, EP5

PT4 1999 0

1

0

0 EP1

Id

year

p := year%4 =0

Pokryte EP

Kryterium MC/DC w wersji ścisłej udało nam się pokryć w 100%, mimo istnienia zależności między warunkami. W każdej kolumnie wartości w szarych komórkach odpowiadają wartościom warunku znaczącego. Czytelnik może sprawdzić, że w odpowiednich wierszach wartości logiczne warunków pobocznych są niezmienne (np. dla warunku znaczącego p odpowiadające mu wiersze to PT1 i PT4, w których warunki q i r mają odpowiednio wartości 1 oraz 0).

9.8. Pokrycie MUMCUT oraz kryteria z nim związane

9.8.1. Opis metody Kryterium MUMCUT zostało zdefiniowane w pracy [118] jako strategia oparta na defektach, która gwarantuje znalezienie określonych typów błędów w specyfikacji wyrażonej w języku logiki. W podejściu tym zakłada się, że każda zmienna w wyrażeniu logicznym może przyjąć wartość logiczną niezależnie od wartości innych zmiennych w tym wyrażeniu. Można pokazać, że każdy predykat da się zapisać w tzw. dysjunktywnej postaci normalnej DNF (ang. Disjunctive Normal Form), która ma postać alternatywy koniunkcji klauzul. Przykładem formuły w postaci DNF jest formuła ab + cde + bef + af gdzie a, b, c, d, e, f są klauzulami, operacja mnożenia oznacza koniunkcję, a symbol + oznacza alternatywę. Predykat w postaci DNF można zawsze zapisać w tzw. nieredundantnej dysjunktywnej postaci normalnej IDNF (ang. Irredundant Disjunctive Normal Form), tzn. w postaci DNF, gdzie żadna z klauzul nie jest redundantna. Klauzula k jest redundantna dla predykatu P, jeśli P da się zapisać bez użycia k. Na przykład, predykat (ab + a)c można zapisać równoważnie jako ac – klauzula b jest redundantna. Jej wartość logiczna nie ma wpływu na wartość logiczną predykatu. Niech P będzie predykatem, t przypadkiem testowym a p koniunkcją klauzul. Przez P(t) (odpowiednio p(t)) oznaczać będziemy wartość logiczną predykatu P (odpowiednio wartość logiczną koniunkcji klauzul p) dla wartości logicznych zmiennych określonych w t. Przypadek testowy t dla predykatu P = p 1 + p 2 +···+ p m zapisanego w postaci IDNF nazywamy punktem prawdy (odpowiednio punktem fałszu), jeśli P(t) = 1 (odpowiednio P(t) = 0). Jeśli jednak tylko dla jednego p i zachodzi p i(t) = 1, a dla każdego j ≠ i mamy p j(t) = 0, to t jest nazywany unikalnym punktem

prawdy. Jeśli p i = x1, x2, …, xn, gdzie xj są klauzulami, to przez p i,–k oznaczać będziemy predykat otrzymany z p i przez zanegowanie k tej klauzuli xk . Przypadek testowy t nazwiemy punktem prawie fałszywym klauzuli xk w p i wchodzącego w skład predykatu P, jeśli P(t) = 0, ale p i,–k (t) = 1. Jesteśmy teraz gotowi do tego, by zdefiniować kryterium MUMCUT oraz inne, powiązane z nim kryteria. Zakładamy, że kryteria są stosowane do predykatu P = p 1 + p 2 + … + p m zapisanego w IDNF, tzn. każdy p i ma postać klauzulami.

gdzie xj są

9.8.2. Kryteria MUTP, MNFP, CUTPNFP i MUMCUT Pokrycie MUTP (ang. Multiple Unique True Point) – dla każdego i zbiór przypadków testowych zawiera unikalne punkty prawdy dla p i takie, że wszystkie możliwe wartości logiczne (prawdy i fałszu) każdej zmiennej niewystępującej w p i są pokryte. Pokrycie MNFP (ang. Multiple Near False Point) – dla każdych i, j zbiór przypadków testowych zawiera punkty prawie fałszywe dla j-tej klauzuli składnika p i takie, że wszystkie możliwe wartości logiczne (prawdy i fałszu) każdej zmiennej niewystępującej w p i są pokryte. Pokrycie CUTPNFP (ang. Corresponding Unique True Point And Near False Point) – dla każdych i, j dla których jest to możliwe zbiór przypadków testowych T zawiera unikalny punkt prawdy t ∈ T dla p i oraz punkt prawie fałszywy t′ ∈ T dla

j-tej klauzuli w p i takie, że t i t′ różnią się tylko na wartości logicznej j-tej klauzuli w p i. Taką parę testów t i t′ będziemy nazywać UN-parą. Pokrycie MUMCUT (ang. MUTP, MNFP and CUTPNFP criteria) – zbiór przypadków testowych spełnia MUMCUT, gdy spełnia jednocześnie kryteria MUTP, MNFP oraz CUTPNFP.

9.8.3. Przykład Rozważmy predykat P = ab + cd, czyli alternatywę dwóch koniunkcji, (a ∧ b) ∨ (c ∧ d), gdzie a, b, c, d są klauzulami. Każdy przypadek testowy składa się z czterech wartości logicznych odpowiadających wartościom klauzul a, b, c, d. Dla wygody przypadki zapisywać będziemy jako ciąg czterech wartości logicznych, np. t = 0100 oznacza przypadek, w którym b ma wartość logiczną prawdy, a pozostałe klauzule – wartość logiczną fałszu. Zbiór przypadków testowych T = {1101, 1110, 0111, 1011} spełnia kryterium MUTP, ponieważ: dwa pierwsze przypadki, 1101 oraz 1110 są unikalnymi punktami prawdy dla pierwszego składnika P, czyli termu ab i rozważane razem pokrywają wszystkie możliwe wartości logiczne dla predykatów niewystępujących w ab, czyli dla c i d; dwa pozostałe przypadki, 0111 oraz 1011 są unikalnymi punktami prawdy dla drugiego składnika P, czyli termu cd i rozważane razem

pokrywają wszystkie możliwe wartości logiczne dla predykatów niewystępujących w cd, czyli dla a i b. Zbiór przypadków testowych T = {0101, 0110, 1001, 1010} spełnia kryterium MNFP, ponieważ: przypadki 0101, 0110 są punktami prawie fałszywymi dla a i pokrywają wszystkie wartości logiczne klauzul niewystępujących pierwszym składniku P, czyli klauzul c i d;

w

ab



przypadki 1001, 1010 są punktami prawie fałszywymi dla b i pokrywają wszystkie wartości logiczne klauzul niewystępujących w ab – pierwszym składniku P, czyli klauzul c i d; przypadki 0101, 1001 są punktami prawie fałszywymi dla c i pokrywają wszystkie wartości logiczne klauzul niewystępujących w cd – drugim składniku P, czyli klauzul a i b; przypadki 0110, 1010 są punktami prawie fałszywymi dla d i pokrywają wszystkie wartości logiczne klauzul niewystępujących w cd – drugim składniku P, czyli klauzul a i b. Zbiór przypadków testowych T = {1100, 0100, 1000, 0011, 0001, 0010} spełnia kryterium CUTPNFP, ponieważ: przypadki {1100, 0100} tworzą UN-parę dla pierwszego składnika ab, czyli dla a; przypadki {1100, 1000} tworzą UN-parę dla drugiego składnika ab, czyli dla b; przypadki {0011, 0001} tworzą UN-parę dla pierwszego składnika cd, czyli dla c; przypadki {0011, 0010} tworzą UN-parę dla drugiego składnika cd, czyli dla d. Zbiór przypadków testowych T = {0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, 1010, 1011, 1100, 1101, 1110} spełnia MUMCUT, ponieważ jest sumą mnogościową przypadków spełniających kryteria MUTP, MNFP i CUTPNFP.

9.8.4. Zdolność metody MUMCUT do wykrywania określonych typów błędów

Opiszemy teraz kilka typów błędów, dla których – jeśli zostaną popełnione – stosowanie kryterium MUMCUT będzie gwarantowało ich wykrycie z definicji lub jeśli będą spełnione pewne dodatkowe, określone warunki. Jako przykład rozważmy następujący predykat, o którym zakładamy, że jest poprawny: , gdzie oznacza negację x. Poniższe rozważania są poprawne tylko dla predykatów zapisanych w postaci DNF. Jak wspomnieliśmy wcześniej, każdy predykat można przekształcić do tej postaci. ENF – błąd negacji wyrażenia (ang. Expression Negation Fault) – wyrażenie lub jego część jest zastąpiona jego negacją, np. P jest zastąpione przez TNF – błąd negacji termu (ang. Term Negation Fault) – term (tu: koniukcja klauzul) jest zastąpiony przez jego negację, np. P jest zastąpione przez TOF – błąd pominięcia termu (ang. Term Omission Fault) – term jest ominięty, np. P jest zastąpione przez

;

ORF – błąd operatora (ang. Operator Reference Fault) – operator alternatywy jest zastąpiony operatorem koniunkcji lub na odwrót, np. P jest zastąpione przez

albo np.

;

LNF – błąd negacji klauzuli (ang. Literal Negation Fault) – klauzula jest zastąpiona przez jej negację, np. P jest zastąpione przez

; ten

błąd często jest zwany błędem negacji zmiennej [119]; LOF – błąd pominięcia klauzuli (ang. Literal Negation Fault) – klauzula jest pominięta, np. P jest zastąpione przez

;

LIF – błąd nadmiarowej klauzuli (ang. Literal Insertion Fault) – do predykatu jest dodana klauzula, np. P jest zastąpione przez ; LRF – błąd zamiany klauzuli (ang. Literal Reference Fault) – klauzula jest zamieniona na inną, np. P jest zastąpione przez

; ten błąd

często jest określany jako błąd zamiany zmiennej; STF – błąd utknięcia (ang. Stuck-At Fault) – błąd powodujący stałą wartość klauzuli, np. gdy a ma zawsze wartość 1, P będzie równoważna , czyli będzie równoważna

; jeśli a będzie miało zawsze wartość 0, P .

Badania empiryczne pokazują, że rzeczywiste awarie w oprogramowaniu są w dużej mierze powodowane przez pomyłki programistów polegające na dodaniu lub usunięciu warunków w predykatach, a także użyciu niepoprawnych operatorów bądź operandów [10], [120], [121]. Kryterium MUMCUT dobrze nadaje się do wykrywania wymienionych błędów. Wiadomo, że zastosowanie MUMCUT dla predykatów w postaci IDNF gwarantuje wykrycie wszystkich dziewięciu wymienionych

typów

błędów.

MUMCUT

subsumuje

kryterium

pokrycia

warunków/decyzji. Ponadto, pod pewnymi warunkami subsumuje również kryterium MC/DC.

9.9. Testowanie pętli 9.9.1. Opis metody Jeśli program zawiera przynajmniej jedną pętlę, to liczba możliwych ścieżek wykonania jest potencjalnie nieskończona. Stwarza to problemy z wyczerpującym testowaniem ścieżek, bo po prostu jest to fizycznie niemożliwe. Jak zatem poradzić sobie z istnieniem w CFG pętli? Najprostsze podejście polega na dostarczeniu takich przypadków testowych, aby każda pętla wykonała się 0 razy oraz dokładnie raz9. Niektórzy autorzy dodają jeszcze warunek, aby pętla wykonała się więcej niż raz, przy czym zwykle dokładna liczba wykonań pętli nie jest sprecyzowana. Wynika to stąd, że w praktyce bardzo trudno jest dostarczyć dane wejściowe, które spowodują wykonanie danej pętli konkretną liczbę razy (najłatwiej zwykle wymusić wykonanie pętli 0 razy i 1 raz). W literaturze można spotkać także wymaganie, aby pętla była przetestowana maksymalną możliwą liczbę razy (jeśli znamy tę liczbę) [80]. Boris Beizer idzie jeszcze dalej. W swojej książce [14] proponuje następujące kryteria co do testowania pętli (zauważmy, że stosuje tu podejście trzech wartości granicznych, wykorzystując technikę analizy wartości brzegowych do liczby przebiegów pętli): jeśli to możliwe, przetestuj pętlę dla wartości mniejszej o 1 od najmniejszej wartości zmiennej iterującej; np. jeśli iterator w pętli może przebiegać od 0 do 100, spróbuj wymusić wartość –1 iteratora;

przetestuj minimalną liczbę iteracji (zwykle 0 lub – w przypadku dowhile – 1); przetestuj dla liczby iteracji większej o 1 od minimalnej liczby iteracji; przetestuj pętlę raz (jeśli jest to przypadek redundantny, pomiń go); przetestuj pętlę dwa razy (jeśli jest to przypadek redundantny, pomiń go); przetestuj pętlę dla typowej wartości przebiegów; przetestuj pętlę dla liczby przebiegów mniejszej o 1 od maksymalnej wartości; przetestuj pętlę dla maksymalnej liczby przebiegów; przetestuj pętlę dla liczby przebiegów większej o 1 od maksymalnej wartości. Jak widać, Beizer kładzie duży nacisk na testowanie negatywne, tzn. na testowanie pętli dla przebiegów, których liczba jest nieprawidłowa (np. ujemna lub większa od maksymalnej). W praktyce nie wszystkie z powyższych kryteriów mogą być osiągalne. Jeśli któregoś z nich nie da się wymusić, to należy go po prostu pominąć. Warunkami testowymi są pętle występujące w programie. Elementy pokrycia określają wymagane liczby iteracji każdej z pętli, a ich postać zależy od przyjętej metodologii. W tabeli 9.26 podsumowano metodę testowania pętli. Tabela 9.26. Podsumowanie metody testowania pętli

W arunki testowe

Pętle

Elementy pokrycia

Liczba wykonania każdej z pętli

Kryterium pokrycia

Zależnie od metody; każda pętla powinna być wykonana okreś loną liczbę razy

Pokrycie

p =

liczba pokrytych wykonań pętli liczba ws zys tkich wymag anych wykonań pętli

× 100%

9.9.2. Pętle zagnieżdżone W przypadku pętli zagnieżdżonych (jedna pętla w drugiej) Beizer proponuje następujące podejście: 1) rozpocznij od najbardziej wewnętrznej pętli, ustawiając wszystkie pętle zewnętrzne wobec niej na minimalną liczbę iteracji (tak, żeby pętla wewnętrzna mogła się wykonać); 2) przetestuj warunki brzegowe (wg 9 kryteriów podanych powyżej) dla pętli wewnętrznej; 3) jeśli przetestowałeś najbardziej zewnętrzną pętlę, idź do punktu 5.; 4) kontynuuj proces opisany powyżej dla każdej pętli z osobna, dopóki nie przetestujesz wszystkich pętli; 5) przetestuj warunki brzegowe dla wszystkich pętli naraz, tzn. postaraj się wymusić dla wszystkich pętli przebieg 0 iteracji, 1 iterację, maksymalną liczbę iteracji i liczbę o 1 większą od maksymalnej liczby iteracji. W praktyce spełnienie punktu 5. może być trudne lub niemożliwe. Można ten warunek nieco osłabić, wymagając np. maksymalnej liczby iteracji dla tak wielu pętli, jak to jest możliwe.

9.9.3. Testowanie wzorców pętli Bardzo ciekawa metoda testowania pętli – wykorzystująca tzw. wzorce pętli – jest podana w [122]. Pozwala ona w sposób systematyczny tworzyć przypadki testowe dla pętli zagnieżdżonych, zakładając, że obliczenia wszystkich ścieżek testowanego programu są liniowe. Wykorzystując metody algebry, w sposób analogiczny do metody punktów ON-OFF oblicza się minimalną liczbę testów10 potrzebnych do przetestowania każdej sekwencji, czyli ścieżki zawierającej pętlę. Sekwencje identyfikuje się przez budowę drzewa zagnieżdżeń. Jego pierwszy poziom stanowią ścieżki proste, czyli ścieżki biegnące od wierzchołka początkowego do końcowego i niezawierające pętli, a kolejne poziomy reprezentują cykle proste, których poziom zagnieżdżenia jest równy numerowi poziomu. Na rysunku 9.4 jest pokazany przykładowy CFG oraz jego drzewo zagnieżdżeń.

Rysunek 9.4. CFG i jego drzewo zagnieżdżeń pętli Drzewo zagnieżdżeń umożliwia identyfikację tzw. ścieżek iterowanych, czyli ścieżek zawierających pętle. Na przykład dla drzewa z rysunku 9.4 mamy cztery ścieżki iterowane: S1 = 1 (2 3 4)* 2 3 4 5 S2 = 1 2 (3 6)* 3 4 5 S3 = 1 (2 3 4)* 2 3 6 7 S4 = 1 2 (3 6)* 3 6 7 gdzie (X)* oznacza dowolną liczbę powtórzeń X (0 lub więcej). Metoda testowania wzorców pętli składa się z 4 kroków: znajdź w CFG wszystkie ścieżki proste i cykle; zbuduj drzewo zagnieżdżeń dla pętli w celu identyfikacji wszystkich ścieżek iterowanych; oblicz , gdzie k to liczba zmiennych wejściowych, a m – liczba zmiennych programu11;

dla każdej sekwencji wykonaj n różnych ścieżek, wliczając w to ścieżkę bazową. Zobaczmy na przykładzie działanie metody. Rozważmy ponownie CFG z rysunku 9.4 wraz z jego drzewem zagnieżdżeń. Załóżmy, że mamy k = 3 wartości wejściowe oraz m = 2 zmienne programu oraz że obliczenia ścieżek są liniowe. Mamy

Dla każdej sekwencji ścieżek iterowanych musimy zatem wykonać

2 ścieżki, wliczając w to ścieżki bazowe. Zbiór testowy powinien zatem zawierać 6 elementów, odpowiadających ścieżkom testowym pokazanym w tabeli 9.27. Tabela 9.27. Pokrycie ścieżek iterowanych w metodzie wzorców pętli

Id

Ścieżka testowa

Pokryte ścieżki iterowane

1

(1 2 3 4 5)

S1, S2 (obie po raz pierws zy)

2

(1 2 3 6 7)

S3, S4 (obie po raz pierws zy)

3

(1 2 3 4 2 3 4 5)

S1 (po raz drug i)

4

(1 2 3 6 3 4 5)

S2 (po raz drug i)

5

(1 2 3 4 2 3 6 7)

S3 (po raz drug i)

6

(1 2 3 6 3 6 7)

S4 (po raz drug i)

Metoda ma dwa główne ograniczenia. Pierwsze dotyczy wykonywalności ścieżek. Liczba obiegów jednej pętli może zależeć od innej. W przypadku problemów z uzyskaniem określonej liczby przebiegów pętli należy posłużyć się drzewem zagnieżdżeń w inny sposób. W każdym jego węźle należy zdefiniować licznik zliczający, ile razy program wszedł w dany cykl prosty. Test kończymy w momencie, gdy w każdym węźle licznik osiągnie wartość n określoną wzorem zdefiniowanym powyżej. Drugie ograniczenie dotyczy założenia o liniowości obliczeń. Można ten problem rozwiązać tak, że testowaniu podlegać będzie liniowa część obliczeń. W praktyce jednak okazuje się że np. większość obliczeń sporej części oprogramowania do zastosowań inżynierskich opiera się na aproksymacji liniowej [123].

9.9.4. Przykład Rozważmy ponownie program SortBąbelkowe z listingu 2.1. Dla wygody jest on przedstawiony ponownie na listingu 9.6. Załóżmy, że chcemy go przetestować metodą testowania pętli w wersji Beizera.

1 function SortBąbelkowe(int T[]) 2 n=liczba_elementów_T 3 do 4 5 6

for (i=0; iT[i+1]) then zamień miejscami T[i] z T[i+1]

7 8

end if end for

9 n=n-1 10 while (n>1) 11 return T 12 end Listing 9.6. Powtórzony kod programu SortBąbelkowe Zanim zaczniemy proces konstrukcji przypadków testowych, przeanalizujmy kod programu pod kątem występujących w nim pętli. Po pierwsze, pętla do-while zawsze wykona się co najmniej raz. Po drugie, zauważmy, że liczba przebiegów wewnętrznej pętli for zależy od zmiennej n, która zmniejsza się o 1 w każdym przebiegu do-while. Taka zależność pętli wewnętrznej od zewnętrznej jest bardzo częsta. Oznacza to, że nie da się obu tych pętli traktować niezależnie i prawdopodobnie niektóre wymagania będą nieosiągalne. Po trzecie, pętla for charakteryzuje się ustaloną z góry liczbą przebiegów, zatem nie możemy manipulować maksymalną czy minimalną liczbą jej wykonań. Liczba ta zależy wprost od wartości zmiennej n ustalanej w pętli zewnętrznej. Załóżmy ponadto, że maksymalny rozmiar tablicy T to MAXINT elementów. Krok 1. Określenie przedmiotu i elementów testów. Elementem testów jest program SortBębelkowe.

ET1: SortBąbelkowe. Krok 2. Wyprowadzenie warunków testowych. Warunkami testowymi są pętle, zatem mamy następujące dwa warunki testowe: WT1: pętla do-while w liniach 3.–10. (dla ET1); WT2: pętla for w liniach 4.–8. (dla ET1). Krok 3. Wyprowadzenie elementów pokrycia. Stosujemy metodologię Beizera. Wymóg ustawienia liczby iteracji na o jeden mniejszy niż wartość minimalna oraz o jeden większy niż maksymalna jest nieosiągalny dla obu pętli. Biorąc pod uwagę to oraz fakt zagnieżdżenia jednej pętli w drugiej, mamy następujące elementy pokrycia: EP1: wykonanie do-while 1 raz i EP2: wykonanie do-while 1 raz i EP3: wykonanie do-while 1 raz i EP4: wykonanie do-while 1 raz i EP5: wykonanie do-while 2 razy

for 0 razy (dla WT2); for 1 raz (dla WT2); for 2 razy (dla WT2); for typową liczbę razy, np. 45 (dla WT2); (dla WT1);

EP6: wykonanie do-while typową liczbę razy, np. 32 (dla WT1); EP7: wykonanie do-while MAXINT – 1 razy (dla WT1); EP8: wykonanie do-while MAXINT razy (dla WT1). EP1–EP4 odnoszą się do warunków z reguły 1. Beizera dla pętli zagnieżdżonych i dotyczą pętli wewnętrznej. EP5–EP8 to pozostałe warunki, dotyczące pętli zewnętrznej. Warunki opisane w punkcie 4. reguł Beizera dla pętli zagnieżdżonych mogą być spełnione tylko w przypadku wykonania obu pętli dokładnie raz, maksymalną liczbę razy lub o jeden mniej niż maksymalną liczbę razy. Przypadki te uwzględnione są w elementach pokrycia EP2, EP7 i EP8. Choć EP7 i EP8 nie nakładają wprost tych warunków dla pętli for, ze względu na strukturę pętli opisaną powyżej wymuszenie wykonania pętli while n razy spowoduje w pierwszym jej przebiegu wykonanie takiej samej liczby iteracji w pętli for. EP3 i EP4 są nieosiągalne, ponieważ pętla for zawsze wykona się co najwyżej tyle razy, ile pętla while.

Krok 4. Zdefiniowanie przypadków testowych. Dobór wartości wejściowych w przypadku naszych pętli jest prosty: rozmiar tablicy T narzuca wprost liczbę wykonań pętli while. Należy jednak pamiętać, że nie zawsze da się w prosty sposób wymusić określoną liczbę wykonań każdej z pętli występujących w programie. Przypadki testowe spełniające kryterium pokrycia pętli są podane w tabeli 9.28. Tabela 9.28. Przypadki testowe dla pokrycia pętli w programie SortBąbelkowe

Oczekiwane wyjście

Liczba wykonań while

Liczba wykonań for

Pokryte EP

PT1 T = [5]

[5]

1

0

EP1

PT2 T = [1, 2]

[1, 2]

1

1

EP2

PT3 T = [3, 2, 2]

[2, 2, 3]

2

2, 1

EP5

Id

wejście

PT4

T = [0, 1, 2, … , 32]

[0, 1 ,… , 32]

32

32, 31, … , 1 EP6

PT5

T = [1, … , MAXINT]

[1, … , MAXINT]

MAXINT – 1

MAXINT – 1, … , 1

EP7

PT6

T = [0, … , MAXINT]

błąd (zakres T)





EP8

Element EP8 może nie zostać pokryty w sensie wymuszenia określonej liczby przebiegów pętli, jeśli np. program zgłosi błąd przekroczenia rozmiaru tablicy. Awaria jest jednak również jakąś odpowiedzią programu. Jako testerzy chcieliśmy wymusić wykonanie MAXINT razy pętli while, co skutkowało błędem wykonania. W tym sensie możemy uznać EP8 za pokryty.

9.10. Liniowa sekwencja kodu i skok (LSKiS) 9.10.1. Opis metody

Liniowa sekwencja kodu i skok (ang. Linear Code Sequence And Jump, LCSAJ) jest białoskrzynkową metodą wymagającą pokrycia ścieżek związanych ze skokiem sterowania. Można ją zaliczyć do rodziny metod testowania ścieżek opisanych w punktach 9.10.3–9.12.6. Tak jak metoda pokrycia pętli, LSKiS jest próbą zdefiniowania sensownego kryterium pokrycia w sytuacji występowania pętli stwarzających potencjalnie nieskończoną liczbę możliwych ścieżek w programie. Najważniejsze pojęcie w metodzie LSKiS to skok. Jest to sterowanie, które przechodzi z linii x do linii y kodu, przy czym y ≠ x + 1, to znaczy linie te nie następują bezpośrednio po sobie. Skok może nastąpić zarówno w przód (np. jeśli warunek pętli while nie jest spełniony, to przeskakujemy całą pętlę do pierwszej linii po końcu struktury while), jak i w tył (np. po zakończeniu wykonywania pętli wracamy z powrotem do jej początku, aby sprawdzić warunek pętli).

Rysunek 9.5. Symboliczna reprezentacja LSKiS Liniowa sekwencja kodu i skok to ścieżka, w której budowie można wyróżnić następujące trzy elementy (patrz rys. 9.5): początek – początek modułu lub miejsce, do którego może nastąpić skok; koniec – koniec modułu lub miejsce, z którego może nastąpić skok; cel – miejsce, do którego następuje skok. Wszystkie trzy elementy to linie kodu. Powszechnie przyjętą konwencją jest odwoływanie się do nich za pomocą numerów linii kodu. Należy tylko pamiętać, aby w jednej linii kodu znajdowała się jedna instrukcja. Dzięki stosowaniu tej konwencji łatwo jest określić miejsca, w których następuje skok. Hipoteza błędu w technice LSKiS polega na tym, że błędy mogą powstawać podczas skoków, czyli w wyniku zaburzeń sekwencyjnego wykonania kodu. Warunkami testowymi i jednocześnie elementami pokrycia są trójki liczb (p, k, s) wyznaczające poszczególne ścieżki LSKiS, gdzie p, k, s oznaczają odpowiednio numery linii: początkowej, końcowej i docelowej po skoku. liniowa sekwencja kodu i skok, LSKiS (ang. Linear Code Sequence and Jump, LCSAJ) – sekwencja instrukcji określana przez trzy miejsca w kodzie, zwyczajowo identyfikowane numerami linii kodu źródłowego: rozpoczęcie liniowej sekwencji wykonywanych instrukcji, koniec sekwencji liniowej i docelowa linia, do której wykonywanie programu jest przekazywane po zakończeniu liniowej sekwencji LSKiS subsumuje kryterium pokrycia gałęzi oraz decyzji12. Metoda LSKiS jest krytykowana przede wszystkim ze względu na następujące kwestie: w praktyce wiele elementów pokrycia jest nieosiągalnych bez zmiany inicjalizacji zmiennych lub wymuszenia sterowania przez zmianę wartości zmiennych podczas działania programu przy użyciu debugera13; niewielkie zmiany w kodzie mogą radykalnie zmienić postać LSKiS, co sprawia, że metoda jest dosyć kosztowna w utrzymaniu;

typowe wykonanie programu zwykle pokrywa wiele LSKiS, co powoduje, że technika jest „przedobrzona”, tzn. kryteria pokrycia, określone w skomplikowany sposób, dają się pokryć bardzo niewielką liczbą testów. Wysiłek włożony w obliczenie tych LSKiS może więc stać pod znakiem zapytania. Ponadto, dla LSKiS postaci (a, b, c) jego spełnienie zagwarantuje również spełnienie każdego LSKiS postaci (x, b, c) gdzie a < x ≤ b. Niektóre LSKiS są więc nadmiarowe. pokrycie LSKiS (ang. coverage, LCSAJ) – odsetek LSKiS modułu, które zostały wykonane przez zestaw przypadków testowych; pokrycie 100% LSKiS oznacza pokrycie 100% decyzji W tabeli 9.29 podsumowano metodę testowania LSKiS. Tabela 9.29. Podsumowanie metody testowania LSKiS

W arunki testowe

Liniowe sekwencje kodu i skoku

Elementy pokrycia

Liniowe s ekwencje kodu i s koku

Kryterium pokrycia

Każda liniowa s ekwencja kodu i s kok powinna zos tać wykonana

Pokrycie

p=

liczba pokrytych LS KiS liczba ws zys tkich LS KiS

× 100%

9.10.2. Przykład Poniższy, niewielki przykład jest z jednej strony łatwo zrozumiały, z drugiej – nietrywialny. Funkcja z listingu 9.7 oblicza wartość xn dla zadanej liczby całkowitej x i liczby naturalnej n. Zapis n[0] oznacza najmniej znaczący bit liczby n. Jeśli jest on równy 0, to n jest parzysta, jeśli 1 – nieparzysta.

function Potęgowanie(int x, byte n)

1 wynik := 1 2 while (n != 0) do 3 4 5 6 7

if (n[0] != 0) then wynik := wynik * x n := n-1 endif x := x*x

8 n := n/2 9 end while 10 return wynik Listing 9.7. Szybkie potęgowanie Krok 1. Określenie przedmiotu i elementów testów. Elementem testów jest program Potęgowanie. ET1: Potęgowanie. Kroki 2 i 3. Wyprowadzenie warunków testowych i elementów pokrycia. Warunkami i elementami pokrycia są LSKiS. Aby je wyliczyć, musimy zbadać, w których liniach i do których linii programu może nastąpić skok. W linii 2., gdy warunek while nie będzie spełniony, nastąpi skok do linii 10. W linii 3., gdy warunek if nie będzie spełniony, nastąpi skok do 7. W linii 9. zawsze nastąpi skok do linii 2., na początek pętli while. W linii 10. następuje skok do linii 10. i wyjście z programu (instrukcje return traktuje się właśnie jako skok do tej samej linii i zakończenie działania modułu; symbolicznie skok reprezentujący koniec pracy oznacza się liczbą –1). A zatem miejsca do których może nastąpić skok to linie 2, 7 i 10. Te linie, razem z linią początkową 1., stanowią jedyne miejsca, z których może nastąpić start sekwencji LSKiS. Miejsca, z których może nastąpić skok, to linie 2, 3, 9 i 10. Te linie stanowią jedyne możliwe końce liniowych sekwencji. Możliwe skoki to 2 → 10, 3 → 7, 9 → 2 oraz 10 → –1. W tabeli 9.30 zawarto wszystkie możliwe LSKiS. Tabela 9.30. Możliwe LSKiS dla programu Potęgowanie

LSKiS id

Start

Koniec

Skok do

1

1

2

10

2

1

3

7

3

1

9

2

4

2

2

10

5

2

3

7

6

2

9

2

7

7

9

2

8

10

10

–1

Otrzymujemy

zatem

8

warunków

testowych

będących

jednocześnie

elementami pokrycia: WT1=EP1: LSKiS 1; WT4=EP4: LSKiS 4; WT7=EP7: LSKiS 7; WT2=EP2: LSKiS 2; WT5=EP5: LSKiS 5; WT8=EP8: LSKiS 8. WT3=EP3: LSKiS 3; WT6=EP6: LSKiS 6; Krok 4. Zdefiniowanie przypadków testowych. Chcemy pokryć każdą z LSKiS. Musimy zatem dobrać dane testowe tak, aby wymusić odpowiednie sterowanie w każdym przypadku. Aby pokryć EP1, pętla while nie może się wykonać ani razu. Można to wymusić, przyjmując na wejściu n = 3. W praktyce, w wielu przypadkach taki LSKiS będzie nieosiągalny, gdyż konstrukcje while często wymuszają co najmniej jedno wykonanie tej pętli. Zauważmy, że pokrycie EP1 automatycznie pokryje EP8. Tak naprawdę, EP8 będzie pokryte przez każdy przypadek testowy, gdyż program zawsze zakończy swoje działanie w linii 10. Spróbujmy pokryć kolejnym przypadkiem testowym więcej niż jeden element pokrycia. Rozważmy typowe wejście do programu, np. x = 3, n = 7. Spowoduje ono wykonanie następującej sekwencji instrukcji: 1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9, 2, 10, 10, –1. Zauważmy, że sekwencja ta pokrywa następujące LSKiS: (1, 9, 2), (2, 9, 2), (7, 9, 2), (2, 3, 7), (2, 2, 10) oraz pokrytą wcześniej (10, 10, –1). Ilustruje to dobrze jeden

z zarzutów pod adresem techniki: skoro jeden, typowy przypadek testowy „załatwia” prawie wszystkie LSKiS, to po co marnować tyle czasu na ich obliczanie? Ponadto, LSKiS (2, 9, 2) oraz (7, 9, 2) są nadmiarowe (redundantne) wobec LSKiS (1, 9, 2). Została nam do pokrycia jeszcze jedna LSKiS: (1, 3, 7). Uzyskamy to, wchodząc do pętli while oraz nie wykonując instrukcji if. Warunkiem takiego działania programu jest to, aby n ≠ 0 oraz n[0] = 0. Wystarczy więc przyjąć jako n dowolną liczbę parzystą. Na przykład dla x = 3, n = 2 otrzymamy wykonanie 1, 2, 3, 7, 8, 9, 2, 3, 4, 5, 6, 7, 8, 9, 2, 10, 10, –1 Pokryje ono LSKiS (1, 3, 7) oraz pokryte wcześniej (2, 9, 2), (7, 9, 2), (2, 3, 7), (2, 2, 10) i (10, 10, –1). Otrzymujemy zatem zbiór przypadków testowych zebranych w tabeli 9.31. Pogrubioną czcionką zaznaczono elementy pokryte po raz pierwszy. Tabela 9.31. Przypadki testowe pokrywające LSKiS

Id

n x

Oczekiwany Pokryte LSKiS wynik

Pokryte EP

PT1 0 3 1

(1, 2, 10), (10, 10, –1)

EP1, EP8

PT2 7 3 2187

(1, 9, 2), (2, 9, 2), (7, 9, 2), (2, 3, 7), (2, 2, 10) i (10, 10, –1)

EP3, EP4, EP5, EP6, EP7, EP8

PT3 2 3 9

(1, 3, 7), (2, 9, 2), (7, 9, 2), (2, 3, 7), (2, 2, 10) i (10, 10, –1)

EP2, EP4, EP5, EP6, EP7, EP8

9.10.3. LSKiS a DD-ścieżki i bloki podstawowe Sylabus poziomu zaawansowanego ISTQB [79] twierdzi, że ścieżki LSKiS są równoważne DD-ścieżkom (patrz p. 5.4.1), co nie jest prawdą14. Po pierwsze, DDścieżki o długości większej od 1 nie mogą przechodzić przez instrukcje decyzyjne, co jest dozwolone w przypadku ścieżek LSKiS. Po drugie, ścieżka LSKiS zawsze kończy się skokiem, a DD-ścieżka kończy się wierzchołkiem, z którego dopiero może nastąpić skok. W przykładzie z punktu 9.10.2 mamy np. ścieżkę LSKiS (1, 2, 3, 7), w której liniową sekwencją jest 1, 2, 3, po czym następuje skok do linii 7. Zauważmy, że ta

ścieżka zawiera instrukcję decyzyjną (linia nr 2)! Nie może więc być DD-ścieżką. DD-ścieżkami w kodzie z przykładu są ścieżki: (1), (10) odpowiadające wierzchołkom początkowemu i końcowemu; (2), (3), (7), (9) odpowiadające wierzchołkom o stopniu wchodzącym lub wychodzącym większym niż 1; (4, 5, 6), (8) odpowiadające łańcuchom maksymalnym. DD-ścieżki zostały zdefiniowane w celu upraszczania grafów przepływu sterowania tak, aby jeden wierzchołek CFG odpowiadał jednej DD-ścieżce. Takie CFG nazywa się DD-grafami. Z grubsza rzecz ujmując, DD-ścieżki odpowiadają blokom podstawowym instrukcji kodu. „Z grubsza” – bowiem definicja DD-ścieżki „rozdrabnia” niektóre bloki podstawowe, wyróżniając osobny przypadek DDścieżki dla instrukcji z więcej niż jednym punktem wejścia lub wyjścia. W naszym przykładzie może być to linia 7. Stanowi ona razem z następującymi po niej liniami 8. i 9. blok podstawowy, ale (7, 8, 9) nie jest DD-ścieżką, bo – patrząc na kod, jak na CFG – stopień wejścia wierzchołka odpowiadającego linii 7. nie jest równy 1. Można nieco „ulepszyć” definicję DD-ścieżki, jeszcze bardziej minimalizując15 liczbę wierzchołków przez dopuszczenie, aby łańcuch maksymalny mógł zaczynać się od wierzchołka o dowolnym stopniu wchodzącym (lecz mającym wciąż stopień wychodzący = 1), a wierzchołkiem końcowym mógł być wierzchołek końcowy CFG. W takiej sytuacji DD-ścieżki będą równoważne blokom podstawowym.

Rysunek 9.6. DD-ścieżki a bloki podstawowe Wywód ten jest zilustrowany na rysunku 9.6. Graf (a) to CFG dla kodu z listingu 9.7, w którym wierzchołek odpowiada jednej linii kodu. DD-graf (b) pokazuje, jak koncepcja DD-ścieżek pozwala łączyć niektóre wierzchołki ze sobą. Nowymi wierzchołkami są szare prostokąty. Każdy z nich odpowiada jednej DD-ścieżce. Możemy jednak jeszcze bardziej uprościć graf, tworząc bloki podstawowe przez łączenie niektórych DD-ścieżek według reguł opisanych w poprzednim akapicie. Graf ten ma o jeden wierzchołek mniej niż graf z rysunku (b), dzięki połączeniu wierzchołków DD 6 i DD 7 w jeden.

9.11. Testowanie ścieżek pierwszych

9.11.1. Opis metody Ścieżką prostą w grafie przepływu sterowania G = (V, E, v0, VT) nazywamy ścieżkę (v1, v2, …, vn), vi ∈ V dla i = 1, 2, …, n, w której każdy wierzchołek pojawia się co najwyżej raz, przy czym dopuszczamy sytuację, w której ostatni wierzchołek jest równy pierwszemu: vn = v1 (mamy wtedy do czynienia z tzw. cyklem prostym). Koncepcja testowania ścieżek prostych prowadzi do zbyt dużych i redundantnych suit testowych. Aby

temu zaradzić, a

jednocześnie utrzymać kryterium

wymagające pokrycia ścieżek stanowiących „podstawowe bloki budulcowe” wszystkich możliwych sekwencji przebiegu sterowania, wprowadźmy pojęcie ścieżki pierwszej (ang. prime path). Nazwa metody ma pokazywać analogię ścieżek CFG do arytmetyki: tak, jak każda liczba naturalna daje się wyrazić jako iloczyn liczb pierwszych, tak każda ścieżka testowa daje się wyrazić jako złożenie ścieżek pierwszych. Ścieżka pierwsza to ścieżka prosta niebędąca właściwą podścieżką16 innej ścieżki prostej. Jeśli ścieżkę p możemy rozszerzyć, dodając na jej początku lub końcu wierzchołek, który nie występował w p i nie jest wierzchołkiem początkowym p, oznacza to, że p nie jest ścieżką pierwszą. podścieżka (ang. subpath) – 1) ciąg wykonywalnych instrukcji wewnątrz modułu; 2) ścieżka będąca częścią innej, większej ścieżki Rozważmy dla przykładu graf przepływu sterowania z rysunku 9.7. Ścieżka (1, 2, 5, 6) jest ścieżką pierwszą. Łatwo zauważyć, że każda ścieżka testowa, nieprzechodząca po żadnym wierzchołku więcej niż raz, jest ścieżką pierwszą. Innym przykładem ścieżki pierwszej może być (1, 3, 4). Rozszerzenie jej do (1, 3, 4, 3) spowoduje powtórzenie się wierzchołka 3. Ścieżka (4, 3) nie jest pierwsza, gdyż można ją rozszerzyć np. do ścieżki (4, 3, 4), (4, 3, 5, 1, 2) lub (4, 3, 5, 6). W szczególności pierwsza z nich jest ścieżką pierwszą, bo jest cyklem prostym.

Rysunek 9.7. Przykładowy CFG Technika ścieżek pierwszych wymaga, aby każda ścieżka pierwsza w CFG była pokryta przynajmniej przez jeden przypadek testowy. Warunkami testowymi są

elementy składowe grafu przepływu sterowania. Elementami pokrycia są wszystkie ścieżki pierwsze występujące w tym CFG. Szczególnym przypadkiem ścieżki pierwszej jest ścieżka cykliczna (ang. round trip), zwana też cyklem prostym. Jest to ścieżka pierwsza o niezerowej długości rozpoczynająca i kończąca się w tym samym wierzchołku. Z pojęciem ścieżki cyklicznej są związane dwa kryteria pokrycia. Podamy tu tylko ich definicje, ponieważ z praktycznego punktu widzenia lepiej jest stosować kryterium pokrycia ścieżek pierwszych, które je subsumuje. Kryterium prostego pokrycia ścieżek cyklicznych (ang. Simple Round Trip Coverage, SRTC) – wymaga, aby zbiór przypadków testowych zawierał przynajmniej jedną ścieżkę cykliczną dla każdego osiągalnego wierzchołka CFG, który leży na jakiejś ścieżce cyklicznej. Kryterium całkowitego pokrycia ścieżek cyklicznych (ang. Complete Round Trip Coverage, CRTC) – wymaga, aby zbiór przypadków testowych zawierał wszystkie ścieżki cykliczne dla każdego osiągalnego wierzchołka CFG, który leży na jakiejś ścieżce cyklicznej. W tabeli 9.32 podsumowano metodę testowania ścieżek pierwszych. Tabela 9.32. Podsumowanie metody testowania ścieżek pierwszych

W arunki testowe

W ierzchołki CFG

Elementy pokrycia

S ekwencje wierzchołków CFG s tanowiące ś cieżki pierws ze

Kryterium pokrycia

Każda ś cieżka pierws za powinna zos tać wykonana przynajmniej raz

Pokrycie

p=

liczba pokrytych ś cieżek pierws zych liczba ws zys tkich ś cieżek pierws zych

× 100%

9.11.2. Algorytm wyznaczania ścieżek pierwszych Istnieje prosta systematyczna metoda wyznaczania ścieżek pierwszych. Jest formalnie opisana na listingu 9.8.

WyznaczŚcieżkiPierwsze(CFG G = (V, E, v0, VT)) 1 P0 := wszystkie ścieżki o długości 0 (czyli wierzchołki CFG) 2

:= wierzchołki końcowe z P0

3 i := 0 4 while ( 5 6 7

) do

Pi+1 := wszystkie rozszerzenia Pi będące ścieżkami prostymi := nierozszerzalne ścieżki z Pi+1 i := i+1

8 PPcandidates := 9 PrimePaths:=PPcandidates \ podścieżki innych ścieżek z PPcandidates 10 return PrimePaths Listing 9.8. Wyznaczanie ścieżek pierwszych Działanie algorytmu opiszemy na przykładzie. Rozważmy ponownie CFG z rysunku 9.7. Wyznaczymy wszystkie ścieżki pierwsze dla tego grafu. Rozpoczynamy od znalezienia zbioru P0 wszystkich ścieżek prostych o długości 0. Są to oczywiście wszystkie wierzchołki CFG: 1) (1) 3) (3) 5) (5) 2) (2) 4) (4) 6) (6)* Gwiazdką oznaczamy elementy należące do zbiorów , czyli ścieżki, których nie można już rozszerzyć ze względu na to, że tworzą cykl prosty lub kończą się w wierzchołku końcowym CFG. Jedynie wierzchołek 6 jest ścieżką nierozszerzalną. W kolejnym kroku rozszerzamy na wszystkie możliwe sposoby ścieżki ze zbioru P0: 7) (1, 2) 11) (3, 5) 8) (1, 3) 12) (4, 3) 9) (2, 5) 13) (5, 1) 10) (3, 4) 14) (5, 6)* Elementy (7) – (13) wciąż da się rozszerzyć, zatem obliczamy zbiór P2:

15) (1, 2, 5) 18) (2, 5, 1) 21) (3, 5, 1) 24) (4, 3, 5) 16) (1, 3, 4)* 19) (2, 5, 6)* 22) (3, 5, 6)* 25) (5, 1, 2) 17) (1, 3, 5) 20) (3, 4, 3)* 23) (4, 3, 4)* 26) (5, 1, 3) Ścieżki (16) nie da się rozszerzyć do ścieżki prostej, bo powtórzyłby się element 3. Elementy (20) i (23) są cyklami prostymi, a (19) i (22) kończą się w wierzchołku końcowym CFG. Pozostałe 7 ścieżek jest rozszerzalnych, obliczamy więc zbiór P3: 27) (1, 2, 5, 1)* 31) (2, 5, 1, 2)* 35) (4, 3, 5, 1) 39) (5, 1, 3, 5)* 28) (1, 2, 5, 6)* 32) (2, 5, 1, 3)* 36) (4, 3, 5, 6)* 29) (1, 3, 5, 1)* 33) (3, 5, 1, 2)* 37) (5, 1, 2, 5)* 30) (1, 3, 5, 6)* 34) (3, 5, 1, 3)* 38) (5, 1, 3, 4)* Jedynie ścieżka (35) jest rozszerzalna, obliczamy więc P4: 40) (4, 3, 5, 1,2)* Żadnej ścieżki z P4 nie da się już rozszerzyć, więc kończymy pętlę 4.–7. algorytmu. Zbiór potencjalnych kandydatów na ścieżki pierwsze, PPcandidates, składa się wyłącznie z nierozszerzalnych ścieżek spośród 40 wcześniej wymienionych. Odwołując się do ich numeracji, mamy PPcandidates = {6, 14, 16, 19, 22, 23, 27, 28, 29, 30, 31, 32, 33, 34, 36, 37, 38, 39, 40} Ostatnim elementem jest usunięcie podścieżek innych ścieżek prostych ze zbioru PPcandidates: (4, 3, 5, 1, 2) jako podścieżkę zawiera (3, 5, 1, 2); (1, 2, 5, 6) jako podścieżki zawiera (2, 5, 6), (5, 6) oraz (6); (1, 3, 5, 6) jako podścieżkę zawiera (3, 5, 6); (5, 1, 3, 4) jako podścieżkę zawiera (1, 3, 4). Usuwając z PPcandidates podścieżki innych ścieżek, otrzymujemy ostatecznie zbiór złożony z 14 ścieżek pierwszych: 1) (4, 3, 5, 1, 2) 5) (1, 3, 5, 6) 9) (4, 3, 5, 6) 13) (3, 4, 3) 2) (1, 2, 5, 1) 6) (2, 5, 1, 2) 10) (5, 1, 2, 5) 14) (4, 3, 4) 3) (1, 2, 5, 6) 7) (2, 5, 1, 3) 11) (5, 1, 3, 4) 4) (1, 3, 5, 1) 8) (3, 5, 1, 3) 12) (5, 1, 3, 5)

Liczba ścieżek pierwszych jest zawsze skończona, gdyż maksymalna długość ścieżki pierwszej jest równa liczbie (skończonej) wierzchołków grafu przepływu sterowania. Algorytm wyznaczania ścieżek pierwszych jest poprawny, tzn. w szczególności ma więc własność stopu. Kryterium pokrycia ścieżek pierwszych subsumuje kryterium pokrycia instrukcji, gałęzi, decyzji, LSKiS oraz pętli (w wersji wymagającej wejścia do pętli 0 oraz więcej niż 0 razy). Jest ono mocnym kryterium, aczkolwiek często zdarza się, że wymaga pokrycia ścieżek nieosiągalnych. Można wtedy je osłabić lub zastosować metody z podrozdziału 9.14.

9.11.3. Przykład Przetestujmy za pomocą kryterium pokrycia ścieżek pierwszych program sprawdzający, czy podana na wejściu nieujemna liczba jest, nomen omen, pierwsza. Na listingu 9.9 przedstawiono testowaną funkcję.

function CzyPierwsza(int n) 1 if (n >) jest zamieniany na każdy inny; ponadto każde wyrażenie jest

zamieniane na jego lewy operand. Na przykład, dla instrukcji x=ab, x=a>>>b, x=a. Zamiana bitowego operatora logicznego – każdy operator bitowy (bitowe and &, bitowe or |, xor ^) jest zamieniany na każdy inny; ponadto każde wyrażenie zamieniane jest na jego lewy oraz prawy operand. Na przykład, dla instrukcji p=x&y możliwe mutanty to: p=x|y, p=x^y, p=x, p=y. Zamiana operatora przypisania – każdy operator przypisania (+=, –=, *=, /=, %=, &=, |=, ^=, =, >>>=) jest zamieniany na każdy inny. Na przykład, dla instrukcji x+=1 możliwe mutanty to: x-=1, x*=1, x/=1, x%=1, x&=1, x|=1, x^=1, x=1, x>>>=1. Wstawienie operatora unarnego – każdy operator unarny (arytmetyczne +, –, warunkowy !, logiczny ~) jest wstawiany przed każde wyrażenie odpowiedniego typu. Na przykład, dla instrukcji x=y+1 możliwe mutanty to: x=+y+1, x=-y+1, x=y++1, x=y+-1. Usunięcie operatora unarnego – każdy operator unarny jest usuwany. Na przykład, dla instrukcji if (a>-b+c) możliwe mutanty to: if ! (a>-b+c), if (a>b+c). Zamiana zmiennej – każda referencja zmiennej jest zamieniana na inną zmienną tego samego typu widzialną w bieżącym zakresie. Na przykład, zakładając że w bieżącym zakresie widzialne są tylko zmienne x, y, z i są one tego samego typu, dla instrukcji x=y+z możliwe mutanty to: y=y+z, z=y+z, x=x+z, x=z+z, x=y+x, x=y+y. Bomba – każda instrukcja jest zamieniana na instrukcję Bomb(). Operatory mutacyjne dla mutacji integracji Zamiana zmiennej w wywołaniu metody – każdy parametr wywołania metody jest zamieniany przez każdy inny o zgodnym typie, widoczny w zakresie metody. Wstawienie operatora unarnego – każde wyrażenie w wywołaniu metody jest modyfikowane przez wstawienie wszystkich możliwych operatorów unarnych przed i za tym wyrażeniem. Przykładem takich operatorów mogą być ++ i –– używane w Javie czy C++.

Zamiana parametrów – każdy parametr wywołania metody jest zamieniany na każdy inny o zgodnym typie. Na przykład, wywołanie power(double x, double y)

może

być

zmutowane

na

power(double y, double x). Usunięcie wywołania metody – każde

wywołanie

metody

jest

usuwane. Jeśli metoda zwraca wartość i jest użyta w wyrażeniu, to wywołanie metody jest zamieniane przez odpowiednią stałą wartość. Jeśli język programowania ma zdefiniowane domyślne wartości typów prostych, to należy je użyć. Jeśli metoda zwraca obiekt, to wywołanie powinno być zamienione na takie, które wywołuje konstruktor właściwej klasy. Modyfikacja zwracanej wartości – każde wyrażenie w każdej instrukcji return w metodzie jest modyfikowane przy użyciu zamiany operatora arytmetycznego lub wstawienia operatora unarnego. Operatory mutacyjne dla mutacji programów zorientowanych obiektowo Zmiana operatora dostępu – np. każdy z operatorów dostępu: private, package, protected, public jest zamieniany na każdy inny. Usunięcie ukrycia zmiennej – każda deklaracja nadpisująca lub ukrywająca zmienną jest usuwana, przez co referencja do zmiennej dotyczy zmiennej zdefiniowanej w nadklasie lub podklasie. Wstawienie ukrycia zmiennej – deklaracja jest dodawana, aby ukryć deklarację każdej zmiennej zdefiniowanej w podklasie. Usunięcie metody nadpisującej – każda deklaracja metody nadpisującej jest usuwana, przez co referencja do metody będzie dotyczyć metody w nadklasie. Zmiana nazwy metody nadpisującej – zmieniana jest nazwa metody w nadklasie, która to metoda jest nadpisana w podklasie, przez co nadpisanie nie wpływa na metodę nadklasy. Usunięcie słowa kluczowego „super” – każde wystąpienie słowa „super” jest usuwane, przez co referencja będzie dotyczyć lokalnej wersji obiektu, a nie wersji w podklasie. Usunięcie konstruktora nadklasy – każde wywołanie do konstruktora „super” jest usuwane, przez co użyty będzie domyślny konstruktor nadklasy (lub podklasy). Aby zabić takiego mutanta, należy znaleźć test,

w którym domyślny konstruktor nadklasy tworzy nieprawidłowy stan początkowy. Zmiana typu aktualnego – aktualny typ nowego obiektu jest zmieniany w instrukcji new(), przez co referencja obiektu wskazuje na obiekt typu innego od oryginalnego typu aktualnego; nowy typ aktualny musi być w tej samej rodzinie typów co oryginalny typ aktualny. Zmiana typu deklarowanego – deklarowany typ każdego nowego obiektu jest zmieniany w deklaracji. Zmiana typu parametru – deklarowany typ każdego parametru obiektu jest zmieniany w deklaracji. Zmiana typu referencji – obiekty z prawej strony instrukcji przypisania są zmieniane tak, by wskazywać obiekty zgodnego typu, np. jeśli w Javie Integer jest przypisane do referencji typu Object, mutacja może zmienić go na String, ponieważ zarówno Integer, jak i String dziedziczą po klasie Object. Zmiana przeciążonej metody – dla każdej pary metod o tej samej nazwie ich ciała są zamieniane. Usunięcie przeciążonej metody – każda deklaracja przeciążonej metody jest usuwana (za każdym razem jedna), co sprawdza, czy każda przeładowana metoda jest wywoływana przynajmniej raz. Zmiana kolejności parametrów wywołania – kolejność argumentów w wywołaniu metody jest zmieniana na kolejność innej metody przeciążonej (jeśli istnieje). Zmiana liczby argumentów – liczba argumentów wywołania zmieniana jest na liczbę argumentów wywołania innej przeciązonej metody (jeśli istnieje). Usunięcie słowa kluczowego „this” – każde wystąpienie „this” jest usuwane, przez co sprawdzane jest, czy zmienne składowe metody są używane poprawnie. Zmiana modyfikatora „static” – każde wystąpienie „static” jest usuwane oraz dodawane do instancji zmiennych. Usunięcie inicjalizacji zmiennej składowej jest usuwana.



każda

inicjalizacja

zmiennej

Usunięcie domyślnego konstruktora – każda deklaracja domyślnego konstruktora (bez parametrów) jest usuwana.

Operatory mutacyjne dla mutacji specyfikacji wyrażanych maszynami stanowymi Zmiana przejścia – przejście ze stanu X do Y jest zamieniane na przejście z X do Z ≠ Y. Zmiana zdarzenia – każde zdarzenie jest zamieniane na inne. Zmiana akcji – każda akcja jest zamieniana na inną. Mutacja warunków – do każdego warunku przy zdarzeniu są stosowane odpowiednie operatory mutacji dla kodu źródłowego. Operatory mutacyjne dla mutacji opartych na gramatykach formalnych Zamiana symbolu nieterminalnego – każdy symbol nieterminalny21 w każdej produkcji jest zamieniany na inny nieterminal, np. A → Bc może być zamienione na A → Ac. Zamiana symbolu terminalnego – każdy symbol terminalny w każdej produkcji jest zamieniany na inny terminal, np. A → Bc może być zamienione na A → Bd. Usunięcie terminala/nieterminala – każdy terminal oraz nieterminal jest usuwany z każdej produkcji, np. A → Bc może być zamienione na A → B lub A → c. Powielenie terminala/nieterminala – każdy terminal oraz nieterminal jest duplikowany (np. dla produkcji A → Bc mutantami mogą być produkcje A → BBc oraz A → Bcc).

Rysunek 9.15. Hierarchia subsumpcji dla białoskrzynkowych kryteriów pokrycia

9.16. Subsumpcja kryteriów Na rysunku 9.15 przedstawiono relacje subsumpcji między opisanymi białoskrzynkowymi kryteriami pokrycia. Linie przerywane oznaczają, że dana subsumpcja jest warunkowa, tzn. zachodzi tylko w pewnych specjalnych okolicznościach. Kryterium MUMCUT subsumuje kryterium MC/DC (pokrycia warunków znaczących), jeśli dla każdego predykatu w postaci IDNF istnieje UN-para testów dla każdej zmiennej w tym predykacie. Kryterium LCSAJ (pokrycia liniowych sekwencji kodu i skoku) subsumuje MC/DC, jeśli są spełnione następujące trzy warunki [129]: żaden predykat w kodzie nie zawiera silnie związanych warunków (ang. strongly coupled conditions); każda decyzja w kodzie tworzy część struktury rozgałęzienia (ang. branching construct); żadna decyzja w kodzie nie zawiera mieszaniny operatorów ∧ oraz ∨, lecz tylko jeden z tych typów (z dopuszczeniem negacji pojedynczych klauzul). Kryterium AUC (pokrycia wszystkich użyć) subsumuje kryterium pokrycia decyzji, jeśli są spełnione następujące trzy warunki: każde użycie jest poprzedzone definicją; każda definicja osiąga przynajmniej jedno użycie; w wierzchołku o wielu krawędziach wychodzących na każdej krawędzi wychodzącej jest przynajmniej jedna użyta zmienna i ta sama zmienna jest użyta na wszystkich pozostałych krawędziach wychodzących.

1 W praktyce, ze względu na właściwość bloków podstawowych CFG, kryteria pokrycia dla metod białoskrzynkowych odnosi się zwykle do elementów modelu, a nie pojedynczych instrukcji czy innych fragmentów kodu. 2 Predykat to zdanie logiczne, które może przyjąć wartość logiczną prawdy lub fałszu. 3 Zdanie to jest prawdą tylko dla elementów testowych z jednym punktem wejścia. W praktyce większość testowanych obiektów ma tę właściwość. 4 Pierwiastkiem (inaczej: miejscem zerowym) funkcji f(x) nazywamy taką wartość x*, dla której f(x*) = 0. 5 Gdyby kompilator nie stosował semantyki short-circuit, a tablica miałaby n elementów indeksowanych od 0 do n – 1, to w przypadku, gdy np. i = n warunek T [i] ≠ 0 i tak byłby sprawdzany. Mogłoby to skutkować błędem, bo następowałoby odwołanie do wartości spoza tablicy. 6 Tak jak w poprzednich kryteriach, formalnie wymagamy, aby zmodyfikowane kryterium MC/DC spełniało kryterium pokrycia instrukcji. 7 Tablica prawdy podaje wartość logiczną predykatu (decyzji) dla wszystkich możliwych wartości logicznych klauzul (warunków). Dla n warunków tablica ma 2n wierszy. 8 Choć w większości języków programowania nie ma wprost zdefiniowanego operatora równoważności ⇔, można go uzyskać, korzystając z następujących praw logiki: p ⇔ q ≡ (p ⇒ q) ∧ (q ⇒ p) ≡ (q ∨ ~p) ∧ (p ∨ ~q) ≡ (p ∧ q) ∨ (~p ∧ ~q) 9 W przypadku pętli typu do-while, gdzie warunek pętli jest sprawdzany po jej wykonaniu, zawsze wykona się ona co najmniej raz, dlatego w tym przypadku kryterium może mówić o wykonaniu pętli raz oraz dokładnie dwa razy. 10

Liczba ta wyraża minimalną liczbę hiperpłaszczyzn leżących na hiperpłaszczyźnie reprezentującej tzw. wzorzec pętli H. Jeśli ścieżka p → q → r zawiera pętlę q → q i funkcje reprezentujące obliczenia ścieżek p, q, r oznaczymy odpowiednio przez f pq, h, f qr , to n-krotne wykonanie pętli na tej ścieżce jest reprezentowane przez złożenie funkcji f n = f pq ◦ hn ◦ f qr . Kładąc f 0 = f pq ◦ f qr oraz f n = f n–1◦ H, mamy Założyliśmy, że obliczenia



Funkcję H nazywamy wzorcem pętli. liniowe, dlatego wszystkie funkcje są

przekształceniami liniowymi. W szczególności więc funkcja

odwrotna do

f qr , jest dobrze zdefiniowana. 11 Liczba ta jest analogiczna do liczby punktów wyznaczających jednoznacznie hiperpłaszczyznę w metodzie punktów ON-OFF. 12 Subsumpcja istnieje także dla programów złożonych z jednego bloku bazowego, bez rozgałęzień. Jest tak dlatego, że LSKiS wymaga w szczególności pokrycia ścieżek zaczynających się na początku modułu oraz kończących w jego punkcie wyjścia. 13 Niektórzy autorzy uważają, że metoda sztucznego wymuszania sterowania powinna być stosowana w testowaniu, w szczególności po to, aby pokryć nieosiągalne elementy pokrycia. Jednak tester testuje funkcjonalnie program w taki sam sposób, w jaki użytkownik będzie go używał – jeśli użytkownik nie może sztucznie zmienić w środku działania aplikacji jakiejś zmiennej, to po co wymuszać takie warunki podczas testowania funkcjonalnego? 14 LSKiS są natomiast równoważne pojęciu „JJ-ścieżki” (ang. jump-to-jump path) [129]. Prawdopodobnie autorzy sylabusa mieli na myśli JJ-ścieżki, a nie DDścieżki. 15 Wielu autorów, podając definicję DD-ścieżki, popełnia błąd, nie uwzględniając tych zasad, choć tak naprawdę implicite je zakładając, bo cała idea DD-ścieżek polega na tym, że powinny odpowiadać blokom podstawowym, aby zminimalizować liczbę wierzchołków CFG w jak największym stopniu. 16 Dla ścieżki (v1, v2, …, vn) jej podścieżkami są wszystkie ścieżki postaci (vi, vi+1, …, vj), 1 ≤ i ≤ j ≤ n. Podścieżka (vi, vi+1, …, vj) jest właściwa, jeśli i > 1 lub j < n. 17 W teorii języków formalnych wektory takie nazywa się wektorami Parikha. 18 Określenie „krawędź bazowa” pochodzi stąd, że krawędź ta tworzy kolumnowy wektor bazowy w macierzy zawierającej wektory ścieżek bazowych. 19 W języku Ada parametr formalny (zdefiniowany w sygnaturze funkcji) może przekazywać dane do parametru aktualnego (zmiennej, która występuje w fizycznym wywołaniu procedury). W takim przypadku do parametrów tych, opisanych słowem kluczowym „out” można się odwoływać tylko tak, aby były one po lewej stronie instrukcji podstawienia. 20 Sygnatura funkcji to opis typów parametrów jej wywołania oraz ich kolejności. 21 Symbole nieterminalne symbolicznie oznacza się zwykle wielkimi literami A, B, C, …, a terminale – małymi. W praktycznych zastosowaniach terminale pisze się w cudzysłowach, a nieterminale wielkimi literami bądź w nawiasach kwadratowych (patrz p. 8.14.2).

10. Techniki oparte na defektach i na doświadczeniu

W tym rozdziale omówimy techniki mniej sformalizowane niż w przypadku metod czarno- i białoskrzynkowych. Mimo ich mniejszego ustrukturalizowania są one wciąż pełnoprawnymi i wartościowymi technikami. Co więcej, niektóre z nich (np. testowanie eksploracyjne), w wykonaniu profesjonalnych, doświadczonych testerów sprawdzają się często o wiele lepiej niż techniki bardziej formalne, narzucające z góry określony sposób postępowania. Z technikami opartymi na defektach mamy do czynienia wtedy, gdy testujemy oprogramowanie, myśląc o konkretnych typach defektów. Zwykle modelem jest tutaj jakaś mniej lub bardziej szczegółowa lista możliwych defektów, które np. występowały w poprzednich projektach. Tworzenie testów polega na dostarczeniu takich danych wejściowych, aby wywołać awarię ujawniającą określony typ usterki. W technikach opartych na doświadczeniu zwykle tester ma wolną rękę, tzn. sam decyduje, co i w jaki sposób testować. Swoje decyzje podejmuje na podstawie własnej wiedzy, doświadczenie oraz obserwacji działania programu. technika (projektowania testów) oparta na defektach (ang. defect-based (test design) technique) – procedura projektowania i/lub wyboru przypadków testowych ukierunkowana na jeden lub więcej typów defektów, w której testy projektuje się na podstawie wiedzy o określonych typach defektów; patrz także: taksonomia defektów technika projektowania testów oparta na doświadczeniu (ang. experiencebased test design technique) – procedura projektowania i/lub wyboru przypadków testowych na podstawie doświadczenia, wiedzy i intuicji testera

typ usterki, kategoria usterki (defect type, defect category) – element systematyki usterek. Systematyka ta może być identyfikowana w zależności od różnych okoliczności, włączając w to, ale nie ograniczając się do: fazy lub czynności deweloperskiej, w której usterka została wytworzona (np. błąd w specyfikacji lub błąd kodowania), charakterystki usterki (np. tzw. błąd o jeden), pomyłki (np. niepoprawny operator relacji, błąd składni języka programowania lub niewłaściwe założenia), problemy wydajnościowe (np. zbyt długi czas wykonania, niedostateczna dostępność); patrz także punkt 27.2.2 – ODC (Orthogonal Defect Classification) W technikach opartych na defektach i doświadczeniu zwykle nie stosuje się kryteriów pokrycia, gdyż nie da się ich dobrze, precyzyjnie zdefiniować. W przypadku stosowania list kontrolnych można oczywiście przyjąć, że stopień pokrycia jest związany z odsetkiem pokrytych testami punktów tej listy, jednak zwykle nie da się ocenić w jakim stopniu pojedynczy test „pokrywa” określony typ defektu z listy.

10.1. Wstrzykiwanie błędów Technika wstrzykiwania błędów (ang. fault injection) polega na celowym wprowadzaniu do oprogramowania usterek i zwykle dotyczy testowania odporności. Po wprowadzeniu usterki badamy zachowanie programu i to, jak sobie z daną usterką poradzi. Metody te można podzielić ze względu na czas wstrzyknięcia usterki na trzy kategorie: wstrzyknięcie przed kompilacją [130] – polega na wstrzykiwaniu usterek do kodu źródłowego; formą tego typu techniki jest testowanie mutacyjne omówione w podrozdziale 9.15; wstrzyknięcie po kompilacji, ale przed uruchomieniem [131] – następuje po tym, jak kod źródłowy zostaje przetransformowany do kodu obiektowego lub bajtkodu i polega na modyfikacji kodu wynikowego; wstrzyknięcie w trakcie działania programu [132] – przeprowadzany zwykle niskopoziomowo przez wprowadzanie zmian w kodzie

maszynowym lub bajtkodzie rezydującym w pamięci. posiew błędów, posiew usterek (ang. error seeding, fault seeding) – proces celowego dodawania defektów do już istniejących w module lub systemie w celu monitorowania efektywności ich wykrywania i usuwania oraz szacowania defektów niewykrytych; posiew usterek jest zwykle częścią rozwoju oprogramowania i może być wykonywany na każdym poziomie testów [7] wstrzykiwanie błędów, wprowadzanie błędów (ang. fault injection, bebugging) – proces zamierzonego dodawania defektów do systemu w celu wykrycia, czy system może wykryć defekt i pracować mimo jego występowania; wstrzykiwanie błędów stara się imitować błędy, które mogą wystąpić w produkcji [133]; patrz także: tolerowanie usterek Wstrzykiwanie błędów wymaga posiadania zbioru usterek, które chcemy posiać w programie. Każda aplikacja ma różne tryby awarii (ang. failure modes), czyli sposoby, na które może objawić się jej nieprawidłowe działanie. Przykładowy model awarii może dzielić nieprawidłowe wykonanie programu na następujące cztery typy: zawieszenie (ang. hang) – następuje, gdy w wyniku wywołania metody nie otrzymujemy odpowiedzi; ten typ awarii może być symulowany za pomocą wstrzyknięcia w odpowiednie miejsce nieskończonej pętli; testowanie wymaga zdefiniowania limitu czasu, w którym musi nastąpić odpowiedź; po tym czasie możemy założyć, że komponent się zawiesił; anormalne zakończenie działania (ang. abend) – ta awaria powoduje rzucenie wyjątku w miejscu, gdzie istnieje kod, który go obsługuje; wstrzyknięta usterka może mieć postać kodu wywołującego wyjątek umieszczonego w miejscu, w którym wyjątek ten może być obsłużony; crash – podobnie do anormalnego zakończenia działania, następuje gdy jest rzucony wyjątek, ale region, w którym ma to miejsce, nie zawiera kodu do jego obsłużenia; zwykle powoduje to propagację awarii do komponentu lub metody wywołującej; błędne zachowanie (ang. erroneous behavior) – następuje, gdy wywołana metoda nie rzuca wyjątku, ale zwraca nieprawidłową

wartość; klasycznym przykładem jest tu tzw. błąd o jeden (tzn. przyjęcie przez jakąś zmienną wartości o jeden mniejszej lub większej od prawidłowej). Klasyfikacja

trybów

awarii

pozwala

na

systematyczne

tworzenie

„generycznych” usterek, które symulują dane awaryjne zachowanie. Usterki te są zwykle dostępne dla testera w odpowiednim oprogramowaniu do posiewu usterek i tester może zdecydować, które z nich wybrać do procesu wstrzykiwania. Przykładami typów wstrzykiwanych usterek mogą być: błędne dane wejściowe; wymuszenie rzucenia wyjątku; wstrzyknięcie zmiany stanu programu; wymuszenie opóźnienia w działaniu programu, wątku lub procesu; wymuszenie błędu pamięci. Na rysunku 10.1 przedstawiono podejście do techniki wstrzykiwania usterek, która pozwala testerowi ulepszyć zbiór przypadków testowych przez ewentualne dodanie do niego testów zdolnych do wykrycia określonych typów usterek, przez co można osiągnąć odpowiedni stopień pokrycia usterek. Tak jak w przypadku testowania mutacyjnego, rozpoczynamy z testowanym programem, początkowym zbiorem przypadków testowych oraz modelem usterki. Na podstawie tego modelu wybieramy typy usterek i wstrzykujemy je w wybrane miejsca programu przy użyciu odpowiednich narzędzi. Na tak zmodyfikowanych wersjach programu wykonujemy testy i sprawdzamy, które usterki nie zostały wykryte. Tworzymy dla nich dodatkowe testy, które będą w stanie je wykryć. Gdy wszystkie usterki są wykryte, proces się kończy. Gdy zachodzi potrzeba wstrzykiwania usterek podczas działania programu, można posłużyć się narzędziami do debugowania. Debugery (np. unixowy gdb) oferują wiele możliwości, takich jak śledzenie i podmiana wartości zmiennych, ustawianie pułapek oraz wiele innych.

Rysunek 10.1. Proces wstrzykiwania usterek i ulepszania zestawu testów (za [131]) Wstrzykiwanie błędów jest techniką wykorzystywaną również wobec sprzętu (np. mikrokontrolerów czy różnego typu systemów wbudowanych). W takim przypadku polega na wymuszaniu podawania błędnych sygnałów na wejście oraz na symulowaniu rzeczywistych awarii wywołanych np. interferencją elektromagnetyczną, zmianą napięcia czy promieniowaniem1.

10.2. Taksonomie Testowanie oparte na taksonomii błędów to metoda, w której testujemy oprogramowanie pod kątem występowania określonych błędów. Zwykle są one zapisane w postaci tzw. taksonomii, czyli hierarchicznego systemu pojęć zaprojektowanego w celu ułatwienia klasyfikacji obiektów opisywanych tymi pojęciami. Taksonomia może być wynikiem doświadczenia naszego lub innych pracowników organizacji, tzn. opisywać błędy, które pojawiały się w przeszłości, przy innych projektach.

taksonomia defektów, taksonomia błędów (ang. defect taxonomy,

bug

taxonomy) – system hierarchicznych kategorii ułatwienia klasyfikacji defektów

celu

zaprojektowany

w

W tabeli 10.1 jest opisana przykładowa taksonomia, pochodząca z [93], a oryginalnie oparta na pracy Beizera [14]. Tabela 10.1. Tasonomia defektów oparta na przyczynie źródłowej (root-cause)

Kategoria

Funkcjonalnoś ć

T yp defektu

Kategoria

S pecyfikacja

Typ

Funkcja

S truktura

Tes t

Dane

Wewnętrzne interfejs y Hardware S ys tem

Proces

T yp defektu

Wartoś ć początkowa Inne

Kod

S ys tem operacyjny Dokumentacja Architektura oprog ramowania

S tandardy

Zarządzanie zas obami

Inne

Arytmetyka

Duplikat

Inicjalizacja

To nie problem

S terowanie lub kolejnoś ć

Zewnętrzny

Log ika s tatyczna

Potrzebna analiza przyczyny

Inne

Niewytłumaczalny

Taksonomia składa się z ośmiu głównych kategorii oraz pięciu dodatkowych (Duplikat, To nie problem, Zewnętrzny, Potrzebna analiza przyczyny, Niewytłumaczalny), które są pomocne przy klasyfikacji w systemach do śledzenia defektów. Dalej opisujemy każdy z typów tej taksonomii: Specyfikacja – specyfikacja funkcjonalna (analiza wymagań, projekt architektury itp.) jest błędna; Funkcja – specyfikacja jest poprawna, ale jej implementacja błędna; Test – istnieje jakiś problem z danymi testowymi, przypadków testowych lub implementacją testu; Wewnętrzne interfejsy nieprawidłowa;



komunikacja

wewnątrz

projektem

systemu

jest

Hardware – problem ze sprzętem; System operacyjny – błąd dotyczy złego działania operacyjnego, pod którym działa testowany program;

systemu

Architektura oprogramowania – projekt architektury (struktury) oprogramowania jest błędny; Zarządzanie zasobami – architektura jest poprawna, ale jej implementacja jest błędna, np. powoduje opóźnienia w przesyłaniu lub aktualizacji danych; Arytmetyka – obliczenia wykonywane przez program są błędne, np. na skutek niepoprawnego wykonywania działań, złego zaokrąglania lub stosowania zbyt małej lub zbyt dużej dokładności; Inicjalizacja – następuje błąd przy pierwszym wykonaniu danej operacji; Sterowanie lub kolejność – nastąpiła zła sekwencja akcji; Logika statyczna – występują błędy dla wartości brzegowych, klasy równoważności nie zawierają właściwych elementów itp.; Inne (w kategorii Proces) – nastąpił inny niż powyższe błąd dotyczący przepływu sterowania lub przetwarzania danych; Typ – zły typ danych;

Struktura – struktura danych jest niewłaściwa lub źle użyta; Wartość początkowa – wartość elementu jest inicjalizowana na niewłaściwą wartość lub źle używana, np. iteracja pętli od indeksu 1 zamiast od 0; Inne (w kategorii Dane) – wystąpił inny niż powyższe błąd związany z danymi; Kod – pomyłki w kodzie, np. literówki; zwykle wyłapywane przez kompilator, ale w niektórych sytuacjach, np. związanych ze skryptami w przeglądarkach, mogą się zdarzać; Dokumentacja – system działa poprawnie, ale dokumentacja przewiduje inne jego zachowanie w danych okolicznościach; Standardy – system nie spełnia określonych norm, standardów czy innych regulacji; Inne – inna niż powyższe przyczyna defektu; Duplikat – dwa raporty o defektach opisują ten sam błąd; To nie problem – zaobserwowane zachowanie jest poprawne; Zewnętrzny – błąd pochodzi z zewnątrz i jest niezależny od testowanego programu (np. rzadka awaria sprzętu); Potrzebna analiza przyczyny – defekt został zamknięty, ale nie przeprowadzono analizy przyczyny podstawowej; Niewytłumaczalny – nikt nie wie, jaki jest powód awarii. Black [93] podaje również inny przykład taksonomii, skupiony na symptomach błędów, w przeciwieństwie do powyższej, skupionej raczej na przyczynach defektów: błędna funkcjonalność; brakująca funkcjonalność; zła użyteczność; nieudana kompilacja (build); zły projekt/architektura systemu; problem z niezawodnością; utrata danych; problem z efektywnością; przestarzały kod; odchylenie od specyfikacji;

zła dokumentacja użytkownika. Przedstawiona taksonomia jest dosyć ogólna, ale prawdopodobnie lepiej niż wcześniejsza pozwala na projektowanie przypadków testowych. W razie potrzeby lub jeśli organizacja ma dokładne dane historyczne z poprzednich projektów oraz wykonywała analizę przyczyny podstawowej, można zaadoptować którąś z istniejących taksonomii i ją uszczegółowić lub zbudować swoją własną. Taksonomie defektów służą nie tylko do projektowania testów lecz także wykorzystuje się je także w procesie udoskonalania procesu testowego (patrz podrozdz. 33.2).

10.3. Zgadywanie błędów 10.3.1. Opis metody Zgadywanie błędów to technika projektowania przypadków testowych na podstawie intuicji i doświadczenia testera. Po raz pierwszy została opisana przez Myersa [10]. Polega na domyśleniu się (zgadnięciu), jakie pomyłki mogły zostać popełnione przez programistę i w jaki sposób można je ujawnić. Metodę można wykorzystywać, wspomagając się taksonomią błędów. Skuteczność metody zależy od indywidualnych cech testera – jego intuicji, doświadczenia, znajomości systemu oraz programistów i tego, jakie błędy najczęściej popełniają. zgadywanie błędów – technika projektowania testów, gdzie, bazując na doświadczeniu testera, przewiduje się, jakie defekty, będące efektem wykonanych pomyłek, mogą być obecne w testowanym module lub systemie i projektuje się testy tak, aby je ujawnić Technika zgadywania błędów może być elementem formalnego, zaplanowanego procesu testowego. Wynikiem stosowania techniki są bowiem konkretne przypadki testowe podlegające udokumentowaniu. Zgadywanie błędów jest również metodą stosowaną podczas analizy ryzyka (patrz rozdz. 19).

10.3.2. Przykład

Rozważmy system ELROJ z Dodatku A. Dalej są opisane testy dla poszczególnych funkcji systemu, jakie mógłby wymyślić tester, „zgadując” możliwe defekty. void setInfo(String info) – funkcja pozwala na ustawienie komunikatu. Przykładem testu mogłoby być wywołanie funkcji dla pustego łańcucha znaków, łańcucha dłuższego niż miejsce na ekranie służące do jego wyświetlenia czy też łańcucha zawierającego znaki specjalne. void removeInfo() – funkcja usuwająca aktualnie wyświetlany komunikat. Test mógłby polegać na dwukrotnym wywołaniu funkcji, zmuszając program do usunięcia komunikatu w sytuacji, gdy komunikatu nie ma. void addLine(int line) – funkcja dodająca do rozkładu linię line. Test mógłby polegać na wywołaniu funkcji dla linii, która już istnieje w rozkładzie i ma jakieś kursy. void addBus(int line, int mm) – funkcja dodająca do rozkładu czas mm przyjazdu autobusu linii line. Przykładem testu mogłaby być próba wywołania funkcji z ujemną wartością dla parametru line oraz mm, wywołanie dla mm = 60 oraz dla mm > 60. Przy okazji, tester mógłby zgłosić niekompletność specyfikacji, bo nie wynika z niej, jak ma się zachować program dla takich parametrów mm – czy traktować je jako błędne, czy brać je modulo 60. void setActualDate(int day, int month, int year) – funkcja ustawia aktualną

datę pokazywaną

u góry

ekranu elektronicznego. Tester może

spróbować wywołać ją np. dla 29 lutego dla roku nieprzestępnego, który jest podzielny przez 100 i nie dzieli się przez 400.

10.4. Testowanie oparte na liście kontrolnej Testowanie oparte na liście kontrolnej (ang. checklist) jest w swej koncepcji nieco podobne do testowania opartego na taksonomii. Różnica jest taka, że taksonomia opisuje defekty, a lista kontrolna – cechy czy właściwości programu, które należy zbadać. Ponadto lista kontrolna jest zwykle skoncentrowana wokół jednego

tematu, np. kwestii bezpieczeństwa, wydajności, standardów, danych. Lista kontrolna może być uaktualniana o nowe aspekty wraz z nabywaniem przez zespół doświadczenia w wytwarzaniu i testowaniu oprogramowania. Listy kontrolne mogą również powstawać jako efekt uboczny analizy ryzyka jakościowego (patrz rozdz. 19). Tabela 10.2. Lista kontrolna dla sprawdzenia użyteczności strony www

Nr Element 1

Nawig acja – czy nawig acja między s tronami jes t widoczna i łatwa w użyciu dla użytkownika?

2

Etykiety – czy opis y i ikony przycis ków s ą zrozumiałe?

3

Kolorys tyka – czy kolorys tyka s trony jes t s pójna i wyg odna dla użytkownika?

4

Liczba obiektów – czy s trona nie jes t przeładowana zbyt wieloma elementami?

5

S truktura – czy s truktura drzewias ta s trony jes t wyważona?

6

S pójnoś ć – czy analog iczne elementy s trony s ą przeds tawiane w taki s am s pos ób?

7

Ods yłacze – czy ws zys tkie ods yłacze działają i s ą widoczne na s tronie?

8

Tytuł – czy s trona g łówna i ws zys tkie pods trony mają poprawne tytuły?

9

Drukowanie – czy każda s trona ma s woją wers ję do druku i czy drukuje s ię poprawnie?

Efektywnoś ć – czy s trona oraz jej pods trony wczytują s ię 10 odpowiednio s zybko? Czy s trony nie s ą przeładowane g rafiką mog ącą opóźniać wczytywanie s trony?

Lista kontrolna pomaga w systematycznym testowaniu, dzięki czemu tester pamięta o wszystkich aspektach programu, które należy przetestować. Można zdefiniować kryterium pokrycia, wymagając, aby dla każdego elementu listy kontrolnej stworzono co najmniej jeden test. Często jednak listy kontrolne mogą być zdefiniowane bardzo ogólnie i często właściwe przetestowanie jednego tylko elementu tej listy wymaga stworzenia wielu testów. Hipoteza błędów w tej technice mówi, że błędy pojawiają się najczęściej w obszarach opisanych listą kontrolną. Tabela 10.2 jest przykładową listą kontrolną dla testowania użyteczności strony www.

10.5. Testowanie eksploracyjne 10.5.1. Opis metody Testowanie eksploracyjne (nazywane również testowaniem ad hoc) należy do metod opartych na doświadczeniu. Może wykorzystywać taksonomię lub listę kontrolną, jednak zasadniczo różni się od tych technik pod jednym względem: jest metodą dynamiczną, w której występuje bardzo duża interakcja między testerem a testowanym programem. Polega ona na jednoczesnym uczeniu się programu, obserwowaniu jego zachowania, projektowania i przeprowadzania testów. Tester na bieżąco decyduje, którą „ścieżką” użytkowania programu podążyć tak, aby z możliwie jak największym prawdopodobieństwem doprowadzić do awarii lub błędu. Skuteczność techniki zależy od umiejętności, intuicji i doświadczenia testera oraz umiejętnego wyszukiwania, rozpoznawania oraz podążania za śladami mogącymi wskazywać na jakieś nieprawidłowości w testowanym programie. testowanie eksploracyjne (ang. exploratory testing) – nieformalna technika projektowania testów, w której tester projektuje testy w czasie, gdy są one wykonywane i wykorzystuje informacje zdobyte podczas testowania do projektowania nowych i lepszych testów [134] W testowaniu eksploracyjnym nie projektuje się testów na podstawie modeli, tak jak ma to miejsce w klasycznych metodach opartych na specyfikacji czy strukturze. Również element planowania nie występuje tak silnie, jak w innych technikach. Testowanie odbywa się „tu i teraz”, choć zwykle w ustalonych

uprzednio ramach czasowych. Jeśli mówimy o planowaniu, to raczej odbywa się ono na wyższym poziomie niż planowanie przypadków testowych i jest związane raczej z określaniem, kiedy i w jakim zakresie mają zostać przeprowadzone sesje testowania eksploracyjnego. Na testowanie eksploracyjne składa się wiele aktywności [135]: eksploracja testowanego produktu – zdefiniuj cel oraz funkcje produktu, typ przetwarzanych danych, obszary potencjalnej niestabilności; zdolność do przeprowadzenia testów eksploracyjnych zależeć będzie od znajomości technologii, informacji o produkcie i jego potencjalnych testowanie;

użytkownikach

oraz

czasu

projektowanie testów – określ strategię obserwowania i oceny testowanego programu;

przeznaczonego sposobu

na

używania,

wykonanie testów – zacznij pracę z programem, obserwuj jego zachowanie, użyj tej informacji do sformułowania hipotez o tym, jak produkt działa; heurystyki – czyli wskazówki lub inżynierskie reguły pomagające w zdecydowaniu, co powinniśmy jako testerzy zrobić w danej sytuacji; pomagają odpowiedzieć na pytanie, co przetestować i w jaki sposób; ocenialne rezultaty – tester musi udokumentować przebieg sesji testowania eksploracyjnego, czyli jakie czynności wykonał i jakie awarie znalazł. Sesja testowania eksploracyjnego często zaczyna się od tzw. karty testu (ang. test charter), zwanej czasem kartą opisu testu lub kartą opisu, w której opisuje się cel sesji oraz – być może – opis techniki, którą chcemy zastosować. Karta testu może być stworzona przez testera lub może zostać narzucona mu przez kierownika testów. Poziom formalizacji w stosunku do karty testów może być bardzo różny – od formalnych, spisywanych dokumentów, po reprezentowanie ich za pomocą dokumentacji opisującej na wysokim poziomie przypadki i procedury testowe. karta opisu testu, karta testu, karta opisu (ang. test charter) – dokument zawierający deklarację celów testu oraz ewentualne pomysły na testowanie. Karty testów są często wykorzystywane w testowaniu eksploracyjnym

sesja testowa nieprzerywalny

(ang. okres

test session) – w testowaniu eksploracyjnym czasu poświęcony testowaniu. Każda sesja jest

zorientowana na kartę testu, ale tester może w tym czasie także odkrywać nowe możliwości lub problemy z oprogramowaniem. Tester tworzy i wykonuje przypadki testowe w locie oraz zapisuje ich postęp Testowanie eksploracyjne jest często techniką niedocenianą. Wynikać to może co najmniej z dwóch powodów: po pierwsze, jej skuteczność najpełniej objawia się, gdy jest stosowana przez doświadczonych testerów. Po drugie, nie jest metodą wymagającą głębokiego planowania, projektowania przypadków testowych przed ich wykonaniem, silnej formalizacji oraz przesadnie dużego narzutu dokumentacyjnego. To może rodzić u kierowników testów obawy, że sesje testowania eksploracyjnego trudno poddają się kontroli oraz ocenie skuteczności. Z punktu widzenia menedżera coś, czego nie da się zaplanować, kontrolować i ocenić, jest działaniem ryzykownym, stąd niechętnie wykorzystywanym. Jak zobaczymy w podrozdziale 23.3, większość tych obaw jest bezzasadna. zarządzanie testowaniem w sesjach (ang. session-based test management) – metoda pomiaru i zarządzania testowaniem w sesjach, np. w testowaniu eksploracyjnym Testowanie eksploracyjne najlepiej stosować, gdy [136]: w krótkim czasie i w rozsądny sposób chcemy ocenić poziom jakości oprogramowania; wymagania są niekompletne lub jest ich brak; struktura programu jest skomplikowana, ma on wiele opcji, trybów działania i nie do końca jesteśmy w stanie zastosować systematyczną technikę projektowania testów; projekt jest prowadzony w metodyce zwinnej, z krótkimi sprintami i często zmieniającymi się wymaganiami; system jest gotowy, istnieje GUI, można przeprowadzać testy end-to-end i chcemy przeprowadzać testy systemowe lub akceptacyjne. Testowanie eksploracyjne nie sprawdzi się jednak, jeśli:

oczekujemy odkrycia błędów określonego typu (np. przez stosowanie tablic decyzyjnych w celu wykrycia błędów w logice biznesowej); testy mają być wartościowymi elementami suity testów regresyjnych; chcemy mieć informację o stopniu pokrycia testami testowanego systemu; system nie jest jeszcze gotowy, nie da się przeprowadzić testów end-toend, a chcemy już testować pojedyncze moduły lub przeprowadzać testy małej integracji.

10.5.2. Przykład sesji testowania eksploracyjnego Pokażemy teraz, jak może wyglądać sesja testowania ekspoloracyjnego w praktyce. Tester jest członkiem zespołu wykonującego testy systemowe programu MS Word 2007. Decyduje się na przetestowanie funkcji tworzenia odsyłaczy, czyli podpisów pod tabelami czy rysunkami. Karta testu przed rozpoczęciem sesji może wyglądać jak na rysunku 10.2.

Id sesji: TEXP.025.001 T ester: Jan Kowals ki Data: 13.07.2014 Czas rozpoczęcia: 11:45 Czas zakończenia: Cel: przetes towanie funkcjonalnoś ci tworzenia etykiet ods yłaczy, ws tawiania ods yłaczy oraz odwoływania s ię do nich w dokumencie Znalezione błędy: Kwestie do dalszej analizy: Rysunek 10.2. Karta testu przed rozpoczęciem sesji testowania eksploracyjnego Tester rozpoczyna sesję od przejrzenia menu Worda w celu odnalezienia wszystkich funkcji mogących mieć związek z celem naszego testu. Znajduje następujące elementy:

Wstawianie > Obraz oraz Wstawianie > Tabela (możemy sprawdzić, czy po wstawieniu rysunku można wstawić podpis, używając menu kontekstowego i czy podpis wstawi się we właściwym miejscu); Odwołania > Wstaw podpis (sprawdzimy funkcjonalność wstawiania podpisów); Odwołania > Odsyłacz (sprawdzimy, czy odwołania do elementów są poprawne); Odwołania > Wstaw spis ilustracji (sprawdzimy, czy są poprawnie tworzone spisy na podstawie istniejących w dokumencie podpisów). Tester wybiera opcję wstawienia podpisu. Zauważa, że jest możliwe stworzenie nowej etykiety, zatem tworzy etykietę o nazwie „Tablica” (rys. 10.3). Następnie tworzy w dokumencie podpis pod wyimaginowaną tablicą (rys. 10.4).

Rysunek 10.3. Stworzenie etykiety w MS Word

Rysunek 10.4. Stworzenie podpisu tablicy w MS Word Kolejny krok to stworzenie w tekście odsyłacza do tej tablicy (rys. 10.5). Intencją testera jest, aby tekst odwoływał się do tabeli przez jej numer: „Jak podano w tablicy 1.”. Niestety, po dodaniu odsyłacza tekst brzmi „Jak podano w tablicy Tablica 1”. Tester, po sprawdzeniu rodzajów odsyłaczy zauważa, że w programie MS Word nie da się tak zdefiniować odsyłaczy, by możliwe było odwołanie się jedynie do numeru tabeli, bez dodawania nazwy etykiety „Tabela”. Jest to pierwszy zauważony problem. Przeprowadzamy sesję eksploracyjną, dlatego tester postanawia przyjrzeć się mu dokładniej. Próbuje zmienić nazwę podpisu z „Tablica 1. Przykładowa tablica” na „1. Przykładowa tablica”, usuwając nazwę etykiety „Tablica”. Teraz odwołanie jest poprawne, ale z kolei podpis zaczyna się od liczby – nie ma w nim słowa „Tablica”. Tester próbuje następnie w podpisie zmienić wyrażenie „Przykładowa tablica” na „Przykładowa Tablica”. Okazuje się, że teraz tekst odsyłacza po wstawieniu go w tekście zmienił się na „1. Przykładowa Tablica”! Tester podejrzewa, że ma to związek z tym, że w nazwie tablicy użyto dokładnej nazwy etykiety („Tablica”, pisane z wielkiej litery). Uznaje to za błąd. Ostatecznie, po zakończeniu sesji karta testów zostaje uzupełniona i może wyglądać tak jak na rysunku 10.6.

Rysunek 10.5. Stworzenie w tekście odsyłacza do tablicy w MS Word

Id sesji: TEXP.025.001 T ester: Jan Kowals ki Data: 13.07.2014 Czas rozpoczęcia: 11:45 Czas zakończenia: 12:20 Cel: przetes towanie funkcjonalnoś ci tworzenia etykiet ods yłaczy, ws tawiania ods yłaczy oraz odwoływania s ię do nich w dokumencie Znalezione błędy: Użycie w podpis ie nazwy etykiety powoduje, w przypadku tworzenia ods yłacza w trybie „ tylko etykieta i numer” , umies zczenie w ods yłaczu całeg o podpis u, zamias t jedynie etykiety i numeru. MS Word nie potrafi odróżnić nazwy etykiety od teg o s ameg o s łowa, ale wys tępująceg o w treś ci podpis u. Kwestie do dalszej analizy: MS Word nie pozwala odwoływać s ię do etykiet tylko za pomocą numeru – w takim przypadku wymus za również wykorzys tanie nazwy etykiety. Zatem definiując podpis „ Tablica 1. XYZ” , nie uda nam s ię s tworzyć podpis u typu „ Jak

pokazano w tablicy 1” , lecz tylko „ Jak pokazano w tablicy Tablica 1.” . Rozwiązaniem może być us uwanie w treś ci podpis u nazwy etykiety, ale wtedy typy etykiet (rys unki, tabele, lis ting i itp.) będą nierozróżnialne, bo np. każdy pierws zy rys unek, tabela oraz lis ting będą zaczynały s woje nazwy od liczby „ 1” . Rysunek 10.6. Karta testu po zakończeniu sesji testowania eksploracyjnego

10.6. Ataki usterkowe Technika ataków usterkowych jest w pewnym sensie połączeniem opisanych we wcześniejszych rozdziałach technik opartych na defektach i doświadczeniu. W technice ataków usterkowych skupiamy się na testowaniu programu pod kątem wykrywania ściśle określonych typów błędów, podobnie jak w metodzie taksonomii. Atak usterkowy jest jednak metodą bardziej ustrukturyzowaną, gdyż opiera się na konkretnym modelu usterki, który opisuje, w jaki sposób usterki mogą pojawić się w programie i dlaczego objawiają się jako awarie. Bazując na modelu usterki, możemy wykorzystać istniejące listy możliwych do przeprowadzenia ataków, co upodabnia metodę do techniki opartej na liście kontrolnej. Przykłady takich list podał Whittaker w swoich znakomitych książkach: How to Break Software [137], How to Break Software Security [138] oraz How to Break Web Security [139]. atak, atak usterek, atak na oprogramowanie (ang. attack, fault attack, software attack) – ukierunkowane działanie mające na celu ocenę jakości, w szczególności niezawodności obiektu testów, przez wymuszenie wystąpienia określonej awarii Przykładowy model usterki jest pokazany na rysunku 10.7.

Rysunek 10.7. Model usterki powodowanej interakcją programu i środowiska Na rysunku 10.7 przedstawiono testowany program w jego otoczeniu. Każda interakcja programu z elementem otoczenia może być źródłem usterek i powodem awarii. Użytkownik bezpośrednio uczestniczy w nikłym odsetku interakcji zachodzących w systemie. Na przykład wejście wprowadzone przez użytkownika z klawiatury może przechodzić przez różne warstwy architektury systemu za pomocą API (ang. Application Programming Interface), powodować komunikację programu z bazą danych, systemem plików czy systemem operacyjnym (np. żądanie zasobów takich, jak miejsce na dysku, pamięć czy czas procesora). Dlatego model usterki niekoniecznie musi dotyczyć wyłącznie bezpośredniej interakcji użytkownika z testowanym programem. Wbrew temu, co większość ludzi myśli, ta komunikacja nie jest głównym aspektem testowania [137]. Whittaker [137] proponuje m.in. następujące kategorie ataków usterkowych: Ataki na interfejs użytkownika – wejścia i wyjścia wymuszenie pojawienia się wszystkich możliwych komunikatów o błędach, co zapewnia, że kod obsługuje sytuacje wyjątkowe; wymuszenie użycia przez program wartości domyślnych, co zapewni przetestowanie możliwie jak największej ilości wartości domyślnych

oraz sprawdzenie, czy program poprawnie inicjalizuje wartości zmiennych; użycie różnych zestawów znaków, typów kodowań (np. ASCII zamiast Unicode), słów zarezerwowanych, typów danych itp., co sprawdzi, jak program kontroluje poprawność wprowadzanych danych; wymuszenie przepełnienia bufora wejściowego, co sprawdza jak program obsługuje wejścia o zbyt dużym rozmiarze; przetestowanie kombinacji wejść, które mogą wchodzić ze sobą w interakcje; wielokrotne powtórzenie tego samego wejścia; wymuszenie wygenerowania różnych wyjść dla tego samego wejścia, podanego w różnych kontekstach; wymuszenie wygenerowania nieprawidłowych wyjść. Ataki na interfejs użytkownika – dane i obliczenia wprowadzenie wejścia w różnych okolicznościach (np. zapisanie pliku po zmianach oraz zapisanie pliku bez wprowadzania żadnych zmian); wymuszenie przechowania w strukturze danych zbyt wiele lub zbyt mało wartości; wymuszenie zmiany strukturę danych;

wewnętrznych

warunków

nałożonych

na

użycie niepoprawnych kombinacji operatorów i operandów (np. dzielenie przez zero, obliczenie pierwiastka z liczby ujemnej, wymuszenie błędów zaokrągleń – np. wykonanie w kalkulatorze Windows ciągu operacji: 2, √, ^2, ^2, –4 powinno dać w rezultacie 0, a daje 5.38 · 10–37); wymuszenie wywołania rekurencyjnego funkcji; wymuszenie zbyt dużego lub zbyt małego wyniku; znalezienie elementów źle współpracujących ze sobą lub nieprawidłowo współdzielących dane. Ataki na interfejs systemowy – system plików całkowite wypełnienie systemu plików, co sprawdzi, jak program radzi sobie w sytuacji, gdy dysk jest pełny;

wymuszenie zajętości lub niedostępności urządzenia lub innego obiektu;

procesu,

programu,

uszkodzenie pliku, programu, urządzenia lub innego obiektu; przypisanie nieprawidłowej nazwy pliku; zmiana praw dostępu do plików; zmiana lub wprowadzenie błędnej zawartości do pliku. Ataki na interfejs systemowy – interfejsy aplikacji/systemu operacyjnego wstrzykiwanie błędów wymuszających rzucanie wyjątków, naśladujących rzeczywiste awarie, błędy pamięci, błędy sieci itp.

10.7. Testowanie ad hoc Testowanie ad hoc polega na nieformalnym wykonywaniu testów. Nie stosuje się żadnych technik projektowania przypadków testowych, nie występuje tu żaden element planowania bądź raportowania. Wykonaniem testu kieruje zupełna dowolność. Zwykle też nie wytwarza się żadnej dokumentacji (np. o incydentach). Niektórzy autorzy używają nazwy „testowanie ad hoc” na określenie testowania eksploracyjnego, jednak o ile w testowaniu eksploracyjnym tester postępuje jednak według pewnych reguł, o tyle w testowaniu ad hoc nie ma absolutnie żadnych formalnych zasad. W szczególności testowanie ad hoc nie jest planowane i często nie powstają żadne raporty z przeprowadzenia takiej sesji testowej. testowanie ad hoc (ang. ad hoc testing) – testy wykonywane nieformalnie. Nie ma miejsca na żadne formalne przygotowanie testu, nie jest użyta żadna rozpoznawalna technika projektowania przypadków testowych, brak jest oczekiwań co do rezultatów, wykonaniem testu kieruje dowolność

1 Niektóre półprzewodniki są wysoce podatne na promieniowanie neutronowe czy promieniowanie alfa, wywołane np. radioaktywnymi izotopami U-238, Th232 lub Pb-210.

11. Wybór odpowiednich technik

Każdy projekt jest odmienny i ma indywidualne, niepowtarzalne cechy. Dlatego nie istnieje jeden, uniwersalny model wytwarzania oprogramowania, którym można skutecznie opisać wszystkie projekty. Kierownik projektu musi wybrać odpowiedni model, ale może też nieco zmodyfikować istniejącą metodykę i dostosować ją do swoich potrzeb. Znane są np. projekty wytwarzania oprogramowania o znaczeniu krytycznym oparte na metodykach zwinnych. W takich przypadkach należy dodać do modelu pewien niezbędny narzut dokumentacyjny, wprowadzić zarządzanie ryzykiem czy zdecydować się na dodatkowe etapy, takie jak przeglądy formalne. Proces testowy wynika z przyjętego modelu wytwórczego i testerzy muszą dostosować się do wybranego podejścia do cyklu życia oprogramowania (podrozdz. 4.1 ma na celu pomóc testerom w zorientowaniu się, jak proces testowy jest umiejscowiony w różnych modelach oraz co z punktu widzenia testowania jest specyficzne dla każdego z nich). Uwagi te odnoszą się również do samego wyboru technik testowania. Nie istnieje jedna, uniwersalna technika. Każda z przedstawionych w poprzednich rozdziałach metod jest skuteczna tylko w pewnych granicach i pozwala uzyskać przypadki testowe wyczerpujące jedno, określone kryterium pokrycia. Niektórzy autorzy są orędownikami konkretnych technik i w swojej pracy ograniczają się tylko do nich. Niestety, nie jest to dobre podejście. Na przykład testowanie eksploracyjne jest bardzo efektywną techniką, zwłaszcza, jeśli jest przeprowadzane przez doświadczonego testera. Jednak technika ta będzie zupełnie nieprzydatna w testowaniu pod kątem wykrywania tzw. koni trojańskich czy bomb logicznych, czyli fragmentów kodu mających na celu umożliwienie użytkownikowi dostęp do systemu z obejściem istniejących w nim zabezpieczeń lub wykonanie szkodliwych akcji. Tego typu defekty można

natomiast wykryć, stosując jedno z najprostszych kryteriów – kryterium pokrycia instrukcji. Doświadczony tester potrafi dobrać odpowiednie techniki (być może nawet tylko jedną lub dwie) i na ich podstawie skonstruować niezwykle skuteczne testy. Czasami przyjęty model wytwarzania oprogramowania jest bardzo specyficzny, np. łączy w sobie cechy kilku innych modeli. Wtedy pewną pomocą w wyborze podejścia do testowania może być Heurystyczny model strategii testowej opracowany przez Bacha [140]. Jest to zbiór wzorców dla projektowania strategii testowej (rys. 11.1).

Rysunek 11.1. Heurystyczny model strategii testowej Środowisko projektu to wszelkiego rodzaju zasoby oraz ograniczenia w projekcie mogące wpłynąć na proces testowania (np. na jego opóźnienie, utrudnienie). Elementy składowe produktu to rzeczy podlegające testowaniu. Odpowiadają one z grubsza warunkom testowym i elementom pokrycia w normie ISO/IEEE 29119-2 [32]. Kryteria jakości to wszystkie reguły, warunki, cechy i parametry produktu i procesu, które pozwalają nam stwierdzić, czy w produkcie

występują jakieś problemy, tzn. pozwalają nam ocenić go pod kątem jakości. Należy pamiętać, że jakość jest pojęciem wielowymiarowym i często trudnym do zdefiniowania. W formułowaniu kryteriów jakości bierze udział wielu interesariuszy – od klienta począwszy, przez analityków biznesowych, kierownika projektu i deweloperów, na testerach skończywszy. Techniki testowania wynikają z trzech opisanych wcześniej części składowych strategii. Bach nazywa je heurystykami, za pomocą których tworzy się przypadki testowe. Techniki te mogą również być bardzo ścisłe i dobrze sformułowane, często nie pozwalając testerowi na zbytnią swobodę w „inwencji twórczej” przy wymyślaniu testów. Stosując techniki testowania, uzyskujemy tzw. postrzeganą jakość. Nie jest to rzeczywista jakość produktu, bo tej nigdy nie możemy poznać w stu procentach. Jakość postrzegana to jakość, której poziom szacować możemy na podstawie tego, jakie testy zaprojektowaliśmy i jaki jest ich wynik. Bach wyróżnia następujące kategorie heurystyk testowych: testowanie funkcji, czyli tego, co program robi; testowanie stwierdzeń, czyli zdań opisujących produkt zawartych w dokumentacji, wymaganiach, podręcznikach użytkownika itp.; testowanie dziedziny, czyli danych oraz relacji między nimi; testowanie przez użytkownika, czyli zaangażowanie końcowego użytkownika produktu do testowania lub też symulowanie działania określonego typu użytkownika na rzeczywistych danych; testowanie wytrzymałościowe (ang. stress testing), czyli to, jak efektywnie program działa w warunkach różnego rodzaju obciążeń (zbyt mało pamięci, niska przepustowość sieci, duży rozmiar danych wejściowych itp.); testowanie ryzyka, tzn. wyobrażenie sobie jakiego rodzaju problemy mogą wystąpić podczas użytkowania, a następnie przetestować program pod tym kątem (jest to forma testowania opartego na defektach lub atakach usterkowych); testowanie sekwencji, czyli wykonywanie czynności w różnej kolejności i czasie, bez resetowania aplikacji między poszczególnymi akcjami; sprawdzanie automatyczne, czyli generacja dużej ilości różnych danych, próba pokrywania różnego rodzaju warunków

testowych/elementów

pokrycia,

automatyzacja,

wykorzystanie

wyroczni, generatorów danych itp.; testowanie scenariuszy, czyli forma testowania systemowego (end-toend) i akceptacyjnego.

12. Priorytetyzacja przypadków testowych

12.1. Wprowadzenie W większych projektach IT często zdarza się, że zestaw testów regresyjnych osiąga bardzo duże rozmiary i wykonanie go może być problematyczne. W [141] jest podany przykład rzeczywistej aplikacji przemysłowej liczącej 20 000 linii kodu, której testy mające spełniać kryterium MC/DC wykonywały się przez 7 tygodni (!). Dlatego zachodzi potrzeba priorytetyzacji i optymalizacji (redukcji) rozmiaru suit testowych przez wykonywanie najważniejszych testów na początku oraz usuwanie testów nieefektywnych. Ważność testu może wynikać z wielu czynników, ale najczęściej dotyczy jego skuteczności, tzn. tego jak dużo defektów jest w stanie wykryć. Formalnie, problem priorytetyzacji testów jest zdefiniowany w następujący sposób: mając suitę testową T, zbiór PT wszystkich możliwych permutacji T oraz funkcję f: PT → R, należy znaleźć taką kolejność wykonywania testów T′ ∈ PT, że ∀T″ ∈ PT, T″ ≠ T′ f(T″) ≥ f(T′). Należy więc znaleźć kolejność wykonywania testów, dla której wartość funkcji f jest najmniejsza. Funkcja f, nazywana funkcją kosztu, jest ilościowym wyrażeniem kryteriów priorytetyzacji, które przyjmujemy. Istnieje wiele praktycznych powodów, aby priorytetyzować testy: zwiększenie współczynnika wykrywania defektów przez suitę testową, tzn. zwiększenie prawdopodobieństwa, że defekty będą znajdowane możliwie jak najwcześniej (na początkowych etapach testów regresji); zwiększenie szybkości pokrycia określonego kryterium pokrycia tak, aby pokrycie kodu dokonywało się jak najwcześniej w całym procesie testowym;

wczesne zwiększenie pewności co do niezawodności systemu; przyspieszenie znajdowania najpoważniejszych defektów (tak, by były znajdowane przed defektami mniej istotnymi); zwiększenie prawdopodobieństwa wykrywania defektów związanych ze zmianami w kodzie we wczesnych fazach testów regresyjnych. W tym rozdziale interesować nas będzie pierwszy powód, mianowicie jak najwcześniejsze wykrywanie defektów. Priorytetyzacja wymaga wiedzy o tym, jak efektywne w wykrywaniu defektów są poszczególne testy. Informacja ta zwykle nie jest dostępna, gdy do tworzenia testów wykorzystuje się techniki nieoparte na defektach (np. dotyczące pokrycia kodu). W takim przypadku stosuje się heurystyki, np. rozmiar pokrycia, szacowaną podatność kodu na błędy, złożoność kodu, zmiany w wymaganiach lub kodzie, profil operacyjny czy analizę ryzyka. Jeśli testy są tworzone za pomocą techniki opartej na defektach, możliwości wykrywania określonych defektów przez poszczególne testy są znane. Informacja ta może być wykorzystana przy priorytetyzacji. Priorytetyzację można stosować w przypadku testów regresji, ale też we wczesnych fazach testowania. Różnica polega na tym, że testy regresji są uruchamiane wielokrotnie, zatem mamy dodatkową informację z poprzednich przebiegów o tym, jak efektywne były poszczególne testy. We wczesnych fazach testowania nie dysponujemy takimi danymi i możemy opierać się jedynie na heurystykach lub danych historycznych z innych projektów.

12.2. Ocena priorytetyzacji – miara APFD Najczęściej wykorzystywaną miarą do oceny skuteczności kolejności testów w suicie jest tzw. miara APFD (ang. Average Percentage of Faults Detected). Jest ona zdefiniowana następująco: niech będzie dana suita testowa T zawierająca n testów i wykrywająca m defektów. Niech T′ oznacza ustaloną kolejność testów z T, a Ti niech będzie numerem pierwszego (w kolejności wykonywania) testu w uporządkowanym zbiorze T′, który wykrywa i-ty defekt. Wtedy

Miara APFD przyjmuje wartości od 0 do 11. Im większa jej wartość, tym szybciej dane uporządkowanie testów wykrywa defekty. Rozważmy przykładowy program z suitą testową T zawierającą 6 testów, które w sumie wykrywają 10 defektów. W tabeli 12.1 pokazano, który test wykrywa które defekty. Tabela 12.1. Przypadki testowe i usterki przez nie wykrywane

Usterka Tes t

1

2

PT1

X

X

3

4

5

6

7

X

X

X

X

X

8

9

X

X

10

X

PT2 PT3

X

PT4

X

PT5

X

PT6

X

X

X

X

X

Rozważmy następujące dwie kolejności przypadków testowych z tej suity: T′ = (PT1, PT2, PT3, PT4, PT5, PT6); T″ = (PT3, PT5, PT6, PT1, PT2, PT4). W T′ pierwszy test, PT1, wykrył 30% usterek (3 z 10). Drugi nie wykrył nic, trzeci wykrył dodatkowe 4 usterki, zatem pierwsze trzy testy w sumie wykryły 70% usterek. Test czwarty nie wykrył żadnej nowej usterki – oba wykryte przez niego defekty zostały znalezione już wcześniej. Test piąty ujawnił 2 nowe usterki, a test ostatni – jedną nową usterkę. Usterki 1, 2 i 4 zostały po raz pierwszy znalezione w teście nr 1, zatem T1 = T2 = T4 = 1. Usterki 3, 5, 6, 7 zostały po raz pierwszy w teście trzecim, stąd T3 = T5 = T6 = T7 = 3. Podobnie, mamy T8 = T9 = 5 oraz T10 = 6. Zatem APFD(T″) =

W przypadku kolejności T″ mamy

. Zatem kolejność T″ jest lepsza od T′, ponieważ szybciej znajdziemy wszystkie 10 defektów, ustawiając testy w kolejności T″ niż

stosując kolejność T′. Miarę tę można zinterpretować geometrycznie. Na rysunku 12.1 przedstawiono miary dla T′ i T″ jako pola figur (oznaczone szarym kolorem) wyznaczonych funkcjami opisującymi, jak szybko zostaną wykryte nowe defekty w kolejnych testach. Numery testów są opisane na osi X. Z kolei na osi Y jest zaznaczona liczba wykrytych defektów. Dla zadanej kolejności funkcja jest zdefiniowana następująco: w zerze przyjmuje wartość zero, a w punkcie i przyjmuje wartość z punktu i – 1 powiększoną o liczbę nowych defektów znalezionych przez i-ty test. Na przykład, rysunek po lewej stronie odpowiada kolejności T′. Pierwszy test znalazł 3 nowe defekty, zatem w pierwszym kroku funkcja skacze o 3 jednostki do góry. Drugi test nie znalazł żadnego nowego defektu, zatem w drugim kroku funkcja nie zmienia swojej wartości. W trzecim teście znaleziono 4 nowe defekty, więc funkcja skacze o 4 jednostki do góry i tak dalej. Im szybciej funkcja osiągnie wartość 10 (równą liczbie wszystkich wykrytych przez suitę testową defektów), tym większe będzie pole pod jej wykresem. Można sprawdzić, że pole to jest dokładnie równe wartości metryki APFD.

Rysunek 12.1. Geometryczna interpretacja miary APFD

Mając dane historyczne lub z poprzedniego przebiegu testu regresji, możemy wykorzystać miarę APFD do priorytetyzacji przy użyciu prostego algorytmu zachłannego: w kolejnych krokach wybieramy ten test, który znajduje najwięcej „niepokrytych” dotąd defektów. Dzięki temu wzrost funkcji z rysunku 12.1 będzie najszybszy. W naszym przykładzie algorytm zachłanny zwróciłby kolejność T″. W takim przypadku moglibyśmy zdecydować o zredukowaniu suity testowej do testów PT3, PT5 i PT6, ponieważ trzy pozostałe nie wykrywają żadnych nowych defektów. Takie decyzje należy jednak zawsze podejmować z ostrożnością, gdyż może się okazać, że np. w wyniku wadliwych zmian w kodzie defekt mógłby być znaleziony tylko przez jeden z usuniętych testów.

12.3. Techniki priorytetyzacji Miara APFD jest tylko jedną z możliwych ocen priorytetyzacji. W literaturze przedmiotu (np. [142]) zdefiniowano wiele różnych kryteriów służących do ustalania kolejności testów, takich jak: kolejność losowa (służy głównie do porównań z innymi technikami); kolejność optymalizująca wykrywanie defektów (miara APFD); kolejność według pokrycia instrukcji; kolejność według pokrycia niepokrytych dotąd instrukcji; kolejność według prawdopodobieństwa wykrycia defektów (można wykorzystać technikę testowania mutacyjnego do oszacowania tzw. potencjału wykrywania usterek [143]); jw., ale dodatkowo uwzględniając dane z poprzednich przypadków testowych; kolejność według pokrycia funkcji; kolejność według niepokrytych dotąd funkcji; kolejność wykorzystująca taksonomię opartego na specyfikacji ([144]).

defektów

dla

testowania

i wiele innych. Eksperymenty pokazują, że stosowanie technik priorytetyzacji może efektywnie zredukować rozmiar suity testowej do ok. 70% jej początkowego rozmiaru bez dużej utraty mocy wykrywania defektów.

1 Ściśle rzecz biorąc, miara APFD przyjmuje wartości od do . Można ją jednak znormalizować tak, aby przyjmowała wartości od 0 do 1, tzn. zdefiniować .

Część III Testowanie charakterystyk jakościowych

Jakość nie jest działaniem, lecz nawykiem Arystoteles Be a yardstick of quality. Some people aren’t used to an environment where excellence is expected Steve Jobs

Część

trzecia

książki

jest

poświęcona

testowaniu

aspektów

jakościowych

oprogramowania. Atrybuty jakościowe są często pomijane w specyfikacjach, które ograniczają się głównie do wymagań biznesowych. Klient zwykle zwraca uwagę na aspekt funkcjonalności oprogramowania, ale jakość niezwiązana bezpośrednio z funkcjonalnością ma równie wielki wpływ na postrzeganie przez niego końcowej jakości produktu. Dlatego testowanie charakterystyk jakościowych (czyli tzw. testowanie niefunkcjonalne) jest tak samo istotne jak testowanie funkcjonalne. W rozdziale 13 przedstawimy krótko model jakości wg ISO 9126. Jest on punktem odniesienia dla wielu metod i technik zapewniania jakości. Od około 2010 roku jest zastępowany przez rodzinę norm ISO 25000, ale ze względu na jego historyczne znaczenie (a także fakt, że do modelu tego odwołują się sylabusy ISTQB) poświęcimy mu nieco miejsca. Rozdział 14 stanowi wprowadzenie w zagadnienia jakości oprogramowania. Przedstawione zostaną tu modele jakości proponowane przez normę ISO 25010. W kolejnych rozdziałach omówimy szczegółowo te modele. W rozdziale 15 skupimy się na jakości użytkowej, czyli – mówiąc ogólnie – na tym, jak użytkownik postrzega oprogramowanie jako całość. Rozdział 16 jest poświęcony omówieniu jakości produktu. To właśnie charakterystyki jakościowe produktu, takie jak wydajność, użyteczność czy niezawodność, są najczęściej kojarzone z pojęciem testowania niefunkcjonalnego. Na koniec, w rozdziale 17 omówimy charakterystyki jakościowe danych. Obecnie systemy informatyczne kładą bardzo duży nacisk na przetwarzanie danych, stąd potrzeba potraktowania ich jakości jako odrębnego zagadnienia.

13. Model jakości według ISO 9126

Norma ISO 9126 składa się z 4 części: ISO 9126-1: Quality model – opisująca model jakości w postaci charakterystyk jakościowych oprogramowania; ISO 9126-2: External metrics – opisująca tzw. metryki zewnętrzne, odnoszące się do jakości zewnętrznej, czyli wymagań na oprogramowanie wyprowadzonych z potrzeb użytkownika; ISO 9126-3: Internal metrics – opisująca tzw. metryki wewnętrzne, odnoszące się do jakości wewnętrznej, czyli wymagań technicznych na oprogramowanie; ISO 9126-4: Quality in use metrics – opisująca metryki dla jakości użytkowej, odnoszące się do: efektywności, produktywności, zabezpieczeń (ang. security) i bezpieczeństwa (ang. safety). Na rysunku 13.1 przedstawiono model jakości ISO 9126. Nie będziemy dokładnie omawiać modelu ISO 9126, ponieważ w następnym rozdziale opiszemy dokładnie jego następcę, model ISO 25000. Normy ISO 9126-2, 9126-3 i 9126-4 zawierają obszerny katalog metryk pozwalających ilościowo mierzyć charakterystyki jakościowe oprogramowania. Niektóre z tych metryk opiszemy w rozdziale 42.

Rysunek 13.1. Model jakości według ISO 9126

14. Modele jakości według ISO 25010

Testowanie charakterystyk jakościowych opiszemy na podstawie nowej rodziny norm ISO 25000, która zastępuje starą normę ISO 9126 [3]. Twórcy normy ISO 9126 inspirowali się tzw. modelem jakości McCalla [145]. Jest to chyba pierwszy model jakości w historii inżynierii oprogramowania. Jest przedstawiony na rysunku 14.1. Zanim przejdziemy do omówienia ISO 25010, opiszemy model McCalla, ze względu na jego historyczne znaczenie.

Rysunek 14.1. Model jakości McCalla Model jakości McCalla został stworzony dla Sił Powietrznych USA, aby wypełnić lukę między użytkownikami a twórcami systemów. Model stara się wiązać punkt widzenia użytkownika z priorytetami programistów. Definiuje 3 perspektywy charakteryzujące jakość tworzonego systemu: rewizję projektu (czyli zdolność do zmian w produkcie), przekazanie projektu (czyli możliwość adaptacji do nowego środowiska) oraz używanie projektu (związane z podstawowymi charakterystykami operacyjnymi). Każdej perspektywie jest przypisanych kilka czynników jakości (w sumie 11). Model ten, choć został zdefiniowany w 1977 roku, nie stracił na aktualności. Jak zobaczymy, idee zawarte w modelu McCalla są stale

obecne we wszystkich innych, bardziej współczesnych modelach jakości. Jest tak dlatego, że model McCalla opisuje w pewnym sensie uniwersalne cechy jakościowe systemów, które nie zmienią się niezależnie od postępu technologicznego. Norma ISO 25010 [2] wyróżnia dwa modele jakości (ang. quality), znane również w literaturze jako „duże Q” i „małe q”. Pierwszy dotyczy cech produktu istotnych z punktu widzenia użytkownika końcowego, stąd jego nazwa: jakość użytkowa (ang. quality in use). Opisuje jakość z punktu widzenia interakcji człowiek– komputer. Model drugi jest związany z jakością produktu z punktu widzenia procesu wytwórczego. Dotyczy „technicznych” cech produktu. Norma ISO 25010 nazywa ten typ jakości jakością produktu (ang. system/software product quality). Model jakości produktu odnosi się do statycznych własności oprogramowania i dynamicznych charakterystyk systemów komputerowych. Norma ISO 25012 [146] opisuje z kolei model jakości danych, dzieląc poszczególne charakterystyki dotyczące danych na dwie zachodzące na siebie grupy: wrodzone oraz zależne od systemu. Na rysunku 14.2 przedstawiono model jakości użytkowej, na rysunku 14.3 – model jakości produktu, a na rysunku 14.4 – model jakości danych.

Rysunek 14.2. Model jakości użytkowej według ISO 25010

Model jakości użytkowej opisuje pięć głównych charakterystyk, które mogą podlegać testowaniu: efektywność, wydajność, satysfakcję, wolność od ryzyka oraz kontekst charakterystyk w działaniu,

użycia.

Model jakości

produktu opisuje osiem

głównych

technicznych: funkcjonalną odpowiedniość, wydajność zgodność, użyteczność, niezawodność, bezpieczeństwo,

utrzymywalność oraz przenaszalność. Większość z charakterystyk

Rysunek 14.3. Model jakości produktu według ISO 25010

jest złożona z podcharakterystyk. W kolejnych rozdziałach opiszemy dokładnie wszystkie charakterystyki i podcharakterystyki, a także metody ich testowania. Model jakości danych nie jest rozłączny z modelami jakości w użyciu oraz jakości produktu. Jak widać, niektóre charakterystyki modelu jakości danych występują także w pozostałych modelach, np. wydajność, poufność czy dostępność. Jest tak dlatego, że jakość danych jest elementem jakości całego oprogramowania lub systemu. Niektóre cechy jakościowe właściwe danym (np. dokładność czy precyzja) mogą być traktowane jako składowa użyteczności lub funkcjonalnej odpowiedniości systemu.

Rysunek 14.4. Model jakości danych według ISO 25012 Model jakości służy testerowi do definiowania celów testowania (głównie niefunkcjonalnego). Można go traktować jak listę kontrolną charakterystyk podlegających weryfikacji, na podstawie których tworzy się kolejno testy sprawdzające poszczególne cechy systemu. Model jakości opisuje całościowo jakość oprogramowania bądź systemu komputerowego. Charakterystyki jakości

można mierzyć za pomocą metryk jakości. W części VII opiszemy przykładowe metryki dla różnych atrybutów jakościowych oprogramowania. atrybut

jakościowy,

charakterystyka

jakości

oprogramowania,

charakterystyka jakościowa, charakterystyka oprogramowania (ang. quality attribute, software quality characteristic, quality characteristic, software product characteristic) – cecha lub właściwość, która wpływa na jakość obiektu [7]

15. Testowanie jakości użytkowej

Jakość użytkowa jest cechą gotowego oprogramowania, dotyczy bowiem aspektów jakościowych interakcji człowiek–komputer. Dlatego testowanie jakości użytkowej ma zazwyczaj charakter testów akceptacyjnych (testy alfa, testy beta). Ze względu na to, że poziom jakości użytkowej jest odczuwany przez człowieka, testowanie jej charakterystyk odbywa się zwykle za pomocą kwestionariuszy i ankiet. W ramach jakości użytkowej można wyróżnić trzy podcharakterystyki: efektywność, wydajność oraz satysfakcję. Każda charakterystyka jakościowa produktu (patrz rozdz. 16) oraz danych (patrz rozdz. 17) ma wpływ na jakość użytkową.

15.1. Testowanie efektywności (effectiveness) Efektywność to dokładność, z jaką użytkownik osiąga za pomocą testowanego programu założone cele. Zauważmy, że nie chodzi tu o dokładność „techniczną” (np. dokładność obliczeń czy danych). Jest to cecha dotycząca celów użytkownika. Jeśli na przykład użytkownik systemu ELROJ (patrz Dodatek A) może wykonać wszystkie operacje opisane w wymaganiach R01–R07, to możemy uznać, że osiąga swoje cele z wysoką dokładnością, czyli oprogramowanie jest dla niego efektywne. Testowanie efektywności można przeprowadzić, używając obustronnego śledzenia testów do wymagań, które testy te pokrywają. efektywność (ang. efficiency) – zdolność procesu do tworzenia zamierzonych wyników w zależności od ilości użytych zasobów

15.2. Testowanie wydajności (efficiency) Wydajność jest to ilość zasobów, jakie wykorzystuje użytkownik w stosunku do dokładności, z którą osiąga założone cele. Przykładem takich zasobów może być czas do ukończenia zadania, materiały lub finansowy koszt użycia. Jeśli usunięcie kursu linii autobusowej w systemie ELROJ trwa średnio 10 sekund, to taki system jest wydajniejszy, niż gdyby trwało to 1 minutę, np. ze względu na konieczność „przeklikiwania się” przez aplikację. Jeśli komunikacja programu z ekranem na przystanku autobusowym wymaga dodatkowego sprzętu np. w postaci jakiegoś elektronicznego interfejsu, to wydajność związana z użyciem materiałów jest niższa niż gdyby system takiego interfejsu nie wymagał. Wydajność można testować, mierząc np. czas zużyty przez użytkowników na wykonanie określonych akcji czy zadań w programie. wydajność, charakterystyka czasowa (ang. performance, time behavior) – stopień, w jakim system lub moduł realizuje swoje wyznaczone funkcje w założonych ramach czasu przetwarzania i przepustowości [7]

15.3. Testowanie satysfakcji (satisfaction) Satysfakcja jest to stopień, w jakim potrzeby użytkownika są spełnione w wyniku użytkowania oprogramowania. Jest to cecha bardzo zindywidualizowana, tzn. każdy użytkownik może odczuwać inny poziom satysfakcji, korzystając z tego samego oprogramowania w taki sam sposób. Satysfakcję trudno zmierzyć, gdyż dotyczy ona indywidualnych osób. Można ją testować np. przez przeprowadzenie wśród użytkowników ankiet dotyczących różnych aspektów satysfakcji, takich jak: przydatność, zaufanie, przyjemność czy komfort.

15.3.1. Przydatność (usefulness) Przydatność to stopień satysfakcji użytkownika wypływającej z postrzeganego przez niego stopnia osiągnięcia celów. Cel dotyczy zwykle efektów działania programu i jest związany z konkretnymi funkcjonalnościami oferowanymi przez program.

15.3.2. Zaufanie (trust) Zaufanie to przekonanie o tym, że system będzie zachowywał się poprawnie. Na poziom zaufania wpływ ma wiele czynników, takich jak: odporność na błędy (jeśli użytkownik co chwilę otrzymuje komunikaty o błędach lub ostrzeżenia, jego zaufanie do oprogramowania będzie niskie), informowanie użytkownika o stanie systemu i aktualnie wykonywanych akcjach, możliwość odzyskania danych po awarii (np. dzięki funkcji autozapisu) itd. Wysoki poziom zaufania do oprogramowania objawia się tym, że użytkownik nie boi się wykonać określonych akcji w programie, bo wie („czuje”), że system czuwa nad bezpieczeństwem danych oraz całego procesu. Zaufanie jest kluczowym atrybutem w systemach o znaczeniu krytycznym, np. takich, jak oprogramowanie maszyny

Therac-25

(patrz

podrozdz.

1.3)

wykorzystywanej

do

terapii

nowotworów. Pacjent poddawany terapii musi być przekonany, że system będzie działał poprawnie i np. nie spowoduje omyłkowo podania śmiertelnej dawki promieniowania.

15.3.3. Przyjemność (pleasure) Przyjemność to uczucie radości z użytkowania oprogramowania. Być może najlepszym przykładem tej charakterystyki jest tzw. grywalność gier komputerowych. Wysokim poziomem przyjemności cechuje się np. bardzo popularna w ostatnich latach seria gier Angry Birds czy też gry sieciowe, takie jak Call of Duty. Przyjemność jest bardzo ważnym atrybutem jakości, bo to ona sprawia, że użytkownik ma ochotę używać danej aplikacji. W przypadku produktów z półki przekłada się to oczywiście na większą sprzedaż, lepsze wyniki finansowe i lepsze postrzeganie marki producenta.

15.3.4. Komfort (comfort) Komfort to przyjemność odczuwana z powodu użytkowania systemu. Pracując z dobrze zaprojektowanym programem o wysokim poziomie komfortu, użytkownik ma wrażenie, że system ten skonstruowano specjalnie dla niego; sposób jego użytkowania jest zgodny z intencją użycia. Zarówno komfort, jak i pozostałe opisane charakterystyki są funkcją całkowitej jakości produktu.

15.4. Testowanie wolności od ryzyka (freedom from risk) Testowanie wolności od ryzyka ma na celu wykrywanie awarii mogących skutkować zaistnieniem ryzyk określonego typu. Norma ISO 25010 wyróżnia trzy rodzaje ryzyk: ekonomiczne; związane ze zdrowiem i bezpieczeństwem; związane ze środowiskiem. Możliwe ryzyka definiuje się w fazie identyfikacji ryzyka (patrz podrozdz. 19.4), a jego wpływ na otoczenie – w fazie analizy ryzyka (podrozdz. 19.6). Testowanie wolności od ryzyka polega na wykonywaniu testów, które odnoszą się do poszczególnych ryzyk (co wiemy dzięki wykorzystaniu obustronnego śledzenia). Wykorzystuje się tu cały arsenał technik testowania, dobieranych w zależności od rodzaju i stopnia ryzyka. Dalej opiszemy krótko wspomniane rodzaje ryzyk. Metody ich łagodzenia są opisane w podrozdziale 19.7.

15.4.1. Ryzyko ekonomiczne (economic risk) Ryzyko ekonomiczne to każde ryzyko, którego zaistnienie powoduje straty ekonomiczne. Straty te najczęściej mają charakter finansowy, ale równie dobrze może to być np. utrata reputacji czy dobrego wizerunku firmy, co nierzadko może skutkować bankructwem.

15.4.2. Ryzyko dotyczące zdrowia i bezpieczeństwa (health and safety risk) Ryzyko dotyczące zdrowia i bezpieczeństwa występuje zwykle w systemach o znaczeniu krytycznym. Są to systemy, od których poprawnego działania zależy zdrowie lub życie ludzkie. Przykładem aplikacji o znaczeniu krytycznym może być np. oprogramowanie maszyny dawkującej promieniowanie w terapii nowotworów czy też systemy awioniki w lotnictwie. W przypadku wystąpienia ryzyk tego typu proces testowania powinien być o wiele bardziej restrykcyjny niż w innych systemach. Szczególnie istotną rolę odgrywają techniki statyczne (przeglądy i inspekcje) oraz techniki wykorzystujące mocne kryteria pokrycia (np. MC/DC, LSKiS).

bezpieczeństwo (ang. safety) – zdolność oprogramowania do osiągania akceptowalnego poziomu ryzyka wystąpienia szkody w stosunku do ludzi, biznesu, oprogramowania, majątku lub środowiska w określonym kontekście użycia [3]

15.4.3. Ryzyko związane ze środowiskiem (environmental risk) Wystąpienie ryzyka związane ze środowiskiem powoduje straty takie jak skażenie bądź zanieczyszczenie wody, gleby czy powietrza. Przykładem systemu, w którym takie ryzyko można zidentyfikować, jest oprogramowanie sterujące przesyłem gazu w gazociągu. Słynna awaria takiego oprogramowania była prawdopodobnie1 przyczyną wybuchu gazociągu na Syberii w 1982 roku.

15.5. Testowanie kontekstu użycia (context coverage) Pokrycie kontekstu to stopień, w jakim oprogramowanie może być użyte w określonym kontekście użycia bądź w innych niż oryginalnie zdefiniowany kontekstach użycia z odpowiednią efektywnością, wydajnością, satysfakcją i wolnością od ryzyka. Testowanie bada stopień spełnienia przez oprogramowanie charakterystyk jakościowych w różnych kontekstach użycia.

15.5.1. Zupełność kontekstu (context completeness) Zupełność kontekstu odnosi się do wszystkich zdefiniowanych kontekstów użycia. Przykładem zdefiniowanego kontekstu użycia może być sytuacja, w której używamy aplikacji na smartfonie, z niskim poziomem sygnału Wi-Fi, z małą ilością wolnej pamięci. Przykładami podobnych, ale odmiennych kontekstów może być obsługa przez system zarządzania pracami dyplomowymi prac magisterskich oraz prac licencjackich.

15.5.2. Elastyczność (flexibility) Elastyczność dotyczy kontekstów użycia niezdefiniowanych oryginalnie w wymaganiach. Przykładem takiego kontekstu może być np. używanie oprogramowania polskojęzycznego przez użytkowników anglojęzycznych.

Elastyczność umożliwia pełne wykorzystywanie oprogramowania z uwzględnieniem warunków i okoliczności nieprzewidzianych podczas projektowania systemu. Może być ona osiągnięta przez adaptację oprogramowania dla dodatkowych typów użytkowników bądź kultur. Przykładem braku elastyczności jest błąd w programie MS Word 2007 polegający na tym, że w odwołaniu do rysunku opisanego przez podpis z etykietą „Rysunek” nie można użyć samego numeru rysunku. Numer musi występować razem z nazwą etykiety. W języku angielskim nie stanowi to problemu, bo w tekście zawsze można napisać „[...] in Figure 5 [...]”, ale w języku polskim, gdzie występuje odmiana przez przypadki, nie zawsze chcemy użyć sformułowania „Rysunek 5”. Czasami chcielibyśmy napisać np. „Na Rysunku 5” (miejscownik zamiast mianownika). Projektanci Worda 2007 nie uwzględnili, że w niektórych językach występuje odmiana rzeczowników przez przypadki, dlatego oprogramowanie to nie jest elastyczne w kontekście użycia go przez polskojęzycznych użytkowników w spolszczonej wersji edytora tekstu (jest to błąd związany z internacjonalizacją).

1 Tak naprawdę do końca nie wiadomo, jaki był prawdziwy powód awarii. Jedna z teorii mówi, że w wykradzionym Kanadyjczykom przez Sowietów oprogramowaniu sterującym pracą gazociągu był defekt celowo zasiany przez wywiad USA (tzw. bomba logiczna).

16. Testowanie jakości produktu

Jakość produktu łączy w sobie kategorie tzw. wewnętrznej oraz zewnętrznej jakości (ang. internal quality, external quality) opisywane przez standard ISO 9126. Jakość wewnętrzna odnosi się do projektu oprogramowania. Grupą najbardziej zainteresowaną wysoką jakością wewnętrzną są deweloperzy. Cechy jakości wewnętrznej nie są zwykle bezpośrednio widoczne dla użytkownika. Jakość zewnętrzna to stopień, w jakim oprogramowanie spełnia swoje cele, czyli stopień wypełnienia wymagań użytkownika. Pojęcia jakości zewnętrznej i wewnętrznej nie mają ostrych granic. Na przykład, można sobie wyobrazić, że jednym z podstawowych wymagań użytkownika jest przenaszalność, tradycyjnie rozumiana jako cecha jakości wewnętrznej. Ponadto sprawę komplikuje fakt, że pojęcie „użytkownika” nie zawsze jest precyzyjnie określone. Można je rozumieć jako klienta końcowego, ale użytkownikiem może być też tester, dział utrzymania, firmy wykorzystujące oprogramowanie stworzone dla innych firm itp. Stąd cechy jakości wewnętrznej z punktu widzenia jednych użytkowników dla innych mogą stać się cechami jakości zewnętrznej. Niezależnie jednak od istniejących i charakterystyk jakości należy dbać o nią całościowo.

podziałów

16.1. Testowanie funkcjonalnej przydatności (functional suitability) Testowanie funkcjonalnej przydatności polega na sprawdzeniu, czy i w jakim stopniu oprogramowanie dostarcza funkcjonalności pozwalającej na wykonanie czynności lub spełnienie potrzeb zawartych w dokumencie wymagań. Należy pamiętać, że tworzenie aplikacji nigdy nie jest celem samym w sobie. Na końcu

całego procesu jest zawsze klient-użytkownik, dla którego oprogramowanie jest przeznaczone. Testowanie funkcjonalnej przydatności to sprawdzenie, jak bardzo to oprogramowanie jest przydatne dla użytkownika. funkcjonalność (ang. functionality) – zdolność oprogramowania do zapewnienia funkcji odpowiadających zdefiniowanym i przewidywanym potrzebom, gdy oprogramowanie jest używane w określonych warunkach [3] dopasowanie (ang. suitability) – zdolność oprogramowania do dostarczenia odpowiedniego zestawu funkcji dla określonych zadań i celów użytkownika [3]; patrz także: funkcjonalność W ramach funkcjonalnej przydatności można wyróżnić trzy podcharakterystyki: zupełność, poprawność oraz stosowność funkcjonalności.

16.1.1. Zupełność funkcjonalności (functional completeness) Zupełność funkcjonalności to stopień, w jakim zbiór oferowanych przez oprogramowanie funkcji pokrywa określone potrzeby użytkownika. Potrzeby te są zwykle opisane w dokumencie wymagań lub w formie historyjek użytkownika. Na przykład, dla programu ELROJ można wyróżnić następujące funkcjonalności: F01: możliwość zdefiniowania komunikatu (wymaganie R01); F02: możliwość usunięcia komunikatu (R01); F03: dodanie nowej linii do rozkładu jazdy (R02);

wyświetlanego

na

ekranie

F04: dodanie kursu do rozkładu jazdy danej linii (R03); F05: jednorazowe dodanie większej liczby kursów (R04); F06: możliwość usunięcia kursu (R05); F07: wyświetlenie kursów na ekranie (R06); F08: możliwość ustawienia daty na ekranie (R07). Stopień zupełności funkcjonalności można mierzyć jako liczbę pokrytych funkcjonalności spełniających potrzeby użytkownika w stosunku do wszystkich wymaganych funkcjonalności. Na przykład zakładając, że F01–F08 to wszystkie potrzebne użytkownikowi funkcjonalności, jeśli program ELROJ pozwalałby na

dodawanie wyłącznie pojedynczych kursów, funkcjonalność F05 nie byłaby pokryta. Wtedy funkcjonalność byłaby zupełna tylko w 7/8 = 87,5%. Tester mógłby zauważyć, że na liście tej brakuje funkcjonalności usunięcia całej linii z rozkładu (co wydaje się być sensownym wymaganiem z punktu widzenia użytkownika).

16.1.2. Poprawność funkcjonalności (functional correctness) Poprawność funkcjonalności to stopień, w jakim oprogramowanie dostarcza poprawny (prawidłowy) wynik. Jeśli na przykład wypisywanie danych na ekran wyglądałoby tak, jak na rysunku 16.1, to należałoby uznać, że funkcjonalność F07 jest błędna. Po pierwsze, komunikat jest wyświetlany w pierwszej, a nie ostatniej linii ekranu. Po drugie, niektóre czasy przybycia są podawane ze zbyt dużą dokładnością (wymagana jest dokładność do jednej minuty). Po trzecie, wyświetlane kursy nie są posortowane ze względu na czasy przybycia autobusów.

12.04.2013 LINIA

Czas przyjazdu

111

0,66666 min

112

45 min

113

5 min

110

58 min

KOMUNIKAT

Rysunek 16.1. Ekran programu ELROJ – przykład niepoprawnej funkcjonalności

16.1.3. Stosowność funkcjonalności (functional appropriateness) Stosowność funkcjonalności to stopień, w jakim funkcjonalność programu pomaga w osiągnięciu celów użytkownika. Rozważmy ponownie system ELROJ oraz funkcję dodawania nowych kursów. Jeśli aplikacja umożliwia dodanie wielu kursów za jednym razem, to funkcjonalność pomaga użytkownikowi osiągnąć

jego cel w większym stopniu niż gdyby umożliwiała jedynie wprowadzanie pojedynczych kursów. Różnica między zupełnością a stosownością jest taka, że zupełność dotyczy istnienia funkcji wykonującej określone zadanie, stosowność natomiast dotyczy jakości tego wykonania.

16.2. Testowanie wydajności w działaniu (performance efficiency) Testowanie wydajności bada oprogramowanie w dwóch obszarach: zachowania w czasie oraz zużycia zasobów. Kwestie dotyczące wydajności często są pomijane w dokumencie wymagań, tzn. są założone implicite, czyli niejawnie. Jeśli działanie testowanego programu bądź systemu polega na wykonywaniu prostych czynności, niewymagających wiele czasu czy zasobów, a ponadto nie ma potrzeby zachowania skalowalności, to testowanie wydajności może okazać się zbędne. Zwykle jednak program nie działa w próżni i najczęściej ma do czynienia np. z ładowaniem danych z zewnątrz oraz ich wewnętrznym przetwarzaniem. W takich sytuacjach, jeśli wymagania dopuszczają znaczące ilości danych lub wysokie natężenie użytkowania systemu (duża liczba równoległych użytkowników, krótki czas między zgłoszeniami itp.), to testowanie wydajności jest obowiązkowym typem testu, jaki należy przeprowadzić. Z testowaniem wydajności jest związany tzw. efekt próbnika1. Na przykład, jeśli mierzymy szybkość działania systemu, to aplikacja dokonująca pomiaru sama również zużywa zasoby systemu, powodując tym samym wolniejsze działanie analizowanej aplikacji. Poziom zmierzonej wydajności tego systemu będzie zatem nieznacznie mniejszy od rzeczywistego. efekt próbnika (ang. probe effect) – efekt wpływu elementu pomiarowego na moduł lub system podczas dokonywania pomiaru, np. przez narzędzie do testów wydajnościowych

16.2.1. Zachowanie w czasie (time behaviour) Można wyróżnić następujące metryki związane z zachowaniem w czasie:

czas odpowiedzi (ang. response time) – czas między wykonaniem polecenia rozpoczęcia transakcji a otrzymaniem odpowiedzi. Ma zastosowanie w systemach interaktywnych. Przykładem może być czas między kliknięciem przycisku „Złóż zamówienie” w sklepie internetowym, a otrzymaniem komunikatu potwierdzającego złożenie zamówienia. Czas odpowiedzi jest sumą czasu przetwarzania oraz czasu transmisji. Czasami czas transmisji jest pomijalny, ale np. w przypadku aplikacji sieciowych może mieć znaczący udział w czasie odpowiedzi. czas przetwarzania (ang. processing time) – czas między otrzymaniem przez system polecenia a zwróceniem wyniku. czas transmisji (ang. transmission time) – czas potrzebny na przesłanie komunikatu. czas operacji (ang. turn around time) – czas potrzebny na uzyskanie odpowiedzi na żądanie użytkownika. Zwykle składa się z wielu czasów odpowiedzi, gdyż dotyczy wielu różnych akcji. Na przykład wypłata pieniędzy z bankomatu jest operacją, na którą składają się czasy: od włożenia karty do prośby o PIN, od wpisania kodu PIN do wyświetlenia menu, od wyboru opcji wypłaty do wyświetlenia ekranu z kwotami, od wyboru kwoty do zwrócenia karty i od zwrócenia karty do wypłaty pieniędzy. Czas można mierzyć zarówno w standardowych jednostkach czasu, takich jak sekundy, minuty czy godziny, jak i w cyklach procesora. Czasami testera interesuje jeden, konkretny czas na wykonanie jakiejś operacji, a czasami jego wartość średnia (np. średni czas odpowiedzi bankomatu na wpisanie kodu PIN i naciśnięcie klawisza OK). Wymagania odnoszące się do zachowania w czasie muszą być precyzyjnie zdefiniowane. Nie należy stosować pojęć typu „czas akcji X powinien być krótki”, „system Y powinien działać szybko” itd. Każde odwołanie do czasu powinno być wyrażone w konkretnych wartościach liczbowych, np.: „generacja raportu tygodniowego nie powinna zajmować więcej niż 5 minut czasu przy normalnym obciążeniu systemu (40% obciążenia procesora)”.

16.2.2. Zużycie zasobów (resource utilization)

Zasoby używane przez systemy lub aplikacje to: pamięć; czas procesora; miejsce na dysku; urządzenia peryferyjne; zasoby ludzkie; zewnętrzne oprogramowanie; miejsce (np. powierzchnia zajmowana przez układ scalony z systemem wbudowanym na płytce obwodu drukowanego, powierzchnia zajmowana przez bankomat itp.). zużycie zasobów, składowanie danych (ang. resource utilization, storage) – zdolność oprogramowania do wykorzystania odpowiedniej ilości i typu zasobów, np. pamięci głównej i dodatkowej wykorzystywanej przez program oraz rozmiaru plików tymczasowych podczas działania oprogramowania w ustalonych warunkach [3] Testowanie zużycia zasobów może polegać na weryfikacji wymagań pod kątem: zużycia czasu procesora na wykonanie określonych funkcji; zużycia dostępnej pamięci na wykonanie określonej funkcji; poziomu wycieków pamięci (stopień wycieku w zależności od czasu i obciążenia systemu); obecności, odpowiedniości i dostępności zasobów ludzkich, urządzeń peryferyjnych, zewnętrznego oprogramowania, miejsca.

16.2.3. Pojemność (capacity) Pojemność to stopień, w jakim maksymalne limity parametrów systemu lub produktu spełniają wymagania. Przykładem może być wymaganie „system jest w stanie obsłużyć do 1000 klientów jednocześnie” lub „system może przechowywać dane o 100 milionach klientów”. Zwykle sam fakt możliwości osiągnięcia założonego stopnia pojemności jest niewystarczający – ta charakterystyka ma sens dopiero w połączeniu z dwoma poprzednimi, tzn. z zachowaniem systemu w czasie oraz ze zużyciem zasobów. Testowanie pojemności z uwzględnieniem

czasu i zużycia zasobów wykorzystuje specjalne techniki obciążania, przeciążania oraz weryfikacji skalowalności. Techniki te omówimy w kolejnym podrozdziale.

16.2.4. Techniki testowania wydajności Techniki

testowania

wydajności

polegają

na

generowaniu

obciążenia

i obserwowaniu jak system zachowuje się pod tym obciążeniem, pod kątem zarówno czasu działania, jak i zużycia zasobów. Testy wydajnościowe muszą być zautomatyzowane, gdyż dotyczą sytuacji, których wykonanie manualne byłoby bardzo trudne lub niemożliwe. Przykładem może być np. wygenerowanie bardzo dużej liczby użytkowników jednocześnie logujących się do systemu, symulowanie małej ilości dostępnej pamięci itp. Testowanie wydajności dla punktu odniesienia. Ten rodzaj testowania sprawdza zachowanie się systemu w normalnych warunkach (ang. baseline performance testing). Wyniki takiego testu służą jako punkt odniesienia dla innych testów, takich jak testowanie skalowalności. Pozwalają również określić średni czas działania i zużycie zasobów podczas normalnego użytkowania systemu, co może być wykorzystane w różnego rodzaju symulacjach. Testowanie obciążeniowe (ang. load testing). Mierzy zachowanie się programu przy wzrastającym obciążeniu, ale poziom obciążenia nie powinien przekraczać maksymalnej dopuszczalnej wartości przewidzianej w specyfikacji. Istnieje kilka rodzajów testów obciążeniowych, pokazanych na rysunku 16.2.

Rysunek 16.2. Przykłady testowania obciążeniowego Na diagramie (a) zilustrowano sytuację, w której testowany system został poddany dużemu, stałemu i niezmiennemu w czasie obciążeniu. Na diagramie (b) pokazano tzw. testowanie naciskowe (ang. peak load). Pik oznacza poziom maksymalnego obciążenia (np. natężenia ruchu na stronie internetowej, liczby jednoczesnych zgłoszeń do systemu). Maksymalne obciążenie stosuje się przez krótki czas, po czym poziom obciążenia spada do zera. Cykl ten jest powtarzany kilkakrotnie. Najciekawsze momenty zachowania się systemu to czas między pikami – ten typ testu sprawdza nie to, jak system radzi sobie z dużym obciążeniem, ale przede wszystkim jaka jest wydajność systemu przy gwałtownie zmieniającym się obciążeniu. Na diagramie (c) zilustrowano obciążenie schodkowe, w którym sukcesywnie zwiększa się poziom obciążenia. Ten typ testu może być wykorzystany w testowaniu skalowalności. Wyniki testu pozwalają na

stworzenie wykresu zależności wydajności systemu w funkcji obciążenia. Kształt funkcji pokazuje szybkość spadku wydajności wraz ze wzrostem obciążenia. Ekstrapolacja tej funkcji pozwala przewidzieć zachowanie systemu przy jeszcze większych obciążeniach, których np. nie byliśmy w stanie wygenerować z przyczyn technicznych2. Na diagramie (d) przedstawiono testowanie dla obciążenia narastającego. Jest ono podobne do obciążenia schodkowego, przy czym każdy cykl rozpoczyna się od coraz większego obciążenia. Na diagramie (e) opisano testowanie przy obciążeniu losowym. Jego rozkład najczęściej wynika z analizy profilu operacyjnego i przedstawia rzeczywisty, typowy rozkład obciążenia programu. Metoda ta pozwala przetestować program w symulowanych warunkach normalnego użytkowania systemu. Testowanie przeciążające (ang. stress testing). W swej idei jest ono podobne do testowania obciążeniowego, przy czym w testowaniu przeciążającym idziemy jeszcze dalej, obciążając system do momentu, w którym nie jest on w stanie poprawnie funkcjonować. Pozwala na znalezienie punktu krytycznego, tzn. maksymalnej wartości obciążenia, przy którym system działa na zadowalającym nas poziomie (np. odpowiada w odpowiednio krótkim czasie lub zużywa odpowiednią ilość zasobów). Testowanie skalowalności (ang. scalability testing). Testowanie skalowalności pozwala zbadać relację między wydajnością systemu a poziomem obciążenia. W zależności od kształtu funkcji opisującej ten związek można wnioskować o tym, na ile system jest skalowalny oraz tworzyć modele wydajności dla poziomów obciążeń niewykorzystanych w procesie testowania. skalowalność (ang. scalability) – zdolność oprogramowania rozbudowywanym w celu obsłużenia wzrastającego obciążenia [59]

do

bycia

Rozważmy przykład systemu rozpoznającego numer rejestracyjny samochodu na podstawie zdjęcia z fotoradaru. Wydajność systemu (definiowana jako czas między wysłaniem zdjęcia do systemu a zwróceniem numeru tablicy rejestracyjnej wraz z danymi o właścicielu pojazdu) zależeć będzie od liczby aktualnie przechowywanych danych o właścicielach pojazdu. Tester przygotował pięć sztucznie wygenerowanych baz danych zawierających odpowiednio 102, 103, …, 106 zdjęć. Następnie, dla każdej bazy wykonał 100 zapytań przez wysłanie stu różnych zdjęć pojazdów. Na rysunku 16.3 pokazano wykres średnich wartości czasu odpowiedzi dla stu zapytań dla każdej z sześciu baz. Skale na obu osiach są

wykładnicze, a zależność między rozmiarem bazy a czasem odpowiedzi jest również wykładnicza. Oznacza to, że system źle się skaluje. Oczekiwalibyśmy przynajmniej zależności liniowej (a najlepiej subliniowej). Na podstawie danych z rysunku 16.3 można zbudować model, który pozwoli przewidzieć czas odpowiedzi systemu dla baz o rozmiarach większych niż 106. Stosując np. metodę regresji nieliniowej dla modelu wykładniczego, moglibyśmy na podstawie danych z rysunku otrzymać zależność t(n) = 41 ⋅ (1,005)0,001 ⋅ n gdzie n jest liczbą rekordów w bazie, a t(n) – średnim czasem odpowiedzi dla bazy o rozmiarze n. Wtedy, dla n = 107 otrzymalibyśmy t(n) = 41 ⋅ (1,005)10000 ms ≈ 1,87 ⋅ 1023 ms, co oznacza, że praktycznie baza nie będzie w stanie działać mając więcej niż 1–2 miliony rekordów.

Rysunek 16.3. Przykład złej skalowalności systemu

16.3. Testowanie zgodności (compatibility) Zgodność to stopień, w jakim testowany produkt, system lub moduł może wymieniać informacje z innymi produktami, systemami lub modułami dzieląc jednocześnie to samo środowisko. Przez środowisko należy rozumieć zarówno sprzęt, jak i oprogramowanie. Jako specjalny przypadek należy wyróżnić aplikacje internetowe, które muszą współdziałać z różnymi przeglądarkami. Niezależnie od typu przeglądarki, aplikacja zawsze powinna wyglądać i zachowywać się tak samo.

16.3.1. Współistnienie (co-existence) Współistnienie to stopień, w jakim testowany produkt może wykonywać swoje funkcje, dzieląc środowisko oraz zasoby z innymi aplikacjami nie wpływając negatywnie na te aplikacje. Na rysunku 10.7 zilustrowano przykład współistnienia: testowany program oraz współistniejące aplikacje nie są bezpośrednio ze sobą związane – nie muszą się ze sobą w ogóle komunikować. Jednak wszystkie te aplikacje mogą wykorzystywać np. tę samą bazę danych lub ten sam system plików. Niewłaściwe działanie testowanego programu powodujące błędy w bazie lub w systemie plików może wpływać na błędne działanie innych programów wykorzystujących te same zasoby. współistnienie, koegzystencja (ang. co-existence) – zdolność produktu oprogramowania do działania z innym, niezależnym oprogramowaniem we wspólnym środowisku, podczas dzielenia wspólnych zasobów [3]

16.3.2. Współdziałanie (interoperability) Współdziałanie to stopień, w jakim testowany produkt może wymieniać informacje z innymi programami i używać tych informacji. Przykładem może być urządzenie pamięci masowej USB, które współpracuje z każdym współczesnym komputerem wyposażonym w port USB. Jest tak dlatego, że wymiana danych przez port USB odbywa się według ściśle określonego standardu. Należy jednak pamiętać, że współdziałanie zachodzi niekoniecznie dlatego, że jest

spełniony jakiś z góry narzucony standard. Może być ono zapewnione indywidualnie, np. dla konkretnych dwóch programów. Metoda wymiany informacji nie musi być zgodna z żadnym istniejącym standardem. współdziałanie

(ang.

interoperability)



zdolność

oprogramowania

do

współdziałania z jednym lub większą liczbą modułów lub systemów [3] Współdziałanie dotyczy charakterystyki obejmuje

przepływu danych, zatem testowanie tej swym zakresem testowanie protokołów

komunikacyjnych. Wymiana informacji może następować przez wywoływanie odpowiednich funkcji API testowanego programu, dlatego testowanie współdziałania ma zazwyczaj charakter testów integracyjnych.

16.3.3. Przykład Testowanie współdziałania między programami rodziny MS Office 2000 a Open Office może polegać na następujących czynnościach: wygenerowanie i wydrukowanie dokumentu w programie MS Word, arkusza kalkulacyjnego w MS Excel i prezentacji w MS PowerPoint; otworzenie i wydrukowanie tych plików w programach Open Office; porównanie plików i wydruków z obu wersji programów; dokonanie zmian w dokumentach otworzonych w Open Office, wczytanie ich w programach MS Office i porównanie. W powyższym procesie testowania mamy do czynienia z wymianą danych, którymi są: dokument tekstowy, arkusz kalkulacyjny oraz prezentacja. Należy zweryfikować zgodność możliwie jak największej liczby funkcji i operacji, jakie można dokonywać w tych plikach. W szczególności, dla dokumentu tekstowego należy sprawdzić współdziałanie pod kątem zachowania się następujących cech tekstu: typy, kroje i wielkości czcionek; akapity, interlinie, paginacja, stopki, nagłówki, marginesy; osadzone rysunki, tabele, tryb wielokolumnowy; odsyłacze, podpisy pod rysunkami; eksport dokumentu do PDF;

numeracja stron, inne pola specjalne (np. data); komentarze, śledzenie zmian; ochrona dokumentu; równania matematyczne itd. W przypadku arkusza kalkulacyjnego: typy, kroje i wielkości czcionek; ukryte kolumny; format danych w komórkach; odwołania między komórkami; funkcje matematyczne, statystyczne, operacje na tekstach itd.; wykresy; eksport dokumentu do PDF; zaznaczanie obszaru drukowania itd. W przypadku prezentacji: typy, kroje i wielkości czcionek; osadzone rysunki, clipart; animacje pokazu slajdów; elementy interaktywne (przyciski sterowania); osadzone pliki muzyczne (mid, wav, mp3) i wideo (avi, mpeg); wzorce slajdów itd.

16.4. Testowanie użyteczności (usability) Użyteczność to stopień, w jakim oprogramowanie może być używane przez określonych użytkowników dla osiągnięcia założonych celów. Innymi słowy, jest to charakterystyka opisująca jak użyteczne dla klienta jest oprogramowanie. W punktach 16.4.1–16.4.6 opiszemy podcharakterystyki użyteczności, natomiast w podrozdziale 16.5 podamy przykłady konkretnych wskazówek (tzw. heurystyki Nielsena) mogących pomóc w tworzeniu systemów o wysokiej użyteczności. Istnieją trzy główne techniki oceny użyteczności [93]:

Inspekcja. Jeśli dysponujemy prototypami lub makietami interfejsu, inspekcja może przybrać formę przeglądu dokonywanego przez użytkowników. Inspekcja może być przeprowadzona również na podstawie listy kontrolnej zawierającej znane aspekty oprogramowania wpływające na poziom użyteczności. Ta technika pozwala wykrywać błędy użyteczności na wczesnych poziomach testowania. Walidacja implementacji. Polega ona na przeprowadzeniu testów użyteczności według założonych scenariuszy testowych. Różni się od klasycznego testowania co najmniej w dwóch kwestiach. Po pierwsze, proces testowania bardziej niż na cechy funkcjonalne zwraca uwagę na takie atrybuty, jak szybkość uczenia się, zrozumiałość, efektywność użycia oprogramowania itp. Po drugie, przed testami i po testach użytkownicy, którzy je przeprowadzają są pytani o oczekiwania oraz wrażenia

z

użytkowania

programu.

Proces

przepytywania

ma

najczęściej formę wywiadu. Przed testowaniem użytkownicy otrzymują ponadto instrukcje opisujące, jak przeprowadzić test. Instrukcje mogą zawierać informacje o tym, jakie czynności należy wykonać, w jakim czasie należy się zmieścić, a nawet, jak krok po kroku wykonać zadanie. Kwestionariusze (ankiety). Kwestionariusze służą do zbierania od użytkowników informacji na temat używanego oprogramowania. Ankietując większą liczbę użytkowników, dostajemy bardziej miarodajną ocenę użyteczności. Istnieją ogólnie dostępne ankiety, takie jak SUMI (Software Usability Measurement Inventory) czy WAMMI (Website Analysis and Measurement Inventory). Wykorzystując tego typu standardowe ankiety, organizacje mogą porównać wyniki testów użyteczności dla różnych produktów lub ich wersji. SUMI jest kwestionariuszem zawierającym 50 pytań. Na każde z nich należy odpowiedzieć „zgadzam się”, „nie zgadzam się” lub „nie wiem”. Oto przykładowe pytania: ten program odpowiada zbyt wolno na dane wejściowe; nauka używania aplikacji na początku sprawia dużo problemów; dokumentacja jest dokładna, wyczerpująca i przydatna; nie chciał(a)bym używać tego oprogramowania na co dzień.

Po zebraniu danych są one kodowane, scalane i przekształcane na tzw. skalę globalną oraz pięć dodatkowych skal wyrażających wydajność, wpływ (ang. affect), pomocność, kontrolowalność i łatwość nauki. Wyniki są skalowane tak, że średni wynik każdej z tych skal w populacji ma wartość 50 z odchyleniem standardowym 10. Wynik testów własnego oprogramowania można więc porównać ze średnią przemysłową i zobaczyć, jak nasz produkt wypada na tle innych. Rezultaty pomiarów są również pomocne w określeniu obszarów, które wymagają usprawnienia z punktu widzenia użyteczności. WAMMI to metoda podobna do SUMI, przy czym dotyczy użyteczności stron internetowych. Jest to kwestionariusz złożony z 20 pytań, na które odpowiada użytkownik po wizycie na stronie. Główną częścią wyniku analizy jest tzw. profil strony, składający się z oceny pięciu charakterystyk: atrakcyjności, kontrolowalności, wydajności, pomocności i łatwości nauki. Wynik zawiera także ocenę łączną. Wszystkie te metryki są tak przeskalowane, że średnia wynosi 50, a ocena maksymalna to 100. Raport z badań zawiera również inne elementy, takie jak: spriorytetyzowane cechy strony według konieczności ich poprawy; analizę dodatkowych pytań z zamkniętymi odpowiedziami; indywidualne profile użytkowników strony itp. Na rysunku 16.4 jest przedstawiony przykładowy wynik analizy WAMMI dla pewnej strony internetowej. Jak widać, strona działa wydajnie, jest pomocna dla użytkownika i łatwa w obsłudze (te trzy wyniki są powyżej średniej), natomiast istnieje problem z jej atrakcyjnością i kontrolowalnością. Te dwie charakterystyki jakościowe są pierwszymi kandydatami do poprawy.

Rysunek 16.4. Przykładowy wynik analizy WAMMI (źródło: www.wammi.com/whatis.html) inwentarz analizy i pomiaru stron internetowych (ang. Website Analysis and MeasureMent Inventory, WAMMI) – oparta na kwestionariuszach technika pomiaru użyteczności stron internetowych, służąca do pomiaru jakości stron internetowych z punktu widzenia użytkownika inwentarz pomiarów użyteczności oprogramowania (ang. Software Measurement Inventory, SUMI) – oparta na kwestionariuszach technika stosowania użyteczności do pomiarów jakości oprogramowania z punktu widzenia końcowego użytkownika

16.4.1. Zrozumiałość (appropriateness recognizability) Zrozumiałość jest cechą

mierzącą

stopień, w jakim użytkownik potrafi

rozpoznać, czy testowany system, program lub komponent jest odpowiedni dla jego potrzeb. Charakterystyka ta wynikać może z dwóch źródeł: pierwszego wrażenia, jakie produkt wywarł na użytkowniku oraz z dokumentacji użytkowej. Zrozumiałość jest zwiększana przez dołączenie do programu demonstracji, przykładowych

danych

wejściowych,

przewodników

i

innych

tego

typu

informacji pozwalających użytkownikowi na szybką ocenę, czy program będzie w stanie pomóc mu w rozwiązaniu jego problemu. Testowanie zrozumiałości może sprawdzać ilość i jakość pomocniczych informacji dołączonych do aplikacji. zrozumiałość (ang. understandability) – zdolność oprogramowania do umożliwienia użytkownikowi zrozumienia, czy jest ono odpowiednie i jak może być użyte do realizacji określonych zadań [3]

16.4.2. Łatwość nauki (learnability) Łatwość nauki to stopień, w jakim użytkownik może opanować zasady użytkowania oprogramowania. Aplikacje o wysokiej łatwości nauki mogą być efektywnie używane bez jakiegokolwiek wstępnego szkolenia. W przypadku aplikacji o niskiej łatwości nauki użytkownik może mieć wrażenie, że program używa nieznanych mu pojęć lub metod i odczuwa brak niezbędnych informacji potrzebnych do używania oprogramowania. łatwość nauki (ang. learnability) – zdolność oprogramowania do wspierania użytkownika w procesie nauki użycia [3]; patrz także: użyteczność

16.4.3. Łatwość użycia (operability) Łatwość użycia to stopień, w jakim oprogramowanie umożliwia użytkownikowi intuicyjne korzystanie z aplikacji. W testowaniu łatwości użycia weryfikuje się m.in. następujące cechy oprogramowania: spójność (w tym spójność interfejsu polegająca na używaniu takiego samego stylu graficznego, układu menu, czcionek itp. w różnych

modułach aplikacji oraz spójność terminologii, podobnych określeń w podobnych sytuacjach); jasne i zrozumiałe komunikaty, zarówno i ostrzegawcze oraz komunikaty o błędach;

czyli

używanie

informacyjne,

jak

możliwość cofania wprowadzonych zmian (im więcej zmian da się cofnąć, tym lepiej); możliwość

konfigurowania

oprogramowania

do

indywidualnych

potrzeb użytkownika (ang. customization), np. możliwość organizacji pulpitu, ustalania kolorystyki, używania zakładek, dodawania wtyczek (w przeglądarkach). łatwość użycia (ang. operability) – zdolność oprogramowania do zapewnienia użytkownikowi możliwości jego obsługi i kontroli [3]; patrz także: użyteczność

16.4.4. Ochrona przed błędami użytkownika (user error protection) Ochrona przed błędami użytkownika to stopień, w jakim system chroni użytkownika przed popełnianiem błędów. Popularnymi rozwiązaniami tego typu są tzw. techniki poka-yoke, czyli metody zapobiegające powstawaniu defektów. Mogą to być zarówno rozwiązania sprzętowe, jak i software’owe. W tabeli 16.1 pokazano przykłady dobrych i złych rozwiązań pod kątem zapobiegania defektom. Należy odróżnić charakterystykę ochrony przed błędami użytkownika od tolerancji na błędy, która jest podcharakterystyką niezawodności, opisaną w punkcie 16.6.2. Pierwsza dotyczy działań użytkownika, natomiast druga – działań samego oprogramowania, które mają zapewnić poprawność działania systemu w sytuacji nastąpienia błędu bądź awarii. Tabela 16.1. Ochrona przed błędami użytkownika – przykłady dobrych i złych rozwiązań

Poziom s przętowy

Przykład dobrego rozwiązania

Przykład złego rozwiązania

wtyczka US B, którą – dzięki jej kons trukcji – da s ię włożyć do

płyta CD, którą można włożyć do

g niazda US B tylko na jeden s pos ób Poziom okno wyboru daty za pomocą s oftware’owy zaznaczania dnia w kalendarzu

czytnika na dwa s pos oby okno teks towe umożliwiające ręczne wpis anie daty

16.4.5. Estetyka interfejsu użytkownika (user interface aesthetics) Estetyka interfejsu użytkownika pozwala na zwiększenie (lub zmniejszenie) odczuwania przez użytkownika satysfakcji z użytkowania oprogramowania. Na estetykę interfejsu mają wpływ takie czynniki jak: dobór kolorystyki elementów GUI; dobór kroju czcionki oraz jej rozmiaru; forma graficzna komponentów GUI; układ elementów na ekranie, logika ich umiejscowienia oraz ich zagęszczenie; animacje podczas wykonywania przez użytkownika akcji (np. kliknięcie przycisku, przesunięcie palcem po ekranie dotykowym, zaznaczenie opcji powodujące pojawienie się dodatkowego panelu).

16.4.6. Dostęp (accessibility) Dostęp to stopień, w jakim oprogramowanie może być używane przez ludzi o różnych cechach psychofizycznych. W szczególności dotyczy to ludzi z różnymi poziomami niepełnosprawności. Testowanie dostępu polega na weryfikacji, czy produkt jest dostosowany do odpowiedniej grupy użytkowników. Na przykład, jeśli oprogramowanie ma być dostosowane dla osób niedowidzących, to powinno umożliwiać wykorzystanie kontrastowych kolorów oraz powiększenie rozmiaru czcionki. Dostosowanie do potrzeb osób niewidomych może np. wymagać, aby komunikaty wyświetlane przez program mogły być odczytane przez specjalistyczne oprogramowanie przetwarzające tekst pisany na głos. Przykładem oprogramowania dbającego o wysoki poziom dostępu jest system operacyjny Windows. Ma on takie narzędzia jak lupa (powiększająca fragment

ekranu), czy tzw. funkcję klawiszy trwałych, dzięki której skróty klawiaturowe takie jak CTRL+ALT+DEL czy CTRL+S można wykonywać, naciskając te klawisze pojedynczo. Przykładem standardu opisującego wymagania dotyczące dostępu jest Rehabilitation Act [147], a dokładniej jego część, Section 508, opublikowana przez U.S. Environmental Protection Agency. Opisuje ona wymagania dla następujących grup produktów i systemów: aplikacje oraz systemy operacyjne; aplikacje internetowe; produkty telekomunikacyjne; produkty video i multimedialne; systemy wbudowane; komputery przenośne i desktopowe.

16.5. Heurystyki dotyczące użyteczności Efektywność, wydajność i satysfakcja opisują użyteczność systemu z punktu widzenia użytkownika. Tworzenie użytecznych systemów jest trudne – nie ma zbioru sztywnych reguł opisujących krok po kroku konkretne akcje, jakie należy wykonać, aby system był efektywny i wydajny, a użytkownik odczuwał satysfakcję z jego użycia. Dlatego zaproponowano wiele różnego rodzaju heurystyk pozwalających zwiększyć użyteczność systemów. Jednymi z najbardziej znanych są tzw. heurystyki Nielsena ([148], [149], [150]). Oprócz nich, z ważniejszych zasad można wyróżnić: 18 kryteriów ergonomii Bastiena–Scapina [151], 10 reguł inżynierii kognitywnej Gerhardta–Powalsa [152], klasyfikację Weinschenka–Barkera [153], 30 zasad użyteczności Connella–Hammonda [154] czy 944 wskazówki dla projektantów interfejsów użytkownika Smitha–Mosiera [155].

16.5.1. Heurystyki Nielsena Jakob Nielsen sformułował w 1990 roku 10 heurystyk dotyczących użyteczności opisanych poniżej. Heurystyki te można wykorzystać jako swoistą listę kontrolną do testowania efektywności, wydajności i satysfakcji. Omówimy je teraz po kolei.

ocena

heurystyczna

(ang.

heuristic

evaluation)



technika

statycznego

testowania użyteczności mająca na celu określenie zgodności interfejsu użytkownika lub jego projektu; wykorzystując tę technikę, przeglądający sprawdzają

interfejs

i

oceniają

jego

zgodność

z

uznanymi

zasadami

użyteczności (tzw. heurystykami) 1. Pokazuj status systemu. Użytkownik powinien być na bieżąco informowany o tym, w jakim miejscu systemu się znajduje, gdzie może pójść dalej, skąd przyszedł oraz co system robi w danym momencie.

Rysunek 16.5. Informowanie użytkownika o stanie systemu Na rysunku 16.5 jest pokazany panel użytkownika konta w banku internetowym. Widać zaznaczoną opcję „Moje Finanse” oraz pod spodem podkreśloną i pogrubioną opcję „Rachunki”, a jeszcze niżej podopcję „Twoje rachunki”. Użytkownik dokładnie wie, w którym miejscu systemu się znajduje. Często wykorzystuje się również tzw. okruszki (ang. breadcrumbs), pokazujące ścieżkę, którą przeszedł użytkownik, np.: Jesteś tutaj: Strona główna → Sklep → Książki → Komiksy. Nazwy linków (hiperłącz) prowadzących do podstron lub innych części systemu powinny być zgodne z tytułami tych podstron lub części systemu. Klikalne elementy powinny być wyraźnie oznaczone, aby wiadomo było, że są odnośnikami. hiperłącze (ang. hyperlink) – wskaźnik w dokumencie elektronicznym, który stanowi odwołanie do innego dokumentu elektronicznego

Gdy system przetwarza jakieś zadanie i operacja trwa nie dłużej niż kilka sekund, nie ma konieczności informowania użytkownika o tym, co się aktualnie dzieje. Jeśli jednak akcja trwa dłużej niż kilka sekund, to tok myślowy użytkownika może zostać przerwany, a jego irytacja wzrośnie. Dlatego w takich sytuacjach system powinien wyświetlać komunikaty opisujące na bieżąco, jakie akcje system wykonuje. 2. Zachowaj zgodność między systemem a rzeczywistością. Zasada ta najczęściej jest rozumiana w ten sposób, że system powinien posługiwać się językiem zrozumiałym dla użytkownika oraz adekwatnym do rzeczywistości.

Rysunek 16.6. Brak zgodności między systemem a rzeczywistością Na rysunku 16.6 jest przedstawiony zrzut ekranu ze strony sklepu internetowego luluforkids.pl. Pomijając fakt użycia całkowicie nieczytelnej czcionki oraz źle dobranej kolorystyki, strona używa pojęcia „wózka” zamiast powszechnie używanego „koszyka” kojarzącego się z zakupami. Akurat w tym przypadku jest to sklep z artykułami dla dzieci, więc odwołanie się do wózka dziecięcego zamiast koszyka może być celowe, jednak w ogólności należy uznać to za przejaw złej użyteczności. 3. Daj użytkownikowi pełną kontrolę. Użytkownik powinien mieć zapewnioną kontrolę oraz jak największą swobodę nad wszystkimi akcjami, które wykonuje. Na rysunku 16.7 przedstawiono stronę sklepu internetowego merlin.pl w chwili wyświetlania szczegółów towaru. Użytkownik ma w tym momencie do wyboru wiele możliwych opcji: może wrócić na stronę główną, klikając logo w lewym

górnym rogu, zalogować się na swoje konto, zobaczyć stan koszyka, rozwinąć szczegółowy opis towaru, dodać towar do koszyka itd. Wszystkie te akcje może wykonać z tego samego ekranu („opis produktu”).

Rysunek 16.7. Kontrola użytkownika nad systemem 4. Trzymaj się standardów i zachowaj spójność. System powinien zachowywać spójność formy elementów graficznych, kolorystyki, ikon itd. Aplikacja powinna używać jednego kroju czcionki; wszystkie łącza powinny wyglądać w ten sam sposób; w nowej wersji oprogramowania (lub w obrębie rodziny aplikacji) ikony oznaczające te same akcje powinny wyglądać tak samo; wszystkie komunikaty powinny mieć tę samą formę graficzną, itd.

Rysunek 16.8. Spójność interfejsów w obrębie rodziny produktów MS Office Na rysunku 16.8 są przedstawione paski narzędzi (tzw. wstążki) trzech programów z rodziny MS Office 2007: MS PowerPoint, MS Excel oraz MS Word. Ikony oznaczające te same akcje wyglądają dokładnie tak samo. Jest również zachowana spójność struktury menu oraz kolorystyki. 5. Zapobiegaj błędom. Aplikacja powinna w możliwie jak największym stopniu zapobiegać potencjalnym błędom użytkownika przez prowadzenie z nim odpowiednio opracowanej komunikacji. Należy stosować odpowiednie pola dialogowe minimalizujące ryzyko popełnienia pomyłki. Na przykład, jeśli użytkownik jest proszony o podanie daty wylotu, nie powinien wpisywać tej daty „z palca”, ale raczej wybrać ją przy użyciu kalendarza; jeśli należy wpisać swój numer PESEL, okno dialogowe powinno umożliwiać wpisanie wyłącznie liczb (dokładnie jedenastu) itd. Na rysunku 16.9 przedstawiono zrzut ekranu ze strony skyscanner.pl. Użytkownik wybiera datę wylotu z wbudowanego kalendarza, dzięki czemu system nie będzie miał problemów z rozpoznaniem formatu daty. Nie wystąpi również ryzyko wpisania błędnej daty (np. 32-15-1983 czy 29 lutego w roku nieprzestępnym).

Rysunek 16.9. Zapobieganie błędom: okno kalendarza zamiast pola na ręczne wpisanie daty 6. Pozwalaj wybierać zamiast zmuszać do zapamiętywania. Wszystkie niezbędne do podjęcia decyzji informacje powinny być widoczne na ekranie. Tam, gdzie to możliwe, należy stosować rozwijalne listy wyboru zamiast zmuszać użytkownika do wpisywania wartości z klawiatury. Jeśli należy wskazać ścieżkę w drzewie katalogów, lepiej umożliwić użytkownikowi nawigację po tym drzewie zamiast zmuszać do wpisywania ścieżki z klawiatury. Użytkownik nie powinien zapamiętywać jakichkolwiek informacji przy przechodzeniu z jednego okna dialogowego do drugiego. Instrukcje dotyczące użycia poszczególnych części systemu powinny być łatwo widoczne lub dostępne.

Rysunek 16.10. Wybór lokalizacji zamiast ręcznego wpisywania ścieżki Na rysunku 16.10 przedstawiono dobrze znane okno wyboru lokalizacji. Dzięki jego wykorzystaniu użytkownik nie musi pamiętać ścieżki do danej lokalizacji. 7. Zapewnij elastyczność i efektywność użycia. System powinien oferować metody przyspieszające pracę z systemem, np. skróty klawiszowe. Powinien pozwalać również na dopasowanie przez użytkownika sposobu wykonywania typowych zadań. Akceleratory, czyli opcje pozwalające na bardziej zaawansowane użytkowanie systemu przez doświadczonych użytkowników nie powinny być widoczne dla użytkowników początkujących. Ma to związek z tzw. jakością odwrotną (ang. reverse quality) występującą w modelu Kano (patrz podrozdz. 38.2).

Rysunek 16.11. Zaawansowane opcje niewidoczne dla początkującego użytkownika Początkujący użytkownik wyszukiwarki Google widzi przed sobą prosty ekran, złożony z pola, w które może wpisać słowo lub frazę do wyszukania (rys. 16.11 po lewej). Użytkownik zaawansowany może przełączyć się na widok zaawansowany (rys. 16.11 po prawej), który pozwala mu na efektywne konstruowanie złożonych zapytań. 8. Dbaj o estetykę i umiar (ang. minimalistic design). Okna dialogowe nie powinny zawierać informacji nadmiarowych, irrelewantnych do aktualnej sytuacji lub zwyczajnie niepotrzebnych. Interfejs użytkownika powinien być czytelny i przejrzysty, nieprzeładowany funkcjami. Poszczególne elementy interfejsu powinny być łatwo identyfikowalne. Kolorystyka powinna być dostosowana do charakteru aplikacji. Należy unikać stosowania „gryzących się” kolorów i wyszukanych krojów czcionek.

Rysunek 16.12. Prosty i estetyczny interfejs elektronicznego kalkulatora Na rysunku 16.12 jest pokazany interfejs działającej na smartfonie aplikacji typu elektroniczny kalkulator. Jak widać interfejs nie jest przeładowany żadnymi nadmiarowymi opcjami, nazwy przycisków są czytelne, a kolorystyka –

stonowana. Bieżący wynik działania jest wyświetlany – tak jak w przypadku rzeczywistych kalkulatorów – na górze ekranu, ponad panelem z przyciskami, czytelną czcionką o dużym rozmiarze i tym samym kroju, co etykiety przycisków. 9. Zapewnij skuteczną obsługę błędów. Błędy nie powinny się zdarzać, lecz nie da się ich niestety uniknąć. Dlatego należy jak najbardziej zminimalizować ich negatywne oddziaływanie na system i na użytkownika. Komunikaty o błędach powinny być zrozumiałe i pisane bez użycia jakichkolwiek kodów bądź innych niezrozumiałych oznaczeń. Powinny dokładnie opisywać problem oraz sugerować sposób jego rozwiązania. Na rysunku 16.13 pokazano cztery złe przykłady komunikatów o błędach. Komunikat o błędzie w Excelu („Nie można wyjść z programu Microsoft Office Excel”) w ogóle nie informuje użytkownika o powodach jego wystąpienia i nie tłumaczy, dlaczego nie można zamknąć aplikacji. Komunikat o błędzie w serwisie iTunes mówi wprost, że wystąpił „nieznany błąd”. Tak samo nieinformacyjny jest komunikat o błędzie na stronie facebook.com. Z kolei komunikat o błędzie w jednej z bibliotek Microsoft Visual C++ zawiera kody trudne do rozszyfrowania przez zwykłego użytkownika. Żaden z tych komunikatów nie proponuje użytkownikowi efektywnego sposobu enigmatycznym „spróbuj później”).

rozwiązania

Rysunek 16.13. Przykłady złych komunikatów o błędach

problemu

(poza

10. Dostarcz system pomocy oraz dokumentację. Dokumentacja oraz wbudowana pomoc powinny pozwolić na szybkie znalezienie potrzebnej informacji. Język dokumentacji i pomocy musi być zrozumiały dla użytkownika, nie może zawierać żargonu technicznego.

16.5.2. Laboratorium badania użyteczności (usability lab) Aby jak najlepiej zbadać użyteczność, testowanie tej charakterystyki oprogramowania przeprowadza się często w tzw. laboratoriach użyteczności. Są to specjalne pomieszczenia, najczęściej złożone z dwóch części. W jednej znajduje się stanowisko użytkownika. Część ta powinna wyglądać możliwie podobnie do docelowego środowiska, w którym użytkownik pracuje z oprogramowaniem. Chodzi o to, aby stworzyć warunki jak najbardziej odpowiadające rzeczywistości. Wtedy użytkownik będzie zachowywał się naturalnie, przez co wyniki badania będą bardziej wiarygodne. Druga część pomieszczenia, zwykle umieszczona za lustrem weneckim, jest przeznaczona dla badaczy. Mają oni do dyspozycji sprzęt monitorujący akcje użytkownika (może to być obraz z kamer ukrytych w pierwszej części pomieszczenia, obraz z monitora użytkownika Przykładowe laboratorium użyteczności jest pokazane na rysunku 16.14.

itd.).

Eksperyment polega na zadaniu użytkownikowi jakiejś czynności do wykonania, a następnie obserwowaniu na żywo, jak sobie z tym radzi. Wszystkie podjęte przez użytkownika akcje są nagrywane i analizowane. Nierzadko sprzęt zainstalowany w laboratorium użyteczności jest bardzo wyrafinowany – można np. używać narzędzi do tzw. śledzenia wzroku (ang. eye tracking), pozwalających stwierdzić, które elementy interfejsu przyciągają uwagę użytkownika i jak rozkłada się intensywność obserwowania poszczególnych części ekranu. Bada się czas wykonania zadań, sposób używania oprogramowania, a nawet emocje towarzyszące wykonywanym zadaniom (np. złość, irytację lub radość).

Rysunek 16.14. Laboratorium badania użyteczności (źródło: www.designperspectives.com/usability.html) Na rysunku 16.15 przedstawiono przykład analizy śledzenia wzroku (tzw. ciepła, ang. heat map). Rysunek pochodzi z bloga

mapę

www.dreamscapemultimedia.com/seo-michigan/seo-case-studies-by-a-michiganseo-company. Pokazuje on rezultat wyszukiwania frazy „holiday villa spain” w wyszukiwarce google. Plamy na rysunku oznaczają miejsca, na które użytkownik zwracał większą uwagę. Im ciemniejsza plama, tym dłużej użytkownik skupiał swój wzrok na danym elemencie strony. Jak widać, największą uwagę użytkownika przykuwają pierwsze trzy rezultaty wyszukiwania (nieprzypadkowo są to linki płatne...). Użytkownik prawie w ogóle nie zwraca uwagi na linki organiczne znajdujące się poniżej, czy też reklamy umieszczone w prawej części strony.

Rysunek 16.15. Mapa ciepła (ang. heat map) dla śledzenia wzroku Wyniki badań użyteczności pozwalają ocenić, w jaki sposób oprogramowanie jest używane, jak zachowuje się użytkownik podczas pracy z aplikacją, gdzie napotyka na trudności lub problemy, które czynności wykonuje szybciej, a nad którymi spędza więcej czasu itd. W tego typu badaniach można również stosować tzw. testy A/B polegające na tym, że różnym grupom użytkowników prezentuje się dwie wersje programu (np. różniące się układem menu lub kolorystyką przycisków GUI), a nastęnie bada różnice w ich sposobie użycia. Pozwala to stwierdzić, która z wersji jest lepsza pod kątem jakiegoś parametru użyteczności. Zwykle eksperymenty dotyczące użyteczności są projektowane zgodnie z klasycznym schematem przeprowadzania kontrolowanego eksperymentu.

Polega to na sformułowaniu hipotezy badawczej (np.: „umieszczenie menu kontekstowego u góry ekranu powoduje szybsze wyszukiwanie opcji menu niż umieszczenie go z boku ekranu”), wybraniu losowej próby badanych użytkowników3, dobraniu grupy kontrolnej, przeprowadzeniu badania oraz analizie wyników i wyciągnięciu wniosków. Próba musi być odpowiednio duża tak, aby wyniki badania były statystycznie istotne.

16.6. Testowanie niezawodności (reliability) Niezawodność określa stopień, w jakim oprogramowanie poprawnie spełnia swoje funkcje w określonym czasie. Jeśli awarie systemu zdarzają się rzadko, to czas bezawaryjnego działania jest długi i niezawodność jest wysoka. Jest to charakterystyka istotna zwłaszcza dla systemów o znaczeniu krytycznych oraz intensywnie używanych. niezawodności to:

Typowe

przyczyny

wpływające

na

spadek

wycieki pamięci; brak zasobów; nieefektywne algorytmy; brak obsługi sytuacji wyjątkowych; problemy z infrastrukturą. niezawodność (ang. reliability) – zdolność oprogramowania do wykonywania wymaganych funkcji w określonych warunkach przez określony czas lub dla określonej liczby operacji [3] Poziom niezawodności można dobrze określić dopiero po dłuższym użytkowaniu oprogramowania bądź systemu, co wymaga długiego czasu. Nie zawsze mamy go do dyspozycji tyle, ile potrzeba. Dlatego w testowaniu niezawodności stosuje się często metody symulujące „przyspieszone” użytkowanie systemu. W przypadku hardware’u jest wykorzystywana tzw. metoda HALT (ang. Highly Accelerated Life Tests) [156], gdzie systemy elektroniczne poddaje się testom wytrzymałościowym (np. wysoka lub niska temperatura czy natężenie prądu). W przypadku oprogramowania można wykorzystać testowanie automatyczne oparte na profilu operacyjnym. Dzięki automatyzacji oprogramowanie jest używane o wiele intensywniej niż w przypadku normalnej

pracy, co pozwala symulować długoterminowe użytkowanie w relatywnie krótkim czasie. W ramach niezawodności można wyróżnić cztery podcharakterystyki: dojrzałość, odporność na błędy, odtwarzalność oraz dostępność. Omówimy je teraz po kolei.

16.6.1. Dojrzałość (maturity) Dojrzałość to stopień, w jakim oprogramowanie spełnia wymogi niezawodności podczas normalnego użytkowania. Przez „normalne” rozumiemy tu użytkowanie typowe, zgodne z profilem operacyjnym. pozwalającymi wyrazić dojrzałość ilościowo są:

Standardowymi

metrykami

MTBF (ang. Mean Time Between Failures), czyli średni czas między awariami; MTTR (ang. Mean Time To Repair), czyli średni czas do naprawy awarii; MTTF (ang. Mean Time To Failure), czyli średni czas do awarii; współczynnik awarii. Między pierwszymi trzema metrykami zachodzi następujący, dosyć oczywisty związek: MTBF = MTTR + MTTF Po wystąpieniu awarii musi upłynąć pewien czas, w którym system powróci do normalnego działania (np. przez restart, czy wykonanie jakichś operacji naprawczych). Po upływie tego czasu system działa do momentu nastąpienia kolejnej awarii. Suma tych czasów jest czasem między awariami. Sytuacja ta zilustrowana jest na rysunku 16.16. Średnia z tych wartości to właśnie MTBF, która jest podstawową metryką dojrzałości.

Rysunek 16.16. Relacje między metrykami MTTR, MTTF i MTBF średni czas między awariami (ang. Mean Time Between Failures, MTBF) – średnia arytmetyczna czasów między awariami systemu w określonym przedziale czasowym średni czas do naprawy (ang. Mean Time To Repair, MTTR) – średnia arytmetyczna czasu, po którego upływie system jest uruchamiany po wystąpieniu awarii Współczynnik awarii jest definiowany jako iloraz liczby awarii (często ograniczonych do określonego typu lub typów) na jednostkę wykonania, np. przedział czasowy, liczbę cykli procesora, liczbę uruchomień. współczynnik awarii (ang. failure rate) – stosunek liczby awarii w danej kategorii do określonej jednostki miary, np. awarie na jednostkę czasu, liczbę transakcji czy na liczbę uruchomień komputera [7] Dojrzałość można również rozumieć jako wydajność systemu po długim okresie użytkowania go. Posiadacze systemów operacyjnych typu Windows często obserwują sukcesywny spadek wydajności systemu, co po pewnym czasie skutkuje podjęciem decyzji o reinstalacji lub „wyczyszczeniu” systemu. dojrzałość (ang. maturity) – zdolność oprogramowania do uniknięcia awarii jako rezultatu defektów [3]

Metryki dojrzałości, takie jak wspomniane wcześniej, mogą również służyć do oceny jakości produktu i podejmowania decyzji o terminie wypuszczenia produktu na rynek. Na rysunku 16.17 przedstawiono rzeczywiste dane o niezawodności jednego z projektów pochodzących z bazy SLED (Software Life Cycle Empirical/Experience Database) [157]. Widać, że wraz z upływem czasu fazy wytwórczej czasy między awariami stają się coraz dłuższe, co oznacza, że awarie następują coraz rzadziej.

Rysunek 16.17. Średni czas między awariami

16.6.2. Odporność na błędy (fault tolerance, robustness) Odporność na błędy to stopień, w jakim oprogramowanie jest w stanie spełniać poprawnie swoje funkcje mimo obecności błędów i wystąpień awarii, zarówno sprzętowych, jak i programowych. Testowanie odporności na błędy może polegać m.in. na:

inspekcji modułów pod kątem obsługi wyjątków (czy każdy możliwy wyjątek jest odpowiednio obsłużony?); testowaniu działania programu w sytuacji błędów pamięci; testowaniu

działania

programu

w

sytuacji

błędów

w

plikach

zewnętrznych; testowaniu działania programu w sytuacji problemów z urządzeniami peryferyjnymi (np. co się stanie, gdy podczas drukowania odłączymy drukarkę?) itp. Naturalną techniką stosowaną w testowaniu odporności na błędy są ataki usterkowe na oprogramowanie (patrz podrozdz. 10.6). Aby dobrze przetestować poziom tej charakterystyki jakościowej, tester musi dobrze znać system i znać potencjalne przyczyny wystąpień awarii. Często, aby wymusić wystąpienie określonych, specyficznych błędów, stosuje się instrumentację kodu, np. za pomocą debugera. Dzięki temu tester może modyfikować wartości różnych zmiennych podczas działania programu, wymuszając powstawanie awarii i obserwować zachowanie systemu. odporność (ang. fault tolerance, robustness) – stopień, w jakim system lub moduł może działać prawidłowo przy nieprawidłowych danych lub przy dużym obciążeniu [7] tolerowanie błędów (ang error tolerance) – zdolność modułu lub systemu do kontynuowania prawidłowego działania mimo podania błędnych danych wejściowych [7] tolerowanie usterek (ang. fault tolerance) – zdolność oprogramowania do utrzymania określonego poziomu wydajności w przypadku występowania usterek (defektów) lub naruszenia jego interfejsów [3]; patrz także: niezawodność, odporność

16.6.3. Odtwarzalność (recoverability) Odtwarzalność to stopień, w jakim oprogramowanie jest w stanie przywrócić stan systemu, w tym wszystkie dane oraz operacje utracone lub przerwane na skutek wystąpienia awarii. Przykładowymi testami mogą być: zwykłe wyłączenie

zasilania komputera podczas działania programu, zabicie procesu testowanego programu, symulacja uszkodzenia dysku itp. Odtwarzalność często testuje się w systemach z redundancją (np. RAID – ang. Redundant Array of Indepedent Disks), w których dane są zapisywane redundantnie, na wielu nośnikach. W przypadku awarii jednego z nich, system przełącza się na wykorzystywanie innego. Testowanie może sprawdzać, czy przełączenie to zadziałało poprawnie. Innym przykładem jest testowanie, czy system właściwie tworzy oraz wykorzystuje kopie zapasowe plików. Metryką ilościową mierzącą poziom odtwarzalności może być MTTR (średni czas do naprawy). Częściej jednak interesuje nas aspekt jakościowy tej charakterystyki, czyli po prostu to, czy system potrafi sam doprowadzić się do stanu sprzed awarii. odtwarzalność (ang. recoverability) – zdolność oprogramowania do osiągania określonego poziomu wydajności i przywracania danych uszkodzonych przez awarię [3]

16.6.4. Dostępność (availability) Dostępność to czas, w którym testowany system jest dostępny, podzielony przez całkowity czas użytkowania tego systemu. Licznik tej metryki może być mniejszy od mianownika na skutek występowania różnego rodzaju awarii oraz akcji, jakie system musi podjąć, aby odtworzyć swój poprawny stan. Dostępność jest więc kombinacją dojrzałości (częstość awarii), odporności na błędy oraz odtwarzalności (czas naprawy). Stanowi ona raczej charakterystykę związaną z oceną jakości niż samym testowaniem. Stopień dostępności pośrednio określa jakość oprogramowania i pozwala wnioskować, czy system był wystarczająco przetestowany. dostępność (ang. availability) – stopień, w jakim moduł lub system działa i jest dostępny, gdy jest wymagane jego użycie, często wyrażany w procentach [7]

16.7. Testowanie zabezpieczeń (security) Zabezpieczenie to stopień, w jakim system lub program chroni dane oraz informacje przed dostępem do nich osób niepowołanych. Z punktu widzenia

testowania

można

wyróżnić

funkcjonalne

oraz

techniczne

testowanie

zabezpieczeń. To pierwsze nazywa się również testami penetracyjnymi. Funkcjonalne testowanie zabezpieczeń sprawdza bezpieczeństwo programu na poziomie i w ramach samej aplikacji (np. poprawność przydziału praw dostępu oraz ich egzekwowanie). Techniczne testowanie zabezpieczeń dotyczy bardziej ataków na oprogramowanie, w tym ataków hakerskich. zabezpieczenie (ang. security) – atrybuty oprogramowania określające jego zdolność do zapobiegania nieautoryzowanym przypadkowym lub umyślnym dostępem do programu i do danych [3] Wymagania dotyczące zabezpieczeń zwykle nie są wyszczególnione w dokumencie wymagań, co sprawia, że problemy z bezpieczeństwem powracają nieustannie przy okazji tworzenia kolejnych systemów. Być może powodem takiego stanu rzeczy jest fakt, że testowanie zabezpieczeń jest w zasadzie obszarem zupełnie odmiennym od pozostałych technik testowania. Wymaga wiedzy eksperckiej i często przeprowadzenie testów zabezpieczeń zleca się zewnętrznym firmom specjalizującym się w tej dziedzinie. Najpopularniejsze techniki testowania zabezpieczeń to ataki oraz taksonomie defektów. Inżynier jakości powinien pamiętać, że często zapewnienie wzrostu bezpieczeństwa powoduje spadek innych charakterystyk jakości, takich jak użyteczność czy efektywność. Na przykład zbyt mocno zabezpieczony system, wymagający wprowadzania wielu długich, skomplikowanych haseł oraz wymuszający ich częstą zmianę powoduje spadek użyteczności oprogramowania. Istnieją różne organizacje zajmujące się poprawą zabezpieczeń oprogramowania. Na ich stronach internetowych można znaleźć wiele ciekawych i przydatnych informacji na temat bezpieczeństwa, popularnych ataków oraz sposobów obrony przed nimi. Przykładem takiej organizacji jest OWASP (Open Web Application Security Project) [158]. W ramach charakterystyki zabezpieczeń można wyróżnić pięć podcharakterystyk: poufność, integralność, niezaprzeczalność, odpowiedzialność oraz uwierzytelnienie. Omówimy je teraz po kolei.

16.7.1. Poufność (confidentiality)

Poufność to stopień, w jakim system zapewnia, że dane są dostępne tylko dla osób do tego upoważnionych. Poufność można uzyskać, stosując system nadawania ról i uprawnień użytkownikom. Dostęp do danych może być także zabezpieczany różnego rodzaju hasłami.

16.7.2. Integralność (integrity) Integralność to zapewnianie dokładności i spójności danych. Wysoki poziom integralności oznacza, że dane nie mogą być modyfikowane w nieautoryzowany sposób.

16.7.3. Niezaprzeczalność (non-repudiation) Niezaprzeczalność oznacza, że użytkownik nie może wyprzeć się wykonania pewnych akcji w systemie. Niezaprzeczalność można realizować zarówno za pomocą logowania wszystkich czynności wykonywanych przez użytkownika, jak i przy użyciu technik kryptograficznych, np. szyfrowania z kluczem prywatnym i publicznym. Testowanie niezaprzeczalności może polegać na weryfikacji tego, czy system rzeczywiście loguje określone akcje lub czy wykorzystuje właściwy algorytm szyfrowania asymetrycznego. Przykładem ataku kryptologicznego związanego z niezaprzeczalnością może być tzw. atak człowiek pośrodku. atak człowiek pośrodku (ang. man in the middle attack) – przechwycenie, naśladowanie lub modyfikowanie oraz następnie przesłanie wiadomości (np. transakcji kartą kredytową) przez stronę trzecią w ten sposób, że użytkownik pozostaje nieświadomy istnienia strony trzeciej

16.7.4. Odpowiedzialność (accountability) Odpowiedzialność to stopień, w jakim działania dokonywane w systemie mogą być jednoznacznie odniesione do konkretnego użytkownika. Odpowiedzialność można uzyskać np. przez stosowanie podpisów cyfrowych.

16.7.5. Uwierzytelnianie (authenticity) Uwierzytelnianie (autentykacja) to stopień, w jakim można udowodnić tożsamość użytkownika. Istnieją trzy typowe metody weryfikacji tożsamości użytkownika:

przez coś, co użytkownik zna (np. hasło); przez coś, co użytkownik ma (np. token); przez coś, czym użytkownik jest (np. dane biometryczne). Testowanie uwierzytelniania polega na weryfikacji, czy przyjęty schemat weryfikacji tożsamości działa poprawnie (w szczególności można sprawdzać jakość samych technik uwierzytelniających, np. to, czy system dopuszcza definiowanie zbyt prostych haseł).

16.8. Testowanie pielęgnowalności (maintainability) Pielęgnowalność (utrzymywalność) dotyczy głównie czynności wykonywanych w fazie utrzymania oprogramowania. Jest to stopień, w jakim produkt może być modyfikowany po jego wydaniu. Wyjątkiem jest testowalność – podcharakterystyka pielęgnowalności dotycząca właściwości oprogramowania na etapie jego tworzenia. Standardem związanym z pielęgnowalnością oprogramowania jest IEEE 1219 [159]. Testowanie pielęgnowalności sprawdza nie tylko możliwość, lecz także łatwość i efektywność modyfikacji oprogramowania czy systemu. Zmiany w oprogramowaniu mogą być wykonywane zarówno przez deweloperów, jak i pracowników wsparcia technicznego oraz samych użytkowników. Pielęgnowalność dotyczy w szczególności instalacji uaktualnień (ang. update) oraz ulepszeń (ang. upgrade). podcharakterystyki:

Można

w

jej

ramach

wyróżnić

następujące

modularność; powtórne użycie; analizowalność; modyfikowalność; testowalność. pielęgnowalność (ang. maintainability) – łatwość, z jaką oprogramowanie może być modyfikowane w celu naprawy defektów, dostosowania do nowych wymagań, modyfikowane w celu ułatwienia przyszłego utrzymania lub dostosowania do zmian zachodzących w jego środowisku [3]

16.8.1. Modularność (modularity) Moduł

to

dający

się

logicznie

wydzielić

fragment

oprogramowania,

odpowiedzialny za realizację określonej funkcjonalności. Moduły komunikują się ze sobą za pomocą interfejsów. Modularność to stopień, w jakim oprogramowanie lub system jest zbudowany z komponentów w taki sposób, że zmiana w jednym z nich (lub jego wymiana na inny) ma możliwie mały wpływ na działanie innych komponentów. Modularność jest ważna z punktu widzenia projektu systemu, ale ma też duże znaczenie z punktu widzenia testowania. Jeśli system jest zaprojektowany z dobrze zorganizowanych modułów, zmiana jednego z nich nie powinna nieść ze sobą dużego ryzyka powstania defektów w innych komponentach lub na styku komponentu zmienianego z innymi modułami, z którymi się komunikuje. Modularność ma wiele zalet: pozwala radzić sobie z dużą złożonością systemu (metoda divide et impera – dziel i rządź) i efektywnie zarządzać procesem wytwórczym; pozwala na fizyczne oddzielenie od siebie poszczególnych części systemu, przez co zmiany zwykle dotyczą małych, lokalnych fragmentów systemu i nie wpływają na inne jego obszary; pozwala na jasny proces mapowania modułów na wymagania (w idealnym przypadku jedno wymaganie jest realizowane przez jeden moduł systemu); pozwala na lepsze zrozumienie systemu, jego struktury i logiki działania.

Rysunek 16.18. Zależności między modułami i odpowiadająca im macierz struktury projektu Moduły powinny cechować się wysoką kohezją oraz niskim powiązaniem (ang. coupling). Cechy te są szczególnie istotne w paradygmacie obiektowym. Istnieją narzędzia analizy statycznej pozwalające wyrażać te cechy liczbowo. Na rysunku 16.8 jest przedstawiona przykładowa struktura systemu złożonego z sześciu modułów (A, B, C, D, E, F) oraz odpowiadająca im tzw. macierz struktury projektu (ang. Design Structure Matrix, DSM). Jest to symetryczna, kwadratowa macierz, w której wiersze i kolumny oznaczają poszczególne moduły a X na przecięciu wiersza P i kolumny Q oznacza, że moduły P i Q są ze sobą w jakiś sposób powiązane (na przykład jeden z nich dostarcza dane do drugiego lub wymieniają się informacjami). Im więcej zależności między modułami, tym niższa pielęgnowalność. W systemie z gęstą siatką powiązań między poszczególnymi elementami trudniej o nieświadome wprowadzenie defektów.

jest

dokonywać

zmian

i

łatwiej

kohezja (ang. cohesion) – stopień powiązania elementów w ramach danego modułu, klasy bądź podsystemu; przyjmuje się, że elementy oprogramowania powinna cechować wysoka kohezja powiązanie (ang. coupling) – stopień powiązania między modułami, klasami bądź podsystemami; przyjmuje się, że powiązanie powinno być możliwie małe

Modularność jest cechą istotną dla rozwoju oprogramowania typu opensource, gdzie nad jednym projektem może pracować i rozwijać go wielu programistów. Baldwin i Clark w pracy [160] wykazali (na gruncie teoretycznym!), że modularność jest jedną z kluczowych cech wpływających na wysoki poziom inicjatywy ze strony deweloperów w rozwijaniu danego oprogramowania. Innymi słowy, im większa modularność, tym chętniej programiści biorą udział w dalszym rozwijaniu danej aplikacji open-source.

16.8.2. Powtórne użycie (reusability) Powtórne użycie określa stopień, w jakim dany fragment oprogramowania może być użyty w więcej niż jednym systemie lub przy tworzeniu innych elementów oprogramowania. Im mniej dany komponent „wie” o systemie, w którym działa i im mniej zależy od pozostałych komponentów, tym łatwiej jest go powtórnie użyć w innym miejscu. Nie zawsze taki fragment oprogramowania można przenieść wprost z jednego systemu do innego. Czasami wymaga to dodatkowego oprogramowania takiego komponentu, czy wprowadzenia mniejszych lub większych zmian w jego kodzie. Im większe możliwości powtórnego użycia, tym lepsza pielęgnowalność systemu. Testowanie tej podcharakterystyki polega głównie na stosowaniu przeglądów i inspekcji.

16.8.3. Analizowalność (analyzability) Analizowalność to poziom efektywności, z jakim możliwe jest ustalenie wpływu, jaki na cały system będzie mieć zmiana w jednym lub kilku miejscach tego systemu. Cecha ta określa również łatwość, z jaką dany produkt lub system można diagnozować pod kątem jego niedoskonałości, a także lokalizować przyczyny awarii. Analizowalność w fazie utrzymania wpływa na jakość pracy wsparcia technicznego (szybkość, z jaką są lokalizowane i usuwane awarie zgłaszane przez użytkowników), a w fazach produkcji oprogramowania – na efektywność procesu debugowania, czyli lokalizacji i usuwania defektów znalezionych w procesie testowania. analizowalność (ang. analyzability) – zdolność wytwarzanego produktu do bycia zdiagnozowanym pod kątem braków lub przyczyn awarii lub pod kątem

rozpoznania części do modyfikacji [3]; patrz także: pielęgnowalność Analizowalność można zwiększyć przez wprowadzenie do kodu źródłowego mechanizmów pozwalających na analizę przez program swoich własnych awarii, np. przez stworzenie mechanizmu obsługi wyjątków wraz z logowaniem zawartości

określonych

zmiennych

oraz

procesów,

które

zachodziły

w działającym programie do momentu wystąpienia awarii. Testowanie analizowalności sprawdza, w jakim stopniu jest zapewniona łatwość w lokalizacji i usuwaniu defektów.

16.8.4. Modyfikowalność (modifiability) Modyfikowalność to łatwość, z jaką system lub program może być modyfikowany bez wprowadzania defektów lub zmniejszania w inny sposób jego jakości produktowej. Na modyfikowalność duży wpływ mają modularność oraz analizowalność. Ta pierwsza wpływa na łatwość wprowadzania zmian, natomiast od drugiej zależy poziom ryzyka pojawienia się nowych defektów podczas wprowadzania zmian. modyfikowalność (ang. changeability, modifiability) – zdolność produktu oprogramowania do wprowadzania wyspecyfikowanych zmian [3]; patrz także: pielęgnowalność Przykładem modyfikowalności jest możliwość zdalnego pobrania przez program łatki (ang. patch), która jest w stanie zmienić kod wykonywalny programu. Sytuacje takie najczęściej zdarzają się wtedy, gdy w oprogramowaniu zostanie wykryta podatność na jakiś atak lub gdy zachodzi potrzeba dostosowania oprogramowania do współpracy z nowym sprzętem, systemem operacyjnym lub innym oprogramowaniem. Testowanie modyfikowalności może sprawdzać, czy proces ściągnięcia i zainstalowania łatki przebiega pomyślnie i czy po jego zakończeniu w oprogramowaniu wprowadzono oczekiwane zmiany.

16.8.5. Testowalność (testability) Testowalność to łatwość, z jaką można wyprowadzić dla oprogramowania lub systemu kryteria testowe, a także wykonać testy w celu weryfikacji spełnienia tych kryteriów. Podczas projektu nowego systemu nie tylko musimy zadać sobie

pytanie „czy potrafimy to zbudować?”, lecz także „czy potrafimy to przetestować?”. Dobra testowalność systemów staje się coraz ważniejsza [161], z uwagi na wzrastającą rolę inżynierii jakości i testowania. testowalność (ang. testability) – właściwość oprogramowania umożliwiająca testowanie go po zmianach4 [3] przegląd testowalności (ang. testability review) – szczegółowe sprawdzenie podstawy testów w celu określenia, czy jest ona na odpowiednim poziomie jakości, aby mogła posłużyć jako dokument wejściowy dla procesu testowego [28] Poziom testowalności ma znaczenie dla wysiłku testerów – im mniej testowalny jest produkt, tym więcej pracy trzeba włożyć w projektowanie, implementację i wykonanie testów [162]. W skrajnych przypadkach testowanie pewnych aspektów programu może w ogóle być niemożliwe. Na testowalność ma wpływ wiele czynników, takich jak: kontrolowalność, czyli możliwość weryfikacji, w jakim stanie aktualnie znajduje się program, a także łatwość wpływania na sposób działania programu; ma to znaczenie przy białoskrzynkowych technikach projektowania testów, gdzie chcemy wymusić np. przejście sterowania ściśle określoną ścieżką; obserwowalność, czyli możliwość obserwowania zarówno końcowych wyników działania programu, jak i kroków pośrednich; często w celu osiągnięcia odpowiednio wysokiego poziomu obserwowalności stosuje się narzędzia do debugowania, które potrafią na bieżąco podczas wykonywania programu pokazywać stan zmiennych, obiektów czy struktur danych; izolowalność, czyli możliwość testowania modułu w izolacji od innych komponentów; im mniejsza izolowalność, tym większa potrzeba tworzenia namiastek lub sterowników (jeśli moduły, z którymi testowany komponent się komunikuje, nie są jeszcze zaimplementowane) lub bardziej skomplikowanych i dłużej trwających testów (jeśli wszystkie moduły są zaimplementowane, ale test wymaga np. komunikacji między wieloma modułami);

złożoność, czyli stopień komplikacji wewnętrznej struktury modułu; im bardziej skomplikowany kod i jego struktura, tym trudniej zaprojektować testy i uzyskać odpowiedni stopień pokrycia; istnienie cyklicznych zależności między modułami sprawia z kolei, że nie wiadomo, jaka powinna być kolejność testowania tych modułów i może to wymagać implementacji namiastek/sterowników [163]; modularność rozumiana jako podział odpowiedzialności między modułami; dzięki dobrej modularności pojedynczy test dotyczy pojedynczej funkcjonalności; stosowanie niektórych wzorców projektowych w paradygmacie obiektowym może utrudniać testowanie, np. wykorzystywanie wzorca Singleton powoduje, że testy na instancji tej klasy będą od siebie zależne, tzn. wynik jednego może zależeć od wyniku drugiego; zrozumiałość, czyli stopień, udokumentowany i objaśniony; automatyzacja, automatycznych.

czyli

w

możliwość

jakim

testowany

przeprowadzenia

kod

jest

testów

Niektórzy autorzy uważają, że na poziomie testów modułowych testowalność może być zwiększona przez wykorzystanie takich technik jak Test Driven Development, jednak testowalność jest w tym przypadku produktem ubocznym, a nie rezultatem zaangażowanych działań i wysiłku. W podejściu TDD nie istnieje żaden proces, który pozwalałby na systematyczne zwiększenie poziomu testowalności. TDD jest podejściem metodą prób i błędów do testowalności i silnie zależy od refaktoringu. Metoda może dobrze sprawdzać się dla małych systemów, ale w dużych projektach czas i wysiłek poświęcony na refaktoring zarówno implementacji, jak i projektu staje się problematyczny [164]. Testowalność jest nie tylko cechą wykonywalnego kodu, lecz także wszystkich artefaktów mogących podlegać testowaniu. W szczególności można określać testowalność wymagań, co jest efektywną metodą obniżania ryzyka rezydualnego w projektowanym systemie. Wykrycie nietestowalnych wymagań pozwala bowiem przeformułować je tak, aby każdy aspekt systemu mógł podlegać kontroli jakości. Wymagania w szczególności przekładają się na rozwiązania projektowe, a wiemy, że wykrycie błędu projektowego po przekazaniu programu do użytkowania skutkuje o wiele kosztowniejszym usunięciem go niż np. w fazie projektu wysokiego poziomu. Testowalność specyfikacji wymagań jest

„prawdopodobnie

najważniejszym

celem

wymagań,

a

jednocześnie

najtrudniejszym do osiągnięcia” [165]. Testowalność może również dotyczyć dziedziny problemu, którego dotyczy tworzona aplikacja. Określa ona łatwość, z jaką można zaprojektować testy, biorąc w szczególności pod uwagę dwa czynniki: problem wyroczni – specyfika dziedziny problemu może utrudniać testowanie, jeśli nie istnieje wyrocznia testowa, bo np. określenie poprawnego wyjścia jest trudne lub czasochłonne dla wykonania ręcznego lub też projektowane oprogramowanie będzie musiało produkować wyjście o tak dużym rozmiarze, że weryfikowanie go staje się niepraktyczne; interakcja z zewnętrznymi systemami – częścią problemu jest wymagana interakcja z zewnętrznymi systemami; interakcja oraz systemy te same w sobie mogą powodować trudności w testowaniu [164]. Souza i Arakaki [166] proponują pięć następujących abstrakcyjnych wzorców, których stosowanie przyczynia się do zwiększenia poziomu testowalności: Built-In Self-Testing (BIST) – koncepcja zaczerpnięta z systemów hardware’owych, polegająca na tym, że komponent raportuje swój własny stan. Deweloper musi zaimplementować standardowy interfejs we wszystkich klasach wykorzystujących BIST. Dependency Injection (DI) – gdy komponent biznesowy zależy od systemów zewnętrznych (tzw. third-party component, bazy danych, webserwisy itp.) często ciężko usunąć lub zmienić te zależności bez zmiany kodu źródłowego. Komponent powinien mieć te zależności wbudowane, zamiast nimi samemu zarządzać. Konsekwencją jest to, że tester będzie musiał tworzyć zaślepki i sterowniki, aby zastąpić nieistniejące komponenty, od których zależy działanie testowanego modułu. Dynamic Component Management Extension (DCME) – gdy zespół testerów ma do dyspozycji wiele namiastek i sterowników symulujących zewnętrzne zachowanie systemu, należy umożliwić ich dynamiczną zmianę, tzn. dynamiczny wybór konkretnej implementacji.

Testability Logger (TL) – zdarzenia oraz dane związane z testowaniem muszą być logowane dla celów procesu testowego, co może prowadzić do redundantnego kodu. Logger dostarcza scentralizowaną kontrolę funkcjonalności logowania i sam zajmuje się sposobem klasyfikacji i logowania zdarzeń. Taki logger zwiększa obserwowalność systemu, natomiast wpływa negatywnie na wydajność systemu. Testability Interceptor (TI) – pozwala na zwiększenie obserwowalności i kontrolowalności przez pozwolenie komponentom na monitorowanie i dynamiczną zmianę ich zachowań. Tester może obserwować i modyfikować funkcjonalność bez ingerencji w wewnętrzną strukturę logiczną komponentu. Testowalność można również zwiększać przez znajdowanie w kodzie lub projekcie architektury tzw. antywzorców, czyli złych praktyk programistycznych obniżających testowalność produktu. antywzorzec (ang. anti-pattern) – powtarzalna akcja, proces, struktura lub rozwiązanie wielokrotnego użytku, które początkowo wydawało się korzystne i jest często używane, ale jest nieskuteczne lub nieproduktywne w zastosowaniu

16.9. Testowanie przenaszalności (portability) Często zdarza się, że zmienia się środowisko, w którym działa oprogramowanie. Może być to spowodowane np. modernizacją systemu operacyjnego do nowej wersji lub wręcz przeniesieniem oprogramowania na inną platformę. Przenaszalność to stopień, w jakim system lub komponent może być przeniesiony z jednego środowiska sprzętowego, oprogramowania lub środowiska operacyjnego do innego. Przykładem dobrej przenaszalności są programy pisane w Javie – jeśli docelowe środowisko, w którym taki program ma działać, ma maszynę wirtualną Javy, to program będzie pracował poprawnie niezależnie od konfiguracji systemu. W ramach przenaszalności można wyróżnić trzy podcharakterystyki: adaptowalność, instalowalność oraz zastępowalność. przenaszalność (ang. portability) – łatwość, z jaką oprogramowanie może być przeniesione z jednego środowiska sprzętowego lub programowego do innego środowiska [3]

16.9.1. Adaptowalność (adaptability) Adaptowalność to stopień, w jakim program lub system potrafi dostosować się do innego lub zmienionego środowiska. Powinna być zapewniona nie tylko pod kątem zmian hardware’u czy systemu operacyjnego, lecz także na poziomie poszczególnych elementów oprogramowania. Przykładem może być wygląd ekranu, który powinien dostosować się do innej niż domyślna rozdzielczości ekranu. W przypadku stron internetowych jest ważne, aby zapewnić adaptowalność do wszystkich (a przynajmniej najczęściej używanych) przeglądarek internetowych. Techniką często stosowaną przy projektowaniu stron www jest tzw. responsive web design. Polega ona na takim projektowaniu strony, aby jej wygląd i układ automatycznie dostosowywał się do zmiany rozmiaru okna, w którym ma być wyświetlona. Jest to szczególnie istotne w przypadku aplikacji mobilnych, wyświetlanych na urządzeniach przenośnych, które mogą być ustawione przez użytkownika zarówno wertykalnie, jak i horyzontalnie. adaptowalność, zdolność do adaptacji (ang. adaptability) – zdolność oprogramowania do dostosowania się do różnych środowisk, bez konieczności stosowania działań lub środków innych niż te, które dostarczono do tego celu [3]; patrz także: przenaszalność Testowanie adaptowalności może polegać na zwykłym czarnoskrzynkowym testowaniu opartym na scenariuszach użycia, może też przyjąć formę inspekcji. Na przykład, dla stron www można zweryfikować poprawność responsive web design przez sprawdzenie, czy kod html zawiera w sekcji „head” instrukcję

Listing 16.1. Fragment kodu html obowiązkowego w responsive web design Najczęściej, w przypadku wielu możliwych wersji różnych komponentów środowiska docelowego, stosuje się technikę pokrycia kombinatorycznego (np.

pairwise) tak, aby każda para wersji dwóch różnych charakterystyk występowała w jednym teście.

16.9.2. Instalowalność (installability) Instalowalność to stopień, w jakim oprogramowanie lub system może być efektywnie zainstalowane i odinstalowane w określonym środowisku. Testowanie instalowalności polegać może na: testowaniu kreatora instalacji; przeprowadzeniu standardowego procesu instalacji zgodnie z instrukcją użytkownika; przerwaniu instalacji w pewnym momencie i sprawdzeniu, czy system wrócił do stanu przed rozpoczęciem instalacji, np. czy w rejestrze Windows nie pozostały jakieś wpisy instalowanej aplikacji lub czy na dysku nie pozostały katalogi bądź pliki tymczasowe stworzone przez program instalacyjny; odinstalowaniu poprawnie zainstalowanego programu i zweryfikowaniu, czy system wrócił do stanu sprzed instalacji; próbie powtórnej instalacji i sprawdzeniu, czy jest to możliwe, a jeśli tak, to czy nie zostają nadpisane jakieś dane z poprzedniej instalacji. kreator instalacji (ang. installation wizard) – oprogramowanie dostarczone na odpowiednich nośnikach, które prowadzi instalatora przez proces instalacji; zazwyczaj wykonuje proces instalacji, informuje o jego wynikach i prosi o wybór opcji instalowalność (ang. installability) – zdolność oprogramowania do bycia zainstalowanym w wyspecyfikowanym środowisku [3]; patrz także: przenaszalność

16.9.3. Zastępowalność (replaceability) Zastępowalność to stopień, w jakim dany produkt może zastąpić inny produkt w tym samym celu i w tym samym środowisku. Zastępowalność nie dotyczy wyłącznie innych aplikacji, ale także nowych wersji tej samej aplikacji. W takim

przypadku sprawdza się, czy ulepszenie (ang. upgrade) przebiega poprawnie i czy funkcjonalność programu nie uległa niepożądanym zmianom. zastępowalność (ang. replaceability) – zdolność oprogramowania do wykorzystania w miejsce innego oprogramowania o takim samym przeznaczeniu i w takim samym środowisku [3]; patrz także: przenaszalność

1 Skrajnym przykładem tego efektu jest tzw. zasada nieoznaczoności Heisenberga, która mówi, że istnieją pary wielkości, których nie można zmierzyć jednocześnie z dowolną dokładnością (np. położenie i pęd cząstki) – zasada ta nie wynika z niedoskonałości metod czy urządzeń pomiarowych, lecz z samej natury rzeczywistości. 2 Taka sytuacja może wystąpić wtedy, gdy w narzędziu do automatycznej generacji obciążenia maksymalna wydajność samego generatora jest niższa niż wydajność testowanego programu w obsłudze tego obciążenia. 3 Losowy dobór uczestników eksperymentu jest wbrew pozorom skomplikowaną i wysoce nietrywialną procedurą. Uzyskanie reprezentatywnej próby może być bardzo trudne zwłaszcza w przypadku, gdy uczestnicy rekrutują się spośród aktualnych użytkowników oprogramowania tworzących homogeniczną populację. 4 Według definicji ISO 9126 testowalność dotyczy testów po zmianach w oprogramowaniu, jednak definicja ta może obejmować ogół cech oprogramowania w ogóle umożliwiających bądź ułatwiających każde jego testowanie, nie tylko po wprowadzeniu zmian do kodu.

17. Testowanie jakości danych

17.1. Model jakości danych W podrozdziale 1.3 opisaliśmy przykład katastrofy projektu Mars Climate Orbiter. Awaria spowodowana była tym, że dwa zespoły pracujące nad dwoma osobnymi modułami oprogramowania używały odmiennych jednostek – jedna wykorzystywała niutony, druga – funty (tzw. funty siły, lbf). Przyczyną tej awarii była zła jakość danych, która jest bardzo częstym powodem występowania błędów w oprogramowaniu. Norma ISO 25012 [146] jest poświęcona w całości modelowi jakości danych, aby charakterystyki niefunkcjonalnej.

podkreślić

znaczenie

testowania

tej

jakość danych (ang. data quality) – atrybut danych opisujący stopień ich poprawności względem predefiniowanych kryteriów, np. wymagań biznesowych, wymagań na integralność danych, spójność danych itp. Model ISO wyróżnia piętnaście podcharakterystyk jakości danych, dzieląc je na trzy grupy (patrz rys. 14.4): inherentne, tzn. jakość tych danych zależy od nich samych, wynika np. z reguł biznesowych danej aplikacji lub z relacji między wartościami różnych zmiennych; zależne od systemu, tzn. ich jakość zależy od aspektów technologicznych, na przykład precyzja zależy od architektury komputera, a odtwarzalność czy przenaszalność od odpowiedniego oprogramowania; należące do obu powyższych grup jednocześnie.

Dane, o których mowa, to zarówno dane wewnętrzne programu (np. wartości poszczególnych zmiennych), jak i wszystkie dane „zewnętrzne”, przetwarzane przez program. W szczególności kontroli jakości poddane być mogą wejścia do programu podawane przez użytkownika bądź wczytywane ze źródeł zewnętrznych, np. z pliku lub z sieci, a także wyjścia w formie raportów, plików wyjściowych czy wszelkiego rodzaju informacji, jakie pojawiają się na ekranie. Obecnie wiele aplikacji oraz (zwłaszcza) stron internetowych jest sterowanych informacją i zawartością (ang. information & content driven), co sprawia, że jakość informacji staje się kluczowym atrybutem jakościowym tego typu systemów. Niektórzy autorzy (np. [167]) proponują swoje wersje modelu jakości danych, będące rozszerzeniem modelu ISO/IEC dopasowanym właśnie do kontekstu systemów sterowanych informacją. Omówimy teraz poszczególne charakterystyki jakości danych wyróżnione w normie ISO 25012. Tester powinien mieć świadomość, że przyczyną złej jakości danych często są defekty w oprogramowaniu. Testowanie jakości danych może pośrednio wskazać obecność tych usterek. Oczywiście defektem jest również niska jakość danych sama w sobie.

17.2. Charakterystyki inherentne 17.2.1. Dokładność (accuracy) Dokładność danych to stopień, w jakim dane reprezentują poprawne wartości w danym kontekście użycia. Można wyróżnić dwa jej typy: syntaktyczną oraz semantyczną. Syntaktyczna poprawność oznacza, że postać danych ma właściwą strukturę. Semantyczna poprawność oznacza, że znaczenie wartości danej odpowiada prawdzie. dokładność (ang. accuracy) – zdolność oprogramowania do zapewnienia właściwych lub uzgodnionych rezultatów lub efektów z wymaganym poziomem precyzji [3] W tabeli 17.1 jest przedstawiona baza danych adresów urzędów wojewódzkich w Polsce w postaci tabeli bazy danych. W wierszu o id=4 kod pocztowy jest niepoprawny syntaktycznie, ponieważ po kresce występują tylko dwie cyfry,

a powinny być trzy. W wierszu o id=2 nazwa ulicy napisana jest małą literą, a powinna być napisana wielką, co również jest błędem syntaktycznym. W wierszu o id=6 adres e-mailowy, choć poprawny syntaktycznie, jest błędny semantycznie, ponieważ domena powinna nazywać się malopolska.uw.gov.pl, a nie malapolska.uw.gov.pl1. Tabela 17.1. Baza danych adresów urzędów wojewódzkich

id województwo ulica

kod miasto

e- mail

1

dolnoś ląs kie

pl. Pows tańców Wars zawy 1

50Wrocław 951

[email protected]

2

kujaws kopomors kie

jag iellońs ka 3

85Bydg os zcz 950

[email protected]

3

lubels kie

S pokojna 4

20Lublin 914

[email protected]

4

lubus kie

Jag iellończyka 668 40

5

łódzkie

Piotrkows ka 103

90Łódź 425

s [email protected] ov.pl

6

małopols kie

Mog ils ka 26

31Kraków 156

urząd@malapols k

7

mazowieckie

Dług a 5

00Wars zawa 263

ws o@mazowieck

8

opols kie

Pias tows ka 14

45Opole 082

ws [email protected]

9

podkarpackie

Grunwaldzka 15

35Rzes zów 959

o@rzes zow.uw.g

Gorzów ws [email protected] orzo Wielkopols ki

10 podlas kie

Mickiewicza 3 15- Białys tok 213

ws o@białys tok.uw

11 pomors kie

Okopowa 21/27

80Gdańs k 810

ws o@g dans k.uw.

12 ś ląs kie

Jag iellońs ka 25

40032

ws oim@katowice

13 ś więtokrzys kie

al. IX Wieków Kielc 3

25Kielce 516

ws [email protected]

warmińs komazurs kie

al. Mar. J. Piłs uds kieg o 7/9

10Ols ztyn 575

s ekrs [email protected] zty

14

17.2.2. Zupełność (completeness) Zupełność danych to stopień, w jakim dane opisujące określony obiekt mają wartości dla wszystkich atrybutów w danym kontekście użycia. W tabeli 17.1 występują dwa błędy związane z zupełnością danych. Po pierwsze, baza zawiera dane adresowe tylko czternastu urzędów wojewódzkich, natomiast województw jest szesnaście – brakuje adresów urzędów dla województw wielkopolskiego i zachodniopomorskiego. Po drugie, w adresie urzędu dla województwa śląskiego (id 12) brakuje nazwy miasta. Jeśli spis, taki jak ten z tabeli 17.1, jest generowany automatycznie przez program na podstawie jakiegoś zewnętrznego źródła, np. bazy danych, fakt brakowania dwóch ostatnich elementów może być oznaką błędu w zapytaniu, które polega na wypisaniu tylko pierwszych czternastu rekordów. Brak miasta w wierszu nr 12 jest prawdopodobnie spowodowany fizycznym brakiem tej wartości w źródłowej bazie danych.

17.2.3. Spójność (consistency) Spójność danych to stopień, w jakim wartości poszczególnych danych są ze sobą niesprzeczne i spójne z innymi danymi w określonym kontekście użycia. Jeśli na

przykład baza danych osobowych kierowców zawiera pole rok urodzenia oraz rok wydania prawa jazdy, to błąd spójności wystąpiłby, gdyby np. data wydania prawa jazdy dla pewnego kierowcy była wcześniejsza niż data jego urodzenia. Inny przykład braku spójności możemy zaobserwować w danych z tabeli 17.1. Zauważmy, że adresy e-mailowe urzędów wojewódzkich pochodzą z bardzo różnych domen, a nawet w ramach jednej domeny (uw.gov.pl) mają różną strukturę. Jest to przykład złej jakości spójności danych. Wszystkie urzędy powinny mieć taką samą formę adresu, na przykład [departament]@uw. [miasto].gov.pl, gdzie [departament] jest kodem departamentu (np. wso = Wydział Spraw Osobowych), a [miasto] – nazwą miasta. spójność (ang. consistency) – stopień jednolitości, standaryzacji oraz brak sprzeczności między dokumentami oraz częściami modułu lub systemu [7] Spójność można także rozumieć jako wewnętrzną integralność (poprawność) danych. Na przykład, aby kontrolować, czy operacje wykonywane na danych nie zmieniły ich zawartości (np. przy przesyłaniu pakietów przez sieć) można stosować tzw. sumy kontrolne (ang. checksum), będące kilku- lub kilkunastoznakowym skrótem dokumentu. Jeśli w zadanym tekście choć jeden bit zostanie zmieniony, to jego suma kontrolna również ulegnie zmianie, przez co bardzo łatwo wykryć uszkodzenie treści pliku lub innego obiektu zawierającego dane.

17.2.4. Wiarygodność (credibility) Wiarygodność danych to stopień, w jakim dane są uważane za odpowiadające prawdzie. Na przykład, dla danych z tabeli 17.1 atrybutowi wiarygodności mogą podlegać adresy urzędów. Użytkownicy tej bazy, np. internauci wyszukujący informacji o urzędzie wojewódzkim w sieci, natrafiając na stronę Ministerstwa Spraw Wewnętrznych z tymi danymi mogą być bardziej przekonani co do prawdziwości adresów urzędów, niż znajdując je na jakiejś innej witrynie, niebędącej w domenie gov.pl. Wiarygodność związana jest w szczególności z pojęciem autentyczności, to znaczy zaufaniem do źródła informacji. Przykład z MSW dotyczy właśnie autentyczności – ludzie ufają instytucjom rządowym (przynajmniej, jeśli chodzi o prawdziwość danych na ich stronach www...).

17.2.5. Aktualność (currentness) Aktualność danych to stopień, w jakim dane mają poprawne atrybuty w określonym czasie. Na przykład przedstawiany na ekranie elektronicznym rozkład jazdy w systemie ELROJ (patrz Dodatek A) musi być uaktualniany tak, aby o danej godzinie były wyświetlane wszystkie kursy autobusów mających przyjechać w najbliższym czasie. Jeśli na ekranie są wyświetlane kursy z godzin 13:00–13:40 oraz 19:00–19:50, choć mogą być zupełnie poprawne i prawdziwe, nie będą zbyt użyteczne dla pasażerów czekających na przystanku o godzinie 13:45.

17.3. Charakterystyki inherentne i zależne od systemu 17.3.1. Dostępność (accessibility) Dostępność do danych to stopień, w jakim dane mogą być uzyskane przez użytkownika w określonym kontekście użycia. W szczególności dotyczy to osób mających różne formy niepełnosprawności. Przykładem złej dostępności do danych jest sytuacja, w której tekst jest prezentowany na czytniku w formie obrazka, przez co nie może zostać przeczytany przez urządzenie przekształcające tekst na głos, co jest wymagane dla aplikacji użytkowanych przez osoby niewidome. Realizacja dostępu może polegać również na możliwości prezentowania danych odpowiednio czytelnym krojem czcionki, różnymi rozmiarami (np. większe napisy dla osób niedowidzących) lub kolorami.

17.3.2. Zgodność (compliance) Zgodność to stopień, w jakim atrybuty danych spełniają określone standardy, regulacje lub przyjęte konwencje. Na przykład na oprogramowanie dla klientów banków może być nałożony wymóg, aby hasło do konta było przesyłane w formie zaszyfrowanej, zgodnie z określonym protokołem kryptograficznym. Innym przykładem może być regulacja występująca w Przepisach Lekkoatletycznych organizacji IAAF (Międzynarodowego Stowarzyszenia Federacji Lekkoatletycznych) określająca precyzję, z jaką ma być mierzony czas biegu. Na rysunku 17.1 przedstawiono fragment tych przepisów, będący przykładem specyfikacji wymagań dla programu mierzącego czas.

Rysunek 17.1. Przykład zgodności danych z zewnętrznymi regulacjami

17.3.3. Poufność (confidentiality) Poufność to stopień, w jakim dane są chronione przed niepowołanym dostępem. Charakterystyka ta wchodzi w skład obszaru bezpieczeństwa informacji (razem z dostępnością oraz integralnością) zdefiniowanego w normie ISO/IEC 13335 [168]. Jeśli firma używa programu komputerowego do obsługi płac, każdy pracownik powinien mieć w tym systemie możliwość wglądu do swoich danych (historia wynagrodzeń i tzw. paski z wynagrodzeniem), ale tylko upoważnione osoby mogą mieć dostęp do danych o pensjach wszystkich pracowników. Innym przykładem zapewniania poufności jest wykropkowanie wpisywanego hasła tak, aby żadna postronna osoba nie mogła odczytać z ekranu hasła, które jest właśnie wpisywane (patrz rys. 17.2).

Rysunek 17.2. Przykład poufności danych (hasło do poczty)

17.3.4. Wydajność (efficiency) Wydajność to stopień, w jakim dane mogą być przetwarzane z zachowaniem określonego poziomu efektywności przy określonym poziomie zasobów. Oto przykłady wysokiej wydajności danych, które pozwalają na lepsze gospodarowanie zasobami (miejsce na dysku) lub na efektywniejsze przetwarzanie danych: przyrostowe zapisywanie kolejnych wersji oprogramowania w systemach kontroli wersji (nowa wersja nie jest zapisywana w całości, ale tylko jako różnica między nią a wersją poprzednią);

kompresja archiwizowanych danych, które prawdopodobnie nie będą często wykorzystywane; stosowanie normalizacji w strukturze bazy danych, co zwiększa szybkość przetwarzania zapytań; stosowanie słowników dla często używanych wartości zmiennych (np. nazwy województw).

17.3.5. Precyzja (precision) Precyzja jest głównie związana z danymi liczbowymi i oznacza dokładność zapisu tych liczb, czyli to, ile miejsc po przecinku ma mieć reprezentacja danej wartości. Na przykład dane walutowe dla złotego polskiego czy euro powinny być zapisywane z precyzją do dwóch miejsc po przecinku (dokładność 1 grosza), choć czasami wymóg precyzji dla danych walutowych może być jeszcze większy (sic!). Można mianowicie wyobrazić sobie program naliczający koszt jakiegoś materiału w zależności od jego masy. Masa jest zmienną ciągłą, dlatego można wyobrazić sobie, że wartość tego towaru będzie liczbą o potencjalnie nieskończonym rozwinięciu dziesiętnym (np. jeśli 1 kg materiału kosztuje 100 PLN, to 1/3 kilograma to 33,333(3) PLN). W takiej sytuacji istotny może być sposób zaokrąglania tych wartości do rzeczywistych kwot walutowych (czy 33,3333 PLN to 33,33, czy też 33,34 PLN?), co może skutkować nałożeniem na system wymogu zapamiętywania tych wartości z większą precyzją niż 2 miejsca po przecinku. Dokładność jest podcharakterystyką odwrotnie proporcjonalną

do

efektywności. Im większej dokładności wymagamy, tym więcej potrzeba na to zasobów i być może czasu do przetwarzania tych danych.

17.3.6. Identyfikowalność (traceability) Identyfikowalność to stopień, w jakim jest możliwe śledzenie dostępu oraz zmian w danych. Odbywa się to zwykle za pomocą logów (ang. audit trial), w których jest zapisywane, kto i kiedy żądał dostępu do jakich danych, kto i kiedy wytworzył lub zmienił dany dokument itd.

Rysunek 17.3. Przykład identyfikowalności danych Na rysunku 17.3 przedstawiono zastosowanie identyfikowalności danych w praktyce. Jest to zrzut ekranu ze strony Uniwersytetu Jagiellońskiego zawierającej zarządzenia Rektora UJ. Przy każdym zarządzeniu podana jest informacja, kto i kiedy wytworzył dany dokument oraz kto go opublikował na stronie internetowej.

17.3.7. Zrozumiałość (understandability) Zrozumiałość danych to stopień, w jakim atrybuty danych pozwalają na właściwe odczytanie i interpretację tych danych przez użytkownika. Zrozumiałość może dotyczyć języka użytego do zapisu danych, odpowiedniej symboliki (np. piktogramy lub ikony programów) bądź jednostek (np. w Wielkiej Brytanii niektóre jednostki układu SI nie są w powszechnym użyciu). Zrozumiałość danych można zwiększyć, wykorzystując tzw. metadane, czyli „dane opisujące dane”. Prostym przykładem jest swoista metryczka pliku wytworzonego przez Rektora UJ, pokazana na rysunku 17.3, mówiąca, że rodzaj tego dokumentu to zarządzenie (a nie np. decyzja).

17.4. Charakterystyki zależne od systemu 17.4.1. Dostępność (availability) Dostępność określa stopień, w jakim dane mogą zostać uzyskane/odzyskane przez uprawnionych użytkowników. Dostępność jest szczególnie istotna w systemach umożliwiających równoległy dostęp do danych wielu użytkownikom – o ile dostęp w trybie odczytu w większości przypadków może być nadany wszystkim uprawnionym użytkownikom jednocześnie, o tyle dostęp do zapisu określonej danej powinna mieć jedna osoba w jednym czasie.

17.4.2. Przenaszalność (portability) Przenaszalność to właściwość pozwalająca na instalowanie, zamianę lub przenoszenie danych z jednego systemu na inny z zachowaniem ich poziomu jakości. Przykładem może być przeniesienie pliku arkusza kalkulacyjnego z programu MS Excel do programu Open Office Calc. Zły poziom przenaszalności może objawić się tym, że dane podczas tej migracji zostaną utracone, błędnie sformatowane, zmieni się precyzja danych liczbowych itp. Cecha ta jest zależna od systemu, gdyż dane same w sobie nie mogą gwarantować przenaszalności – proces ten zawsze wykonywany jest przez jakiś zewnętrzny wobec danych proces.

17.4.3. Odtwarzalność (recoverability) Odtwarzalność to stopień, w jakim dane są chronione przed utratą oraz możliwość ich odzyskania w przypadku awarii. Przykładowym mechanizmem zapewniającym odtwarzalność jest stosowanie redundancji (np. dyski RAID) czy też polecenie ROLLBACK w języku SQL, którego przykład, pochodzący z podręcznika języka PL/SQL [169] jest podany na listingu 17.1. W bloku BEGIN – END informacja o pracowniku o employee_id = 120 jest uaktualniana w trzech różnych tabelach: emp_name, emp_sal i emp_job. Jeśli jednak instrukcja INSERT usiłuje zapisać numer id już istniejący w bazie, to zostanie rzucony wyjątek DUP_VAL_ON_INDEX. W takim przypadku w bloku EXCEPTION instrukcja ROLLBACK spowoduje cofnięcie zmian dokonanych na wszystkich tabelach, pozwalając odtworzyć stan bazy sprzed awarii.

CREATE TABLE emp_name AS SELECT employee_id, last_name FROM employees; CREATE UNIQUE INDEX empname_ix ON emp_name (employee_id); CREATE TABLE emp_sal AS SELECT employee_id, salary FROM employees; CREATE UNIQUE INDEX empsal_ix ON emp_sal (employee_id); CREATE TABLE emp_job AS SELECT employee_id, job_id FROM employees; CREATE UNIQUE INDEX empjobid_ix ON emp_job (employee_id); DECLARE emp_id emp_lastname emp_salary emp_jobid

NUMBER(6); VARCHAR2(25); NUMBER(8,2); VARCHAR2(10);

BEGIN SELECT employee_id, last_name, salary, job_id INTO emp_id, emp_lastname, emp_salary, emp_jobid FROM employees WHERE employee_id = 120; INSERT INTO emp_name VALUES (emp_id, emp_lastname); INSERT INTO emp_sal VALUES (emp_id, emp_salary); INSERT INTO emp_job VALUES (emp_id, emp_jobid); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN ROLLBACK; DBMS_OUTPUT.PUT_LINE('Inserts have been rolled back'); END; / Listing 17.1. Skrypt PL/SQL z poleceniem ROLLBACK

1

Jest to rzeczywisty błąd, występujący na stronie MSW www.msw.gov.pl/pl/sprawy-obywatelskie/ewidencja-ludnosci-dowo/wydzialewidencji-ludn/4519,dok.html (dostęp 03.05.2014). Na stronie tej występuje więcej błędów, np. polskie znaki diakrytyczne w adresach mailowych czy brak odstępu między nazwą miasta a adresem mailowym (w przypadku Gdańska).

Część IV Zarządzanie testowaniem

In preparing for battle, I have always found that plans are useless but planning is indispensable Dwight D. Eisenhower

W tej części książki zajmiemy się zagadnieniami dotyczącymi jednej konkretnej roli w testowaniu: kierownika testów. Zarządzanie nie jest czynnością przesadnie twórczą, niektórzy uważają nawet, że jest zajęciem dosyć nudnym. Niezależnie od takich opinii, zarządzanie testowaniem – zwłaszcza w dużych, skomplikowanych projektach – jest koniecznością. Zarządzanie to radzenie sobie ze złożonością problemu. Dobry kierownik testów powinien cechować się wieloma umiejętnościami: powinien być dobrym strategiem i taktykiem, powinien umieć rozmawiać z ludźmi, wywierać wpływ, negocjować, planować, przewidywać, kontrolować, monitorować i podejmować trafne decyzje. Kierownik testów jest osobą pośredniczącą między zespołem testerów a kierownikiem projektu czy kierownictwem wyższego szczebla. W rozdziale 18 opiszemy, jak wygląda zarządzanie testowaniem w kontekście różnych uwarunkowań i ograniczeń projektowych. Omówimy również kwestie dotyczące różnic między zarządzaniem a przywództwem. Rozdział 19 jest poświęcony zarządzaniu opartemu na ryzyku, czyli najczęściej wykorzystywanemu podejściu analitycznemu do zarządzania. W rozdziale tym omówimy dokładnie fazy procesu zarządzania ryzykiem oraz techniki oparte na ryzyku, takie jak PRAM, SST, PRisMa, analiza zagrożeń, Cost of Exposure, FMEA, Quality Function Deployment, metoda drzewa usterek, metodologia TMap oraz TestGoal. W rozdziale 20 opiszemy pozostałe strategie testowania: oparte na wymaganiach, na modelu, metodyczne, oparte na standardzie, reaktywne, „good enough” i konsultacyjne. Rozdział 21 jest poświęcony dokumentacji związanej z testowaniem. Opiszemy najczęściej używane typy dokumentów wraz z konkretnymi przykładami ich użycia. Rozdział 22 dotyczy szacowania kosztu oraz pracochłonności testów. Przedstawimy typowe techniki szacowania oraz omówimy kwestię negocjacji zakresu testów. W rozdziale 23 omówimy czynności związane z nadzorem i kontrolą postępu testów. Wprowadzimy przykłady metryk, które pomagają kierownikowi testów w ocenie postępów prac zespołu testowego. Osobno opiszemy kwestię nadzoru i kontroli w przypadku testowania eksploracyjnego, które na pierwszy rzut oka trudno poddaje się procesowi monitorowania.

Rozdział 24 dotyczy biznesowej wartości testowania, czyli oceny zysków, jakie możemy uzyskać, stosując proces testowy w organizacji. Pokażemy, w jaki sposób, budując

odpowiednie

business-case’y,

można

przekonywać

kierownictwo

do

wspierania testowania i zwiększania budżetu na prace testowe. W rozdziale 25 omówimy zagadnienia związane z testowaniem przeprowadzanym poza naszym zespołem testowym: rozproszone, zakontraktowane i zewnętrzne. Przedstawimy zalety i wady tych podejść oraz sposoby zarządzania nimi. Krótki rozdział 26 dotyczy kwestii wdrażania standardów przemysłowych i norm do procesu testowego. Na zakończenie, w rozdziale 27 opiszemy, jak zarządzać incydentami, jak je śledzić i mierzyć, oraz co z tych informacji może uzyskać kierownik testów. Omówimy również kwestię profesjonalnego komunikowania incydentów.

18. Zarządzanie testowaniem w kontekście

Zarządzanie polega na nadzorowaniu wykonania procesu, który w zamyśle ma wnieść jakąś wartość dodaną do projektu czy dla organizacji. Testowanie jest takim procesem, ponieważ pozwala ocenić jakość wytwarzanego oprogramowania oraz wskazać jego słabe strony (defekty), które można naprawić, tym samym podnosząc jakość końcowego produktu oraz minimalizując ryzyko. Menedżer testów (zwany także kierownikiem testów) zajmuje się właśnie tym procesem. Testowanie nigdy nie odbywa się w próżni – jest zwykle częścią większego procesu, mianowicie procesu tworzenia oprogramowania. Z jednej strony proces testowy sam w sobie jest na tyle skomplikowany, że wymaga zarządzania. Z drugiej, ponieważ jest on składową innego procesu, występuje wiele czynników, warunków i ograniczeń, do których należy go dostosować. Kierownik testów jest więc odpowiedzialny nie tylko za wybór i implementację odpowiedniej strategii testowania, lecz także musi rozpoznać i zdefiniować wszystkich interesariuszy procesu testowego i zarządzać ich wymaganiami wobec testowania czy ogólnie jakości wytwarzanego oprogramowania. Jeśli w organizacji zdefiniowana jest strategia testowa lub inne wysokopoziomowe dokumenty określające standardy panujące w organizacji, to kierownik testów musi zapewnić, aby proces testowy był zgodny z tymi dokumentami. podejście do testu (ang. test approach) – implementacja strategii testów dla konkretnego projektu; zwykle zawiera decyzje podjęte na podstawie celów i analizy ryzyka projektu (testowego), punkty startowe procesu testowego, techniki projektowania testów, kryteria wyjścia i typy testów do wykonania

Wreszcie, proces testowy sam w sobie może być traktowany jako projekt, zatem podlega klasycznym ograniczeniom przedstawionym na rysunku 18.1: czasowi, budżetowi oraz zakresowi.

18.1. Kontekst ograniczeń projektowych Projekt testowy polega na wykonaniu wielu czynności, które w sumie dają określoną jakość, reprezentowaną polem trójkąta z rysunku 18.1. Długości boków trójkąta reprezentują ograniczenia projektowe. Trójkąt można zmieniać, manipulując długościami boków, jednak tak, aby jego pole nie przekroczyło określonej wartości. Rysunek ten można równoważnie przedstawić w postaci równania: ZAKRES ⋅ BUDŻET ⋅ CZAS = const. Kierownik testów nigdy nie ma nieograniczonych zasobów. Testowanie musi zakończyć się w określonym czasie i zamknąć w określonym budżecie. Nie możemy dowolnie manipulować wszystkimi trzema parametrami naraz – mając dwa z nich, trzeci jest dany, wynika z zadanych pozostałych dwóch ograniczeń. Na przykład, przy ustalonym budżecie i czasie jesteśmy w stanie wykonać określony zakres projektu (nie więcej). Mając zadany zakres i budżet, musimy poświęcić określoną ilość czasu (nie mniej) itd.

Rysunek 18.1. Klasyczny trójkąt ograniczeń projektowych Interesariusze, np. menedżerowie wyższego szczebla, często nie rozumieją istoty tych ograniczeń i żądają wykonania określonego zakresu testowania przy zadanym czasie i budżecie, co zwykle jest niewykonalne. W takiej sytuacji kierownik testów musi negocjować zmianę przynajmniej jednego z parametrów: zwiększenie budżetu, czasu lub zmniejszenie jakości. W negocjacjach tych są bardzo przydatne dane historyczne z poprzednich projektów, ponieważ wiadomo wtedy, jaka jest produktywność zespołu testowego, ile przeciętnie kosztuje stworzenie jednego testu i ile czasu to zajmuje.

18.2. Kontekst interesariuszy procesu testowego W procesie testowym interesariuszem jest każdy zainteresowany wewnętrzną czy też końcową jakością produktu (tzw. jakością użytkową). Kierownik testów musi zidentyfikować wszystkich interesariuszy procesu testowego oraz poznać ich oczekiwania i wymagania wobec tego procesu, a następnie sprawnie nimi zarządzać. Przykładowymi interesariuszami mogą być: właściciel projektu – jest to organizacja lub osoba, która finansuje projekt; często właścicielem jest po prostu klient, a w przypadku projektów wewnętrznych – własna organizacja; klient bezpośredni – czyli osoba lub organizacja zamawiająca program lub system podlegający testom; klient pośredni – w przypadku produktów z półki (COTS) klientem nie jest konkretna osoba, lecz zbiór osób będących potencjalnymi nabywcami i użytkownikami testowanego oprogramowania; testerzy – ponieważ to oni projektują i wykonują testy; outsorcingowe zespoły testerów – w przypadku, gdy organizacja zleca wykonanie całości lub części testów podmiotom zewnętrznym; kierownik projektu – odpowiada za całość projektu i jego powodzenie, a więc w szczególności za podproces testowy; analitycy biznesowi – czyli osoby tłumaczące wymagania klienta z języka biznesowego na język techniczny; wymagania powinny być testowalne i powinny temu testowaniu podlegać; projektanci – ich zaangażowanie w proces testowy polega na tym, że powinni dbać o testowalność kodu pisanego na podstawie ich projektów; deweloperzy – którzy sami często stosują testy jednostkowe, a ponadto wspólnie z zespołem testerów biorą udział w procesie zarządzania i usuwania defektów; wsparcie techniczne – czyli osoby wspierające użytkowników; muszą oni być świadomi, jakie błędy pozostały w wydanym oprogramowaniu i jak można je obejść lub załagodzić w przypadku ich wystąpienia. Lista ta nie wyczerpuje wszystkich możliwych interesariuszy. Niezależnie od tego kierownik testów musi zidentyfikować wszystkie osoby zainteresowane

procesem testowym, dobrze zrozumieć rolę każdego z nich oraz poznać jego wymagania w stosunku do procesu testowego i samego produktu. Struktura zależności między poszczególnymi rolami w projekcie może być bardzo skomplikowana (patrz rys. 18.2). Ponadto, kierownik testów musi określić: z którymi osobami, w jaki sposób i jak często się komunikować; co raportować, którym interesariuszom oraz na jakim poziomie abstrakcji; do kogo zwracać się w przypadku wystąpienia różnego rodzaju problemów (niektóre z nich mogą wymagać interwencji kierownika projektu, a inne można rozwiązać bezpośrednio z klientem). Bardzo ważną rolą kierownika testów jest wyważenie wszystkich oczekiwań interesariuszy wobec projektu testowego, często bowiem zdarza się, że wymagania te są sprzeczne i niemożliwe do zrealizowania w stu procentach. Często kierownik testów musi wykazać się umiejętnościami miękkimi w negocjacjach lub przekonywaniu interesariuszy, że niektóre z ich oczekiwań czy wymagań są niedobre lub wręcz niebezpieczne dla projektu. Na przykład wielu menedżerów ma pokusę, aby oceniać efektywność zespołu testowego przez użycie metryki liczby testów lub wykrytych defektów, co prowadzi do tworzenia wielu słabych, nieistotnych i redundantnych testów bądź do znajdowania nieistotnych błędów. Kierownik testów musi potrafić edukować interesariuszy i uświadamiać im istniejące ograniczenia oraz zagrożenia. Często wymaga to przedstawienia analizy ryzyka oraz zaproponowania jej jako podstawy dalszych działań.

Rysunek 18.2. Skomplikowana struktura zależności między rolami w projekcie

18.3. Kontekst produkcji oprogramowania Testowanie dotyczy programu lub systemu produkowanego poza procesem testowym, dlatego kierownik testów musi zaplanować i przeprowadzać wszystkie czynności testowe tak, aby były zgodne z nadrzędnym procesem wytwórczym. Należy pamiętać, że istnieje obustronna interakcja między tymi dwoma procesami: proces testowy wpływa na proces produkcji – np. przez wymuszanie wprowadzenia zmian po wykryciu defektu, wymuszenie określonego standardu implementacji zapewniającego testowalność kodu; proces produkcji wpływa na proces testowy – np. jeśli organizacja używa metodyki zwinnej wytwarzania oprogramowania, to kierownik testów powinien ustalić z kierownikiem deweloperów, w jaki sposób

testerzy powinni uczestniczyć w tym procesie (np. mając dostęp do testów jednostkowych mogą sugerować, jak je poprawić, aby uzyskać lepsze pokrycie lub bardziej zminimalizować istniejące ryzyko oraz mogą wykorzystać tę wiedzę o testach jednostkowych przy produkcji swoich testów, nie powielając tego, co już zostało przetestowane; ponadto, metodyka zwinna często jest związana z ciągłą integracją, co może powodować problemy z wykonywaniem testów regresji, jeśli jest ich bardzo dużo; szybko i często zmieniające się wymagania w procesie zwinnym stawiają pod znakiem zapytania sens automatyzacji testów itd.). Kierownik projektu powinien uczestniczyć (lub delegować testerów ze swojego zespołu) właściwie we wszystkich czynnościach procesu produkcji: w fazie analizy wymagań – aby pomóc w zapewnieniu testowalności wymagań oraz móc możliwie jak najwcześniej opracować plan testów akceptacyjnych i systemowych; w fazie projektowania architektury – aby wcześnie wykrywać błędy projektowe, zapewnić testowalność kodu tworzonego na podstawie projektu oraz inne cechy jakościowe testowanego programu, takie jak utrzymywalność czy przenaszalność; w fazie kodowania – aby skorelować wydań, wspomagać deweloperów przy uczestniczyć wspólnie z zespołem zarządzania defektami (defekty są

czynności testowe z datami testach jednostkowych oraz programistów w procesie zwykle poprawiane przez

deweloperów, dlatego musi istnieć dobra i profesjonalna komunikacja między nimi a testerami); w zarządzaniu projektem – ponieważ testowanie jest częścią projektu, kierownik testów raportuje do kierownika projektu, informując go o postępach, ryzykach i szacowaniach dotyczących samego procesu, a także jakości tworzonego oprogramowania; kierownik projektu wspólnie z kierownikiem testów powinni opracować także harmonogram czynności testowych oraz budżet zespołu testerów; w zarządzaniu konfiguracją, wydaniami i zmianą – kierownik testów lub delegowany przez niego przedstawiciel powinien uczestniczyć w pracach rady kontroli zmian (ang. Change Control Board, CCB),

w szczególności podczas szacowania w analizie wpływu, jak określona zmiana w wymaganiach lub konfiguracji wpłynie na poziom ryzyka oraz zakres testów regresji; w procesie dokumentowania – aby ustalić harmonogram i metody testowania dokumentacji oraz wspólnie z dokumentalistami zarządzać defektami znalezionymi w dokumentacji; w procesie wsparcia technicznego – aby dostarczyć zespołowi wsparcia technicznego odpowiednie produkty testowe (raporty itp.) oraz informację na temat tego, jakie błędy pozostały w wydanym systemie oraz jak można sobie z nimi radzić, jeśli przytrafią się klientowi; kierownik testów razem z kierownikiem wsparcia powinien przeanalizować błędy polowe, aby właściwie zaimplementować poprawę procesu testowego; kierownik testów może również (zwykle za pomocą działu zapewniania jakości) wykorzystać modele jakości oprogramowania i przewidywać liczbę błędów polowych oraz ryzyko, jakie niosą ze sobą, co jest podstawą do szacowania ilości zasobów w dziale wsparcia technicznego.

18.4. Kontekst cyklu życia oprogramowania Testowanie w cyklu życia omówiliśmy dokładnie w rozdziale 4. Z punktu widzenia kierownika testów istotne jest to, aby wiedział on, jakie czynności testowe oraz kiedy wykonać, mając narzucony określony cykl życia oprogramowania. Proces testowy powinien być dostosowany do przyjętego cyklu życia. Wybór metodyki wytwarzania oprogramowania może istotnie wpłynąć na poziom istotności różnych czynności testowych. Na przykład w metodykach zwinnych zazwyczaj nacisk na dokumentację jest o wiele mniejszy niż w modelach sekwencyjnych. Informacje o tym, jak uzgodnić testowanie z cyklem życia mogą być zawarte w strategii testów. Wtedy kierownik testów musi dostosować swój projekt do tych wymogów, chyba że z jakichś względów będą konieczne odstępstwa od strategii (w takim przypadku w planie testów należy wyraźnie to zaznaczyć wraz z uzasadnieniem odstępstwa). W szczególności strategia może określać poziomy testów. Jeśli ich nie określa, kierownik testów musi sam je zdefiniować w planie testów. Przyjęty cykl życia może wymuszać inne niż cztery standardowe poziomy

testów omówione w podrozdziale 4.3, np. testy integracji software–hardware czy testy integracji systemowej.

18.5. Kontekst testów Przyjęcie określonego typu testów rzutuje na kwestie zarządzania testami. Na przykład testy niefunkcjonalne są często drogie, a ponadto występuje bardzo dużo ich typów. Kierownik testów musi zdecydować, które będą odpowiednie dla danego projektu i które wykonać najpierw (priorytetyzacja). Ta decyzja musi oczywiście być zgodna ze strategią testów oraz zgodą odpowiednich interesariuszy. Testy niefunkcjonalne zwykle wykonuje się przy użyciu narzędzi, więc trzeba zaplanować zakup narzędzia, dokonać wyboru, skalkulować opłacalność wykorzystania narzędzia itd. (patrz rozdz. 30). Kierownik testów powinien pamiętać, że testy niefunkcjonalne nie muszą być wykonywane dopiero po testach funkcjonalnych. Wręcz przeciwnie – wiele testów niefunkcjonalnych można zacząć wcześniej i dzięki temu wcześniej wykryć poważne błędy. Priorytetyzacja powinna wynikać z analizy ryzyka (np. poziom użyteczności dla prototypu GUI albo wydajność systemu, który jeszcze nie ma GUI, ale z którym można już się komunikować). W przypadku testowania opartego na doświadczeniu kierownik testów musi mieć świadomość, że ten typ testów nie daje się łatwo dokumentować ani replikować. Decydując się na przeprowadzenie testowania opartego na doświadczeniu, należy pozostawić wolną rękę testerowi, ale należy dokładnie zaplanować czas trwania sesji. Istnieją natomiast metody zarządzania tego typu testami w zakresie nadzoru i kontroli, omówione w podrozdziale 23.3. Jeszcze jednym

problemem, przed jakim

staje kierownik

testów, jest

zarządzanie procesem testowym zleconym na zewnątrz (outsourcing). Kwestie te omówimy w rozdziale 25.

18.6. Kontekst czynnika ludzkiego Mówi się, że największym kapitałem organizacji są zatrudnieni w niej ludzie. Paradoksalnie, również ludzie stanowią największe źródło kłopotów i problemów na drodze do sukcesu. Kierownik testów musi zarządzać nie tylko procesem testowym, lecz także zespołem złożonym z różnych ludzi. Ich doświadczenie,

postawa, charakter oraz umiejętności wpływają znacząco na przyjmowanie przez kierownika testów odpowiednich metod zarządzania, kierowania i wspierania zespołu oraz wyboru metod pracy grupowej. Kwestie związane z zarządzaniem zasobami ludzkimi omówimy w rozdziale 28.

19. Testowanie oparte na ryzyku

W idealnym projekcie wszystko jest doskonale zaplanowane, a prace projektowe podążają dokładnie za planem. Wszystko się udaje, wszystko jest zrobione na czas i kosztuje dokładnie tyle, ile miało kosztować. Testowanie wykrywa wszystkie defekty tkwiące w oprogramowaniu, dzięki czemu klient otrzymuje produkt całkowicie wolny od błędów. Brzmi wspaniale? Niestety, nie ma projektów idealnych – w każdym występuje mnóstwo nieprzewidzianych zdarzeń, które negatywnie wpływają zarówno na postęp projektu, jak i na jakość produktu. Te wszystkie negatywne zdarzenia, które mogą, ale nie muszą wystąpić to ryzyka. Naszym celem zwykle jest dążenie do ideału, który opisaliśmy w poprzednim akapicie, dlatego naturalnym podejściem w zarządzaniu testowaniem jest testowanie oparte na ryzyku. Technika ta polega na identyfikacji możliwych ryzyk, szacowaniu ich wpływu na projekt, opracowaniu planów redukujących ryzyka oraz tzw. planów B, czyli gotowych do przeprowadzenia działań na wypadek rzeczywistego wystąpienia tych ryzyk. Wszystkie te czynności opiszemy w kolejnych rozdziałach. testowanie oparte na ryzyku (ang. risk based testing) – testowanie nastawione na wykrycie i dostarczenie informacji o ryzykach produktowych [59] Testowanie oparte na ryzyku jest jedną z najpowszechniejszych i ogólnie uznanych analitycznych technik zarządzania testowaniem. Dobrze przeprowadzone minimalizuje prawdopodobieństwo porażki projektu i pozwala na uzyskanie wysokiej jakości produktu końcowego. testowanie analityczne (ang. analytical testing) – testowanie oparte na systematycznej analizie np. ryzyk produktowych lub wymagań

19.1. Czym jest ryzyko? Ryzyko jest możliwością wystąpienia niepożądanego zdarzenia. Jego pojawienie się ma negatywny wpływ na produkt bądź proces. Nieodłączną cechą ryzyka jest jego probabilistyczny charakter, tzn. ryzyko może, ale nie musi wystąpić. W tej niepewności tkwi natura ryzyka i cały problem z nim związany. Gdybyśmy bowiem wiedzieli z góry, kiedy jakieś ryzyko wystąpi, moglibyśmy bardzo dokładnie przygotować się na jego nadejście. Jednocześnie, gdybyśmy wiedzieli z całą pewnością, że dane ryzyko w ogóle nie wystąpi, nie musielibyśmy się nim przejmować i moglibyśmy je zignorować. Niestety, ponieważ nie potrafimy przewidywać przyszłości, możemy jedynie szacować szanse wystąpienia danego ryzyka i tworzyć plany na wypadek jego wystąpienia. Nigdy jednak nie będziemy wiedzieli, czy plany te trzeba będzie wdrożyć w życie. Wystąpienie ryzyka może wpływać pośrednio lub bezpośrednio na postrzeganie przez klienta, użytkownika czy interesariusza jakości produktu lub projektu. Ponieważ, jak już wspomnieliśmy, bardzo trudno dokładnie oszacować prawdopodobieństwo wystąpienia ryzyka, a jeszcze trudniej wpływ jego negatywnych konsekwencji, ryzyko zwykle określane jest subiektywnie. Bardzo trudno ująć je w dokładne miary ilościowe, choć nie jest to niemożliwe – techniki szacowania ryzyka zaprezentujemy w podrozdziale 19.6. W

wielu

projektach

(zwłaszcza

wykorzystujących

zwinne

metodyki

wytwarzania) używa się jakościowego podejścia do ryzyka, tzn. nie określa się go liczbowo, ale w skali porządkowej (np.: znikome, małe, średnie, duże, bardzo duże). Choć skala porządkowa jest mniej dokładna od skali „liczbowej”, wykorzystanie tej pierwszej, zwłaszcza w tak trudnym zadaniu jak szacowanie ryzyka, nie tylko nie jest błędem, lecz także przez wielu jest wręcz zalecane. Skoro bowiem nie da się dokładnie (liczbowo) określić prawdopodobieństwa czy wpływu ryzyka, to lepiej operować na bardziej zgrubnych miarach, które przynajmniej pozwolą nam uszeregować ryzyka od tych najważniejszych po najmniej istotne. Skala porządkowa, stosowana w analizie jakościowej, umożliwia określanie kolejności oraz porównywanie ze sobą elementów tej skali. Więcej informacji o skalach pomiarowych Czytelnik znajdzie w podrozdziale 39.2. Istnieje wiele definicji ryzyka. Poniżej definicja stosowana przez ISTQB [6]: ryzyko (ang. risk) – możliwość wystąpienia czynnika powodującego negatywne konsekwencje, wyrażana przez prawdopodobieństwo wystąpienia oraz

potencjalny wpływ na produkt lub projekt Inny przykład to definicja pochodząca z raportu Heemstry i in. [170]: ryzyko – prawdopodobieństwo wystąpienia odchylenia od zamierzonego rezultatu Niezależnie od definicji ryzyka, zawsze musi zawierać ona w sobie element niepewności („możliwość” wystąpienia w definicji ISTQB czy „prawdopodobieństwo” wystąpienia w definicji Heemstry). wpływ ryzyka (ang. risk impact) – szkoda, jaka powstanie, jeżeli ryzyko zmaterializuje się jako rzeczywisty skutek lub zdarzenie prawdopodobieństwo ryzyka (ang. risk likelihood) – oszacowane prawdopodobieństwo, że ryzyko wystąpi jako wynik rzeczywisty lub zdarzenie Ryzyko ryzyku nierówne – jedne cechują się większym prawdopodobieństwem wystąpienia, inne mniejszym. Wystąpienie jednych może mieć duży wpływ na system, a innych – znikomy. Prawdopodobieństwo oraz wpływ są od siebie niezależne, tzn. nie istnieje zależność mówiąca, że np. im większe prawdopodobieństwo wystąpienia, tym większy wpływ ryzyka na system. Ryzyko może być bardzo prawdopodobne, ale o znikomym wpływie. Tak samo mogą istnieć ryzyka o bardzo niskim prawdopodobieństwie wystąpienia, ale o wpływie katastrofalnym.

Rysunek 19.1. Prawdopodobieństwo ryzyka a jego wpływ Sytuację tę zilustrowano na rysunku 19.1. Mamy tu przedstawionych sześć ryzyk. Ryzyko A cechuje się zarówno niskim prawdopodobieństwem wystąpienia, jak i znikomym wpływem. Tego typu ryzykami można zająć się w ostatniej kolejności lub nawet je zignorować. Ryzyko B, choć mało prawdopodobne, niesie ze sobą bardzo negatywny wpływ. Takim ryzykom należy się przyglądać. Tym bardziej należy zająć się ryzykiem F, które nie dość, że ma katastrofalny wpływ, to jest bardzo duża szansa, że się urzeczywistni. Ryzyko E jest przkładem ryzyka „uciążliwego” – nie powoduje wielkich strat czy kosztów, ale występuje bardzo często. Ryzyko C charakteryzuje się średnim prawdopodobieństwem wystąpienia i takim samym poziomem wpływu. Jest jednak mniej ważne od ryzyka D, które –

w stosunku do C – ma zarówno nieco większe prawdopodobieństwo, jak i nieco większy wpływ. Na podstawie tej analizy moglibyśmy stworzyć priorytetyzację: na pewno najwyższy priorytet ma ryzyko F, następnie grupa ryzyk B, C i D, potem E i na końcu A. Poziom ryzyka określa się zazwyczaj jako iloczyn prawdopodobieństwa wystąpienia ryzyka oraz wpływu, który nastąpi w przypadku zaistnienia ryzyka. Prawdopodobieństwo jest wielkością bezwymiarową, zatem poziom ryzyka jest wyrażony w jednostkach wpływu. W podejściu ilościowym wpływ wyraża się zwykle w kwotach pieniężnych. Wtedy poziom ryzyka opisuje potencjalną stratę finansową, jaka może nastąpić w przypadku urzeczywistnienia się ryzyk. W przypadku podejścia jakościowego poziom ryzyka jest określany na skali porządkowej. Ilościowe ujęcie ryzyk z rysunku 19.1 jest przedstawione w tabeli 19.1. Każde z ryzyk ma przypisane prawdopodobieństwo oraz wielkość wpływu, a poziom ryzyka to iloczyn tych wartości. Możemy zatem dokonać priorytetyzacji według poziomu poszczególnych ryzyk. Kolejność najważniejszych do najmniej istotnych) będzie następująca: F, D, B, C, E, A.

(od

poziom ryzyka (ang. risk level) – określenie istotności ryzyka zdefiniowane przez jego właściwości: wpływ i prawdopodobieństwo; poziom ryzyka może być użyty do określenia poziomu rodzaju bądź dokładności testów, które należy przeprowadzić Tabela 19.1. Ilościowe szacowanie ryzyka

W pływ Ryzyko Prawdopodobieństwo (w tys. USD)

Poziom ryzyka (w tys. Priorytet USD)

A

0.1

1 200

120

6

B

0.2

5 300

1 060

3

C

0.4

2 500

1 000

4

D

0.6

4 800

2 880

2

E

0.8

1 000

800

5

F

0.79

5 100

4 029

1

Właścicielem ryzyka produktowego jest zawsze biznes, nie zespół testowy. To interesariusze biznesowi są najbardziej zainteresowani tym, aby poziom ryzyka w produkcie był możliwie zminimalizowany. Osoby te są odpowiedzialne za podjęcie decyzji o tym, kiedy produkt jest wypuszczany na rynek lub do klienta, a zatem decydują o tym, że aktualny poziom ryzyka jest dopuszczalny. Informacje o tym poziomie dostają oni od zespołu zapewniania jakości lub zespołu testerów. Testerzy często popełniają błąd, wchodząc w rolę interesariusza biznesowego i decydując o poziomie akceptacji ryzyka produktowego. Należy pamiętać, że poziom ten zawsze powinien być określony przez właściciela projektu lub klienta, czyli interesariusza biznesowego. W tej kwestii zadaniem testera jest jedynie dostarczanie obiektywnych informacji o poziomie ryzyka. Tester nie powinien być w tym względzie osobą decyzyjną, natomiast czynności, które tester wykonuje w procesie testowym dopuszczalnego ryzyka.

powinny

uwzględniać

wspomniany

wyżej poziom

19.2. Zalety testowania opartego na ryzyku W podejściu opartym na ryzyku wszystkie czynności związane z testowaniem: planowanie, projektowanie, wykonanie oraz monitorowanie są odnoszone do poziomu ryzyka. Celem testowania staje się minimalizacja ryzyka rezydualnego (pozostałego w produkcie). Ten cel pośrednio wpływa na zwiększenie jakości końcowego produktu, ponieważ mniejsza liczba pozostałych ryzyk oznacza mniejsze prawdopodobieństwo awarii systemu. Testowanie oparte na ryzyku koncentruje się na pytaniu „co może pójść źle, jeśli nastąpi awaria?”. Takie podejście wymusza konieczność ustalenia możliwych ryzyk, to znaczy możliwych rodzajów awarii oraz przeanalizowanie wpływu ich pojawienia się – czynności te omówimy dokładnie w podrozdziałach 19.5 oraz 19.6. Następnie testowanie weryfikuje, czy poszczególne ryzyka rzeczywiście istnieją w systemie, czy też nie. Test zdany oznacza, że ryzyko związane z tym testem nie istnieje lub szansa jego pojawienia się jest mniejsza. Im więcej zdanych testów pokrywa coraz większy obszar ryzyk, tym bardziej wzrasta nasze przekonanie o tym, że te ryzyka nie stanowią zagrożenia.

Jedną z głównych zalet testowania opartego na ryzyku jest możliwość priorytetyzacji ryzyk, co pozwala zająć się w pierwszej kolejności tymi najpoważniejszymi. Takie podejście optymalizuje naszą pracę pod kątem maksymalizacji zapewnianej jakości. Bardzo często w projektach zostaje niewiele czasu na testowanie lub proces testowy jest zakańczany wcześniej ze względu na brak środków (tzw. squeeze on testing). W takich przypadkach, stosując priorytetyzację, efektywnie wykorzystujemy dany nam czas na testowanie. W pierwszej kolejności testujemy bowiem obszary o największym ryzyku. Co prawda nie przetestowaliśmy wszystkiego, co chcieliśmy, ale przynajmniej przetestowaliśmy najważniejsze, kluczowe aspekty produktu pod kątem wystąpienia najpoważniejszych zagrożeń. testowanie oparte na ryzyku (ang. risk based testing) – podejście do testowania mające na celu minimalizację ryzyka rezydualnego (pozostałego w produkcie) oraz informowanie interesariuszy o statusie wszystkich ryzyk, od momentu rozpoczęcia projektu do jego zakończenia; podejście to wymaga identyfikacji ryzyk oraz określenia poziomu każdego z nich w celu priorytetyzacji i właściwego ukierunkowania procesu testowego Testowanie oparte na ryzyku jest bardzo podobne do koncepcji testowania wystarczającego (ang. good enough testing) opisanej w punkcie 20.5.2. Metodologia ta opisuje sposób testowania w sytuacji, gdy osiągnięcie „idealnego” rozwiązania jest niemożliwe. Skoro cel uzyskania zero defektów jest nieosiągalny, jest nieunikniony pewien kompromis między poziomem jakości, wysiłkiem włożonym w testowanie i ograniczeniami projektowymi. Testowanie oparte na ryzyku wpasowuje się w strategię „good enough” w co najmniej trzech aspektach, odpowiadając na następujące pytania: czy wykonane testy wykazały, że dostarczona funkcjonalność jest poprawna i przynajmniej w minimalnym stopniu spełnia wymagania użytkownika? czy w systemie pozostały krytyczne defekty? Takie defekty należy usunąć przed wydaniem i wykazać – przez odpowiedni zbiór zdanych testów – że w systemie nie pozostał żaden znany defekt o znaczeniu krytycznym

czy mamy wystarczającą informację do podjęcia decyzji o wydaniu oprogramowania? Te trzy pytania dotyczą w gruncie rzeczy wyważenia między wystarczającą (good enough!) jakością, z akceptowalnym poziomem ryzyka, a ilością zużytych na ten cel zasobów [171]. Wyważenie to jest zilustrowane na rysunku 19.2. Zwykle jakości asymptotycznie zbiega do pewnej ustalonej wartości

przyrost

i zwiększanie zużycia zasobów w coraz mniejszym stopniu wpływa na poprawę tej jakości. Na przykład, zwiększenie zużycia zasobów z wielkości A do B = A + δZ daje wzrost jakości równy ∆Q1. Jednak takie samo zwiększenie zużycia zasobów dla większych wartości, tzn. z C do D = C + δZ daje wzrost jakości jedynie o ∆Q2, które jest dużo mniejsze niż ∆Q1. Generalnie, wraz ze wzrostem nakładów, aby zwiększyć poziom jakości o stałą wartość trzeba zużywać coraz więcej zasobów. Oczywiście po określonym czasie przestanie się to opłacać. Jeśli chcielibyśmy osiągnąć maksymalny poziom jakości, to musielibyśmy zużyć bardzo dużą ilość zasobów. Jednak po co to robić, skoro o wiele mniejszymi nakładami (np. na poziomie D z rys. 19.2) możemy osiągnąć prawie maksymalny poziom jakości? Celem jest więc wyważenie obu tych wartości, tzn. przyjęcie odpowiednio dużego, ale akceptowalnego poziomu zużycia, które da niekoniecznie maksymalny, ale za to akceptowalny poziom jakości.

Rysunek 19.2. Zależność między wielkością zużycia zasobów a osiąganym poziomem jakości W podejściu opartym na ryzyku testowanie uwzględnia ryzyko na trzy następujące sposoby [171]: testowanie ukierunkowane (ang. targeted testing), tzn. określanie poziomu wysiłku testowego, wybór technik testowania oraz retestowanie w sposób odpowiedni dla poziomu ryzyka związanego z danym, zidentyfikowanym ryzykiem produktowym (czyli testowanie rzeczy bardziej krytycznych powinno być dokładniejsze); priorytetyzacja testów (ang. prioritized testing), tzn. priorytetyzacja większych, ważniejszych ryzyk i testowanie obszarów z nimi związanych wcześniej niż obszarów obarczonych ryzykiem mniejszym; raportowanie wyników testów oraz statusu procesu testowego i jakości oprogramowania w terminach ryzyka rezydualnego, czyli ryzyka, które pozostaje w systemie z powodu nie wykonania odpowiednich testów pokrywających te ryzyka.

Wszystkie te trzy rodzaje radzenia sobie z ryzykiem powinny występować podczas całego cyklu wytwarzania oprogramowania, nie tylko na początku i końcu projektu (jak to często bywa w praktyce). Poziom danego ryzyka może zmieniać się w czasie. Zespół testowy powinien okresowo przeglądać listę ryzyk oraz uaktualniać ich parametry, gdyż zarówno prawdopodobieństwo wystąpienia, jak i wpływ danego ryzyka może się zmieniać na skutek przeprowadzenia określonych testów, odsetka zdanych testów, reewaluacji ryzyk itp. W trakcie prac wytwórczych mogą również pojawiać się nowe, nieznane dotąd ryzyka – w takich sytuacjach należy je dodać do listy ryzyk, oszacować ich prawdopodobieństwo i wpływ, oraz uaktualnić priorytetyzację uwzględniając te nowe zagrożenia. Stosując podejście oparte na ryzyku, należy pamiętać o tym, że testowanie wyłącznie na podstawie ryzyk zdefiniowanych w fazie identyfikacji ryzyka może spowodować, że przeoczymy jakiś istotny obszar i nie przetestujemy go lub zrobimy to niewystarczająco dokładnie. Dlatego zawsze należy pozostawić pewną swobodę zespołowi testowemu, który może np. zastosować testowanie eksploracyjne czy inne techniki oparte na doświadczeniu omówione w rozdziale 10.

19.3. Rodzaje ryzyka Ryzyka można podzielić na dwa główne rodzaje: produktowe (zwane też ryzykiem jakościowym); projektowe. Ryzyka produktowe to te zagrożenia, których urzeczywistnienie się ma negatywny wpływ przede wszystkim na wytwarzany produkt. Przykładem ryzyka produktowego może być defekt w oprogramowaniu, który powoduje zawieszanie się systemu. Ryzyka projektowe mają z kolei bezpośredni wpływ na powodzenie samego projektu. Przykładem ryzyka projektowego może być np. brak wykwalifikowanej kadry, spowodowany np. zwolnieniem się kilku osób z firmy. Sam fakt braku ludzi nie ma bezpośredniego wpływu na jakość produktu, ale może spowodować opóźnienie projektu w czasie.

ryzyko produktowe, ryzyko jakościowe (ang. product risk, quality risk) – ryzyko bezpośrednio powiązane z przedmiotem testów, np. atrybutem jakościowym ryzyko projektowe (ang. project risk) – ryzyko związane z zarządzaniem i kontrolą projektu (testowego), np. braki zasobów, napięty harmonogram, zmieniające się wymagania itp. Praktycznie nie da się wymienić wszystkich ryzyk, jakie mogą wystąpić w projekcie. W tabeli 19.2 są podane przykłady ryzyk produktowych oraz projektowych. Tabela 19.2. Przykłady ryzyk produktowych i projektowych

RYZYKA PRODUKT OW E (JAKOŚCIOW E) wys oka złożonoś ć s truktury modułu (zwięks za prawdopodobieńs two defektu) s komplikowana architektura (pog ars za pielęg nowalnoś ć s ys temu) niewykorzys tywanie lub błędne wykorzys tywanie wzorców projektowych podczas kodowania (pog ars za lub uniemożliwia tes towalnoś ć) nowi, niedoś wiadczeni prog ramiś ci w zes pole (zwięks zają ryzyko wprowadzenia defektów) nowe technolog ie wykorzys tywane w projekcie

RYZYKA PROJEKT OW E

ś rodowis ko tes towe i g otowoś ć narzędzi (może opóźnić proces tes towy) dos tępnoś ć pers onelu tes ters kieg o i jeg o kwalifikacje (może wpłynąć neg atywnie na jakoś ć tworzonych tes tów oraz opóźnić tes towanie) nis ka jakoś ć artefaktów tes towych (może wprowadzać w błąd kadrę zarządzającą przy podejmowaniu decyzji dotyczących produktu) zbyt duże zmiany w zakres ie lub definicji produktu (może opóźnić

(zwięks zają np. ryzyko błędów integ racji) brak przes zkolenia w przeprowadzaniu ins pekcji formalnych (zwięks za prawdopodobieńs two niezauważenia defektów podczas analizy s tatycznej kodu) napięte harmonog ramy (powodują s tres , niedokładnoś ć działania i więks ze s zans e na popełnienie pomyłek) nowe reg ulacje prawne (ryzyko błędneg o działania prog ramu)

proces tes towy i zwięks zyć kos zty fazy tes towania) dos tępnoś ć wykwalifikowanych członków zes połu (może opóźnić projekt) konflikty w zes pole (mog ą obniżyć morale zes połu, a co za tym idzie jeg o efektywnoś ć) brak ws parcia kadry zarządzającej (utrudnia prowadzenie projektu, jeg o finans owanie, rozwiązywanie konfliktów; w eks tremalnym przypadku może doprowadzić do anulowania projektu)

19.4. Zarządzanie ryzykiem w cyklu życia Z punktu widzenia zarządzania, w testowaniu opartym na ryzyku można wyróżnić cztery fazy: identyfikacja ryzyka; analiza ryzyka; łagodzenie ryzyka; monitorowanie ryzyka. Dwie pierwsze tradycyjnie postrzega się jako czynności związane z planowaniem i szacowaniem ryzyka (ang. risk assessment), natomiast dwie ostatnie – jako czynności związane z kontrolą ryzyka. Schematycznie model zarządzania ryzykiem jest pokazany na rysunku 19.3.

Rysunek 19.3. Model zarządzania ryzykiem Choć na rysunku poszczególne aktywności są narysowane jako odrębne fazy, w praktyce czynności identyfikacji, analizy, łagodzenia i monitorowania istotnie na siebie zachodzą. Dzieje się tak z kilku powodów. Proces zarządzania ryzykiem rozciąga się na cały okres trwania projektu. Co jakiś czas pojawiają się nowe ryzyka, które należy przeanalizować i oszacować ich poziom oraz opracować metody ich łagodzenia. Inne ryzyka mogą już wystąpić i w stosunku do nich należy wdrożyć plany naprawcze. Przy przejściu do kolejnej fazy projektowej może zajść potrzeba identyfikacji nowych ryzyk, związanych z daną fazą lub obszarem, którego dotyczyć będzie ta faza. Przy tym wszystkim na bieżąco, w sposób ciągły musimy monitorować i raportować poziom ryzyka. zarządzanie ryzykiem (ang. risk management) – systematyczne wdrażanie procedur i praktyk dla zadań identyfikacji, analizowania i ustalania priorytetów oraz kontrolowania ryzyka W idealnym przypadku proces zarządzania ryzykiem produktowym i projektowym powinien być opisany w wysokopoziomowym dokumencie, takim jak polityka testów czy strategia testów. Dokument taki powinien również

określać, w jaki sposób zarządzanie ryzykiem należy zintegrować z cyklem wytwórczym oprogramowania w organizacji bądź w poszczególnych projektach. W organizacjach o dojrzałym procesie wytwarzania proces identyfikacji ryzyk nie kończy się na ich wypisaniu. Dokonuje się pogłębionej analizy tak, aby dotrzeć do źródła – pierwotnej przyczyny powstawania ryzyk, stosując tzw. analizę przyczyny źródłowej (ang. root cause analysis). Usunięcie tych pierwotnych przyczyn nie tylko minimalizuje prawdopodobieństwo i/lub wpływ poszczególnych ryzyk, lecz także jest istotnym elementem ulepszania procesu wytwórczego bądź testowego. analiza przyczyny źródłowej, analiza przyczyny podstawowej (ang. root cause analysis) – technika analizy zorientowana na identyfikację podstawowych przyczyn defektów; przez wprowadzenie ukierunkowanych miar na podstawowe przyczyny defektów oczekuje się, że prawdopodobieństwo ponownego wystąpienia defektu będzie zminimalizowane W kolejnych czterech podrozdziałach opiszemy dokładniej poszczególne fazy zarządzania ryzykiem. W ostatnim podrozdziale omówimy istniejące techniki analizy ryzyka.

19.5. Identyfikacja ryzyka Identyfikacja ryzyka to próba możliwie najdokładniejszego określenia, co może pójść źle oraz kiedy i gdzie to się może wydarzyć. Jest to kluczowa faza dla całego procesu zarządzania opartego na ryzyku, ponieważ zarówno analizę, łagodzenie, jak i monitorowanie ryzyka możemy wykonywać dla znanych nam ryzyk. Identyfikacja może dotyczyć różnych typów ryzyk, związanych z typem testów, których wykonanie będzie minimalizować te ryzyka. typ ryzyka, kategoria ryzyka (ang. risk type, risk category) – specyficzna kategoria ryzyka, związana z typem testów, które należy przeprowadzić w taki sposób, aby świadomie je ograniczać i łagodzić (np. ryzyko związane z interfejsem użytkownika może być łagodzone przez przeprowadzenie testów użyteczności)

Produktem końcowym fazy identyfikacji jest lista ryzyk. Lista ta powinna być możliwie kompletna. Oczywiście nigdy nie mamy gwarancji, że zidentyfikowaliśmy wszystkie możliwe ryzyka, niemniej jednak należy starać się znaleźć ich tak dużo, jak to tylko możliwe. Aby osiągnąć ten cel, kierownik testów lub inna osoba odpowiedzialna za zarządzanie ryzykiem musi zapewnić, aby w pracach nad identyfikacją ryzyka brali udział wszyscy interesariusze. Każda osoba ma bowiem inne spojrzenie na system i oczekuje od niego innych rzeczy. Uzyskanie możliwie różnorodnego spojrzenia na produkt umożliwi zdefiniowanie większej liczby ryzyk. identyfikacja ryzyka (ang. risk identification) – proces identyfikacji ryzyk wykorzystujący takie techniki jak burza mózgów, listy kontrolne, historia awarii

19.5.1. Analiza interesariuszy (stakeholder analysis) W dużych projektach, w których tworzony produkt będzie oddziaływać na wiele osób identyfikację interesariuszy można przeprowadzić w postaci formalnego procesu, zwanego analizą interesariuszy (ang. stakeholder analysis). Proces ten wchodzi w skład obszaru zarządzania interesariuszami. Interesariuszem jest każda osoba lub organizacja, która wpływa na projekt, na którą projekt może mieć wpływ lub która jest w jakikolwiek sposób zainteresowana powodzeniem danego przedsięwzięcia. Ze względu na stopień wpływu na projekt, interesariuszy można podzielić na: głównych interesariuszy (ang. primary stakeholders), czyli osoby lub organizacje bezpośrednio zaangażowane w projekt (np. programiści, testerzy, analitycy biznesowi, użytkownik końcowy oprogramowania); pobocznych interesariuszy (ang. secondary stakeholders), czyli osoby lub organizacje, na które wpływ projektu jest pośredni (np. kierownictwo działu finansowego firmy wdrażającej system finansowo-księgowy nadzoruje pracowników tego działu, którzy bezpośrednio będą z tego systemu korzystać; program ten oddziałuje bezpośrednio na pracowników, ale pośrednio na ich kierownika, który nie jest bezpośrednim użytkownikiem programu);

kluczowych interesariuszy (ang. key stakeholders), czyli osoby lub organizacje niezbędne dla powodzenia przedsięwzięcia, których wpływ na projekt jest kluczowy (np. sponsor projektu lub klient zamawiający oprogramowanie). Interesariuszy można również podzielić ze względu na stopień powiązania z projektem: interesariusze wewnętrzni (np. pracownicy organizacji wytwarzającej oprogramowanie, kierownictwo tych organizacji); interesariusze powiązani (np. dostawcy, sprzedawcy, pracownicy działu obsługi klienta, wdrożeniowcy, pracownicy działu finansowego lub/i prawnego, dystrybutorzy); interesariusze zewnętrzni (np. użytkownicy, agencje rządowe, urzędy kontroli, prasa/media, społeczeństwo). Z punktu widzenia procesu tworzenia oprogramowania interesariuszy można podzielić ze względu na rolę, jaką odgrywają w tym procesie. Oto przykładowe role: kierownik projektu; sponsor projektu (osoba lub organizacja finansująca projekt); użytkownicy bezpośredni (osoby, które będą bezpośrednio używać oprogramowania); użytkownicy pośredni (osoby, które będą kupować nasz produkt sprzedawany jako „oprogramowanie z półki”); klient (osoba lub organizacja zamawiająca produkt); analityk biznesowy (osoba, która tłumaczy wymagania klienta wyrażone w języku biznesowym na język techniczny, zrozumiały dla zespołu; architekt (osoba, która tworzy projekt architektury systemu); programista (osoba, która na podstawie dokumentu wymagań oraz projektu tworzy kod); tester (osoba, która testuje tworzony kod oraz wszystkie artefakty procesu wytwórczego); kierownictwo (np. kierownik testów, kierownik programistów);

kierownictwo wyższego szczebla (dyrektor IT, kierownik działu, prezes); ekspert (osoba mająca wiedzę dziedzinową lub biznesową pokrywającą obszar dziedzinowy projektu, np. ekspert z dziedziny prawa, bankowości, standardów przemysłowych lub osoba mająca wiedzę związaną z charakterystykami jakościowymi oprogramowania, np. ekspert użyteczności, ekspert ds. bezpieczeństwa); wsparcie techniczne (osoby, które będą współpracowały po stronie producenta lub dystrybutora oprogramowania z klientem używającym tego oprogramowania, rozwiązując jego problemy); dokumentalista (osoba tworząca dokumentację użytkową). Każdy z tych interesariuszy będzie miał inne spojrzenie na system, inne priorytety co do systemu, inne wymagania, inny poziom władzy oraz inny stopień zainteresowania projektem. Każdy z nich będzie również w stanie zidentyfikować innego rodzaju ryzyka produktowe oraz projektowe.

19.5.2. Technika „władza versus zainteresowanie” Istnieje wiele formalnych technik analizy interesariuszy (zob. np. [172]). Jedną z nich jest technika o nazwie „władza versus zainteresowanie” (ang. power versus interest). Pozwala ona zidentyfikować interesariuszy, których interesy oraz podstawy władzy muszą być wzięte pod uwagę podczas rozwiązywania problemu. W naszym przypadku problemem jest identyfikacja ryzyk.

Rysunek 19.4. Macierz „władza versus zainteresowanie” Technika polega na skonstruowaniu macierzy przedstawionej na rysunku 19.4. Składa się ona z czterech części, podzielonych ze względu na dwa kryteria: stopień władzy, jaką ma interesariusz oraz stopień jego zainteresowania projektem. Używając techniki burzy mózgów, identyfikuje się wszystkich interesariuszy i umieszcza ich nazwiska w odpowiednich polach macierzy. W ten sposób tworzy się cztery grupy interesariuszy. Każda z tak stworzonych grup wymaga odmiennego podejścia i traktowania. Z punktu widzenia analizy ryzyka najważniejszymi grupami są „podmioty” oraz „kluczowi gracze”. Osoby przypisane do tych kategorii będą najbardziej

wartościowymi interesariuszami pod kątem identyfikacji ryzyk. Kluczowi gracze to osoby o dużej władzy, a tę ma zwykle kierownictwo wyższego szczebla, dlatego będą oni zwykle potrafili określać ryzyka projektowe. „Podmioty” z kolei będą się skupiać na ryzykach produktowych, czyli tych, które z punktu widzenia testowania interesują nas najbardziej. „Tłum” to osoby, których projekt nie dotyczy bezpośrednio i które nie będą zbytnio w niego zaangażowane. Przykładem może być dział prawny zajmujący się kwestiami licencji na tworzone przez firmę oprogramowanie. Osoby z działu prawnego nie muszą brać bezpośrednio udziału w procesie identyfikacji ryzyk, natomiast powinny być informowane o wszystkich działaniach, które mogą mieć związek z kwestiami prawno-legislacyjnymi. „Kreatorzy kontekstu”, podobnie jak „tłum”, nie są zbyt zainteresowani projektem, ale mają za to dużą władzę. Mogą dostarczyć informacji na temat ryzyk projektowych, ponieważ są to osoby, które zwykle mają wgląd w całość prac organizacji i potrafią zidentyfikować ryzyka o charakterze strategicznym, tzn. długofalowe lub związane z działalnością całej organizacji i mające przez to przełożenie na nasz projekt. Należy pamiętać, że testerzy jedynie przeprowadzają proces identyfikacji ryzyk – samo ryzyko należy do biznesu. Jeśli podczas procesu identyfikacji ryzyk część z nich zostanie pominięta, to zawsze jest odpowiedzialny za to biznes – nigdy tester.

19.5.3. Techniki identyfikacji ryzyk Istnieje wiele technik identyfikacji ryzyk. Każda skupia się na trochę odmiennym podejściu do procesu identyfikacji zagrożeń. Nie wszystkie ryzyka są oczywiste i nie wszystkie mogą zostać zidentyfikowane w ten sam sposób. Wybór odpowiedniej techniki zależy od wielu czynników, m.in. od stopnia dojrzałości organizacji, kwalifikacji i doświadczenia osób uczestniczących w procesie identyfikacji ryzyk, dostępnego czasu oraz tego, w którym momencie cyklu życia proces identyfikacji jest przeprowadzany. Najczęściej spotykane techniki identyfikacji ryzyka to: Technika oparta na wymaganiach – przydatna, jeśli mamy dobrze określony zbiór wymagań. Każde wymaganie jest w pewien sposób związane z odpowiadającym mu ryzykiem, ponieważ niespełnienie lub niewystarczające spełnienie wymagania oznacza problem, a więc jest

ryzykiem. Większość wymagań można przeformułować jako ryzyka (np.

wymaganie

„system

powinien

odpowiadać

na

zapytanie

użytkownika w ciągu 5 sekund” jest związane z ryzykiem „czas działania – system może działać zbyt wolno”). Burza mózgów – czyli spotkanie, na którym w krótkim czasie ludzie w nim uczestniczący mogą wygenerować wiele różnych pomysłów. Podstawową zasadą burzy mózgów jest to, że nie można krytykować pomysłów wymyślanych przez innych uczestników, ponieważ każdy, nawet najgłupszy czy najbardziej absurdalny pomysł może być dla innego uczestnika spotkania bodźcem do wymyślenia bardziej sensownego pomysłu. Wymyślone przez uczestników ryzyka są zapisywane w postaci krótkiej nazwy oraz wskazania jego źródła. Na etapie burzy mózgów nie dokonuje się analizy ryzyka – jedynie identyfikację. Nacisk jest położony na wymyślenie jak największej liczby pomysłów, ich jakość jest drugorzędna. Więcej informacji na temat burzy mózgów oraz innych technik pracy grupowej jest podane w rozdziale 29. Wywiady eksperckie – metoda stosowana, gdy interesariusze mogący wziąć udział w burzy mózgów lub innej technice są niedostępni. Polega ona na przeprowadzaniu kilkudziesięciominutowych wywiadów (zwykle jest to ok. 30–40 minut) z kadrą zarządzającą, zewnętrznymi konsultantami, ekspertami dziedzinowymi, doświadczonymi kierownikami projektów bądź kierownikami testów. Ważne jest, aby osoba, z którą przeprowadza się wywiad, była dobrze poinformowana o aktualnym statusie projektu oraz jego specyficznej charakterystyce. Wadą tej techniki jest brak interakcji pobudzającej do kreatywności, jaka ma miejsce np. w przypadku burzy mózgów. Niezależna ocena ryzyka – czyli dokonanie oceny przez doświadczonych, ale niezależnych ekspertów. Niezależność nie oznacza, że ekspert musi pochodzić spoza organizacji, ale że nie ma żadnego interesu w powodzeniu projektu. Innymi słowy, ekspert nie powinien być interesariuszem projektu. Listy kontrolne – stosowane, aby uniknąć „odkrywania koła na nowo”. Listy kontrolne zawierają spis możliwych ryzyk. Metoda polega na przeglądaniu listy kontrolnej i decydowaniu, czy poszczególne ryzyka

występujące na niej mogą się urzeczywistnić w naszym projekcie, czy w ogóle go nie dotyczą. Innymi słowy, wybieramy podzbiór interesujących

nas

ryzyk.

Istnieje

wiele

ogólnodostępnych

list

kontrolnych, z których można skorzystać. Można również posługiwać się swoją własną listą kontrolną, budowaną na podstawie doświadczeń z poprzednich projektów. Przed zastosowaniem metody należy upewnić się, czy elementy znajdujące się na liście kontrolnej mają odpowiedni poziom abstrakcji. Listy kontrolne mogą dotyczyć w szczególności takich zagadnień jak: zgodność ze standardami, możliwe wewnętrzne i zewnętrzne zagrożenia dla sprzętu, oprogramowania, danych lub zasobów ludzkich, kryteria akceptacji itp. [171]. Doświadczenie z poprzednich projektów (ang. lessons learned). Jest to bardzo ważna i skuteczna technika, zwłaszcza w przypadku organizacji, w których przeprowadzono wiele projektów w podobny sposób. Doświadczenie nabyte podczas pracy przy tych projektach jest bezcenne. Technika ta wymaga skrupulatnego zbierania danych w czasie trwania całego projektu. Dobrym momentem na zebranie takich danych jest np. spotkanie retrospektywne czy spotkania na zakończenie poszczególnych faz bądź kamieni milowych projektu. Często stosowaną praktyką jest mieszanie różnych technik identyfikacji ryzyk. Na przykład stosowanie listy kontrolnej można połączyć z burzą mózgów, traktując ryzyka występujące na liście jako „wyzwalacze” kreatywnych pomysłów uczestników sesji. Takie podejście można traktować w ogóle jako osobną metodę, zwaną „warsztatami ryzyka” (ang. risk workshops). niezgodność (ang. non-conformity) – niespełnienie konkretnego wymagania [1]

19.5.4. Przykłady ryzyk produktowych Black [173] podaje obszerną listę ryzyk, w formie listy kontrolnej. Przytoczymy ją tu w całości, ponieważ jest ona bardzo przydatna w praktycznych sesjach identyfikacji ryzyk. Black grupuje ryzyka ze względu na poziom testowania: Poziom testów jednostkowych:

stany (czy zachowania stanów modułu są poprawne? czy przejścia ze stanu do stanu następują podczas odpowiednich zdarzeń/wyzwalaczy? czy są wyzwalane prawidłowe akcje? czy z kazdym wejściem są związane poprawne zdarzenia?); transakcje (czy moduł poprawnie przetwarza transakcje? czy nie ma żadnych efektów ubocznych?); pokrycie kodu (jakie instrukcje, rozgałęzienia, decyzje, warunki, pętle, ścieżki oraz inne elementy przepływu sterowania mogą powodować awarie?); pokrycie przepływu danych (jakie przepływy danych w module, do modułu lub z modułu mogą powodować natychmiastowe lub opóźnione awarie? jaki typ przepływu może być za to odpowiedzialny – przekazanie wartości przez parametr, obiekt, zmienną globalną, trwałą strukturę danych, np. plik lub tabela bazodanowa?); funkcjonalność (czy funkcjonalność dostarczana przez ten moduł jest poprawna? czy może powodować efekty uboczne?); interfejs użytkownika (jeśli komponent wchodzi z użytkownikiem, czy użytkownik może mieć

w interakcję problemy ze

zrozumieniem komunikatów, informacji, żądań systemu? czy kolorystyka, grafika i inne elementy projektowe są odpowiednie dla użytkownika?); zużycie mechaniczne (czy komponent sprzętowy może się zużyć lub zepsuć podczas intensywnego używania?); jakość sygnału (czy w przypadku komponentów sprzętowych sygnały przez nie przetwarzane są odpowiedniej jakości? czy są prawidłowe?). Poziom testów integracyjnych: interfejsy komponentów lub podsystemów (czy interfejsy między komponentami są poprawnie zdefiniowane? jakie problemy mogą wystąpić podczas bezpośredniej i pośredniej interakcji komponentów?); funkcjonalność (czy mogą wystąpić efekty uboczne w wyniku interakcji komponentów? czy interfejsy obsługują całkowicie żądaną funkcjonalność?); pojemność i przepustowość (czy pamięć operacyjna i dyskowa wystarczają do przechowywania niezbędnych danych? czy medium

przekazu danych takie jak np. sieć ma odpowiednią przepustowość?); obsługa wyjątków i odtwarzanie po awarii (czy zintegrowany komponent zachowuje się poprawnie zarówno podczas typowych, jak i ekstremalnych sytuacji? czy komponent radzi sobie poprawnie z obsługą błędów, jakie mogą się pojawić? czy komponent potrafi powrócić do normalnego działania w przypadku awarii?); jakość

danych

(czy

system

potrafi

przechowywać,

ładować,

modyfikować, archiwizować i przetwarzać dane w sposób niezawodny, nie uszkadzając ich ani nie tracąc?); wydajność (czy mogą wystąpić jakieś problemy z czasem odpowiedzi, optymalną alokacją i zużyciem zasobów itp.?); interfejs użytkownika (jak w przypadku testów jednostkowych). Poziom testów systemowych i akceptacyjnych: funkcjonalność (czy funkcjonalność „end-to-end” jest zapimlementowana poprawnie? czy kombinacje funkcji programu poprawnie ze sobą współpracują?); interfejs użytkownika i użyteczność (czy system jest spójny na poziomie całego oprogramowania? czy interfejs jest zrozumiały dla użytkownika?); stany i transakcje poprawnie?); jakość danych (jw., i akceptacyjnych);

(czy ale

system na

obsługuje

poziomie

odpowiednie

testów

stany

systemowych

operacje (złożone systemy często wymagają administracji; czy mogą pojawić się problemy związane z utrzymaniem systemu, z migracją danych, z archiwizacją, z przydzielaniem uprawnień użytkownikom, z tworzeniem kopii zapasowych itp.?); wydajność i pojemność (czy są problemy z czasem odpowiedzi? czy system dobrze działa pod dużym obciążeniem? czy występują kłopoty z przepustowością łącza?); niezawodność, dostępność, stabilność (czy system podlega awarii podczas normalnego użytkowania? czy podczas dużego i ekstremalnego obciążenia? czy system może być niedostępny? czy wszystkie funkcje są stabilne?);

obsługa wyjątków i odtwarzanie po awarii (jw., ale na poziomie testów systemowych i akceptacyjnych); obsługa daty i czasu (czy zdarzenia związane z czasem mogą być awaryjne? czy funkcje używające daty/czasu dobrze współpracują ze sobą? czy sytuacje takie jak rok przestępny lub zmiana czasu (letni na zimowy bądź na odwrót) mogą powodować jakieś problemy? co ze strefami czasowymi?); lokalizacja (czy są poprawnie wspierane strony kodowe dla różnych języków?); sieć (czy opóźnienia, przepustowość lub inne czynniki związane z siecią mogą powodować problemy?); kompatybilność (czy system może być niekompatybilny z innym oprogramowaniem, sprzętem, systemem operacyjnym lub środowiskiem?); standardy (czy system używa jakichś standardów i czy może je naruszyć?); bezpieczeństwo (czy użytkownicy w ramach przydzielonych uprawnień mogą podjąć akcje wymagające innych uprawnień? czy mogą uzyskać dostęp do zasobów, do których nie powinni mieć dostępu? czy dane są szyfrowane tam, gdzie jest to konieczne? czy system jest podatny na ataki bezpieczeństwa?); środowisko (w przypadku systemów sprzętowych, czy działanie środowiska może powodować awarie systemu? czy czynniki fizyczne, takie jak temperatura, wilgoć czy zanieczyszczenia, mogą spowodować trwałą lub tymczasową awarię?); zużycie energii (czy są problemy ze zbyt wysokim zużyciem energii w systemach sprzętowych? czy wahania napięcia mogą spowodować awarie?); dokumentacja (czy dokumentacja zawiera błędy, nieścisłości? czy jest kompletna i pomocna? czy jest spójna? czy jest napisana zrozumiałym językiem?); pielęgnowalność (czy można zauktualizować system? czy można bezproblemowo zainstalować łatki? czy można usunąć niepotrzebne lub doinstalować nowe funkcjonalności bez powodowania awarii?).

19.6. Analiza ryzyka Po dokonaniu identyfikacji możliwych ryzyk należy je przeanalizować, to znaczy oszacować ich poziom, czyli efekty potencjalnego ich wystąpienia. O ile identyfikacja polega na dostarczeniu jak największej ilości ryzyk, o tyle analiza pozwala na ich skategoryzowanie bądź uszeregowanie od najważniejszych do najmniej istotnych. Jak już wcześniej wspomnieliśmy, poziom ryzyka wyraża się najczęściej jako iloczyn prawdopodobieństwa jego wystąpienia oraz wpływu, jaki faktyczne wystąpienie ryzyka będzie miało na projekt bądź produkt: Poziom ryzyka = Prawdopodobieństwo wystąpienia × Wpływ analiza ryzyka (ang. risk analysis) – proces oceny zidentyfikowanych ryzyk mający

na

celu

oszacowanie

ich

wpływu

i

prawdopodobieństwa

urzeczywistnienia się Niektóre metody zarządzania ryzykiem (np. PRisMa) nie uwzględniają poziomu ryzyka, traktując od początku do końca prawdopodobieństwo oraz wpływ jako oddzielne czynniki. Dzięki temu nie tracimy informacji o wielkości każdego z tych składników (np. dla ryzyka o bardzo dużym wpływie, ale niewielkim prawdopodobieństwie, jego poziom będzie niski bądź średni, co wcale nie musi odzwierciedlać stopnia ważności tego ryzyka, właśnie ze względu na wysoki wpływ). W podrozdziale 19.9 omówimy dokładniej istniejące i wykorzystywane w praktyce techniki analizy ryzyka.

19.6.1. Klasyfikacja ryzyk W analizie ryzyka, poza szacowaniem poziomu ryzyk, powinno się również dokonywać ich klasyfikacji. Podstawą klasyfikacji może być systematyka przyjęta i używana w organizacji, ale można również wykorzystać dostępne normy i standardy, takie jak rodzina norm ISO/IEC/IEEE 25000 (tzw. SQuaRE) [174]. Klasyfikacja może być oparta na charakterystykach jakościowych oprogramowania (np. funkcjonalność, użyteczność, niezawodność, przenaszalność, efektywność). Klasyfikacja ryzyk jest bardzo pomocna podczas procesu łagodzenia ryzyka, ponieważ typ ryzyka zwykle jest silnie skorelowany z określonym typem testu. Jeśli stosujemy klasyfikację opartą na charakterystykach jakościowych, to np. ryzyko należące do typu „wydajność”

najprawdopodobniej będzie redukowane przez wykonanie odpowiedniego testu wydajnościowego. Statystyczna informacja o liczbie oraz poziomie ryzyk określonego typu pozwala również lepiej zorganizować i zaplanować proces testowy. Jeśli np. okaże się, że najwięcej ryzyk o najwyższym wpływie jest związanych z użytecznością, prawdopodobnie proces testowy będzie się skupiał na tej charakterystyce jakościowej. Być może trzeba będzie zaplanować zorganizowanie laboratorium użyteczności, stworzyć odpowiednie kwestionariusze dla użytkowników itd.

19.6.2. Czynniki wpływające na prawdopodobieństwo i wpływ ryzyka Poziom ryzyka zależy od prawdopodobieństwa jego wystąpienia oraz potencjalnego wpływu. Czynniki wpływające na wielkość prawdopodobieństwa mają odmienną naturę od czynników określających wpływ. Prawdopodobieństwo określa szansę na to, że potencjalny problem ujawni się w testowanym systemie. Sam fakt pojawienia się problemu zależy od takich rzeczy, jak: użyty język programowania, stopień skomplikowania kodu, stosowanie określonych praktyk programistycznych, doświadczenie programistów, dostępność zasobów itp. Innymi słowy, prawdopodobieństwo wynika z ryzyka technicznego. Wpływ z kolei pochodzi z ryzyka biznesowego, określa on bowiem dotkliwość (lub: ważność, ang. severity), z jaką wystąpienie ryzyka działa na produkt oraz jego interesariuszy. dotkliwość, ważność (ang. severity) – stopień wpływu defektu na rozwój lub działanie modułu lub systemu [7] Na rysunku 19.5 przedstawiono przykładowe czynniki wpływające zarówno na ryzyko techniczne, jak i biznesowe.

Rysunek 19.5. Czynniki wpływające na prawdopodobieństwo i wpływ ryzyka

19.6.3. Ilościowa i jakościowa ocena ryzyka produktowego Poziom ryzyka produktowego może być ustalony na dwa sposoby: ilościowo lub jakościowo. Należy jednak pamiętać, że cały czas mówimy o postrzeganym, nie rzeczywistym poziomie ryzyka. Wszystkie nasze oceny są oparte na naszych przekonaniach co do prawdopodobieństwa czy wpływu ryzyka. Nie znamy jego rzeczywistego wpływu1. Podejście ilościowe pozwala wyrazić ryzyko jako stratę wyrażoną w wartości pieniężnej (lub też np. jako opóźnienie wyrażone w jednostce czasu, np. w dniach, tygodniach, miesiącach). Używanie liczb ma tę zaletę, że jest dokładne, ale niestety poziomu ryzyka nie da się wyrazić w dokładnych wartościach liczbowych. Dlatego bardzo często wykorzystuje się podejście jakościowe. Operuje ono skalą porządkową, zwykle złożoną z kilku stopni. Najczęściej spotykane skale porządkowe to: skala trójstopniowa, np.: prawdopodobieństwo duże, średnie, małe;

skala Likerta (pięciostopniowa), np.: ryzyko znikome, małe, przeciętne, duże, olbrzymie; skala dziesięciostopniowa, np. od 0 do 9 lub od 1 do 10. Używanie skal porządkowych o dużej liczbie możliwych wartości skutkuje tym, że osoby korzystające z tej skali rzadziej wykorzystują wartości skrajne. Używając skali dziesięciostopniowej, należy pamiętać, że liczby na niej występujące można ze sobą porównywać, tzn. można powiedzieć, że 4 jest mniejsze od 5, ale nie można powiedzieć, że między 0 a 1 jest taka sama różnica jak między 4 a 5, ponieważ jest to skala porządkowa, a nie interwałowa. Najczęściej stosowaną skalą porządkową jest pięciostopniowa skala Likerta. Z jednej strony ma ona odpowiednio dużo elementów, z drugiej jest ich na tyle mało, że użytkownicy nie mają oporów przed stosowaniem wartości skrajnych. ocena ryzyka (ang. risk assessment) – proces oceny ryzyka projektowego lub produktowego w celu określenia jego poziomu, zwykle przez wyznaczenie wartości prawdopodobieństwa i wpływu, a następnie połączenie tych wskaźników w pojedynczy wskaźnik priorytetu ryzyka Jeśli stosujemy podejście jakościowe do szacowania ryzyka, to musimy zdefiniować, w jaki sposób przeprowadzać mnożenie prawdopodobieństwa i wpływu. Zwykle definiuje się w tym celu specjalną tabelę, zwaną macierzą ryzyka produktowego, określającą wprost wyniki mnożeń elementów takich jak „duży”, „przeciętny” czy „znikomy”. Przykłady takich macierzy wyznaczających poziom ryzyka na podstawie jakościowo określonego prawdopodobieństwa i wpływu są pokazane na rysunku 19.6. Oczywiście każda organizacja i każdy kierownik projektu może zdefiniować własną macierz opartą na własnych skalach.

Rysunek 19.6. Przykłady macierzy ryzyka produktowego Macierz w lewej górnej części rysunku pokazuje przypadek, w którym zarówno prawdopodobieństwo, jak i wpływ oraz poziom ryzyka są podane na tej samej skali. Zauważmy, że wartości są tu ułożone symetryczne względem przekątnej, co sugeruje, że zarówno prawdopodobieństwo, jak i wpływ są traktowane w podobny sposób. Macierz w lewej dolnej części (wykorzystująca klasyczną skalę Likerta) nie ma już tej symetrii: średnie prawdopodobieństwo i olbrzymi wpływ dają olbrzymi poziom ryzyka, natomiast olbrzymie prawdopodobieństwo i średni wpływ dają tylko duży poziom ryzyka. Olbrzymi wpływ ma tutaj większą wagę niż olbrzymie prawdopodobieństwo. Macierz w prawej górnej części to przykład na to, że skala wyniku mnożenia prawdopodobieństwa przez wpływ nie musi być identyczna ze skalą tych dwóch czynników. Jak widać, skala poziomu ryzyka w stosunku do skal prawdopodobieństwa i wpływu używa dodatkowych dwóch elementów: „bardzo mały” i „bardzo duży”. Macierz w prawym dolnym rogu z kolei pokazuje, że skale obu czynników nie muszą być takie same; co więcej, nie muszą mieć takiej samej liczby elementów. W tym przykładzie wpływ odnoszony jest na trzystopniowej skali, a prawdopodobieństwo – na dwustopniowej. Ostateczna postać macierzy ryzyka produktowego zależy od konkretnego produktu, potrzeb i decyzji osób odpowiedzialnych za zarządzanie ryzykiem.

19.6.4. Osiąganie konsensusu w procesie decyzyjnym W inżynierii oprogramowania dane, na podstawie których analizuje się ryzyko zwykle nie są ścisłe i statystycznie poprawne. Dlatego ocena ryzyka zwykle jest

subiektywna. Każdy z interesariuszy może postrzegać różne ryzyka oraz ich poziomy w odmienny sposób. Jest więc konieczne ustalenie sposobu na osiągnięcie konsensusu w przypadku, gdy rozpiętość ocen poszczególnych osób jest bardzo duża. Istnieje wiele metod osiągania konsensusu w takich sytuacjach. Oto kilka z nich: Uśrednianie wyników – metoda najprostsza, zwłaszcza w przypadku, gdy mamy do czynienia z danymi ilościowymi. Jeśli jednak rozpiętość poszczególnych ocen jest bardzo duża, to ta wysoka wariancja oznacza wysoką niepewność i nie zawsze zwykłe wyciąganie średniej może być najlepszym pomysłem. Czasami zamiast średniej stosuje się medianę, która jest odporna na wartości ekstremalne. Metoda „wide band Delphi” – pokonuje ona trudności metody uśredniania; w przypadku dużych rozbieżności oceny skrajne poddane są dyskusji, po czym proces oceny jest powtarzany do momentu uzyskania pełnego konsensusu lub akceptowalnego poziomu rozbieżności (w takim przypadku wynik się uśrednia lub oblicza medianę). Oczekuje się, że w wyniku dyskusji na temat ocen skrajnych eksperci biorący udział w panelu zmodyfikują swoje oceny tak, że w rezultacie ich wyniki będą ze sobą bardziej zbieżne w kolejnych rundach. Więcej informacji na temat tej i podobnych metod pracy grupowej podanych jest w rozdziale 29. Metoda odcinania wartości skrajnych – jeśli w procesie oceny bierze udział większa liczba osób (co najmniej 3, ale najlepiej 5 lub więcej), po dokonaniu indywidualnych ocen odrzuca się wartości skrajne, po czym oblicza się średnią; ta metoda w stosunku do metody uśredniania jest mniej podatna na wartości odstające, np. dla ocen 1, 5, 5, 5 wynikiem będzie 5 (średnia z dwóch środkowych wartości, po usunięciu jedynki i jednej piątki), natomiast w przypadku metody uśredniania wynikiem będzie (1+5+5+5)/4 = 4.

19.6.5. Priorytetyzacja ryzyk Gdy analiza ryzyk zostanie ukończona, możemy dokonać priorytetyzacji według ich poziomu. Pozwoli to uszeregować wszystkie ryzyka od najważniejszych do tych, którymi można zająć się na samym końcu. Poziom, prawdopodobieństwo,

wpływ oraz klasyfikacja umożliwiają również spojrzenie na ryzyka z różnych perspektyw. Oto kilka przykładów. Załóżmy, że w procesie identyfikacji i analizy otrzymaliśmy listę ryzyk przedstawioną w tabeli 19.3. Tabela 19.3. Lista ryzyk z określonym poziomem i kategorią

Ryzyko Prawdopodobieństwo

W pływ Poziom Kategoria

R1

0,2

400

80

funkcjonalnoś ć

R2

0,05

20 000

1000

bezpieczeńs two

R3

0,3

600

180

bezpieczeńs two

R4

0,1

1500

150

wydajnoś ć

R5

0,3

3000

900

niezawodnoś ć

R6

0,3

2000

600

funkcjonalnoś ć

R7

0,25

2500

625

bezpieczeńs two

R8

0,5

100

50

funkcjonalnoś ć

R9

0,6

400

240

niezawodnoś ć

R10

0,3

450

135

użytecznoś ć

R11

0,15

240

36

niezawodnoś ć

Jakie informacje możemy otrzymać, analizując szczegółowo tę tabelę? Po pierwsze, możemy dokonać priorytetyzacji ryzyk, tak jak to pokazano w tabeli 19.4 w pierwszych trzech kolumnach. Jak widać, największy priorytet mają ryzyka R2 i R5, natomiast najmniej istotnymi są ryzyka R1, R8 i R11. Po drugie, możemy określić całkowite ryzyko przez obliczenie średniej ważonej wpływu, gdzie wagami są prawdopodobieństwa (jest to po prostu suma poziomów ryzyka): Rtotal = 0.2 ⋅ 400 + 0.05 ⋅ 20000 + … + 0.3 ⋅ 450 + 0.15 ⋅ 240 = 3996.

Oczywiście zabieg ten możemy wykonać tylko wtedy, gdy stosujemy podejście ilościowe do szacowania ryzyka. Porównując całkowitą wartość ryzyka z poziomami poszczególnych ryzyk, możemy określić ilościowo ich wpływ na powodzenie projektu. Jest to znacznie dokładniejsza informacja niż sama priorytetyzacja. Ta ostatnia ustala jedynie kolejność, natomiast ilościowe określenie wpływu pozwala stosować miary ilorazowe, tzn. pozwala nam np. powiedzieć, że jedno ryzyko ma dwukrotnie większy wpływ na powodzenie projektu niż inne. Procentowy udział w całkowitym ryzyku jest przedstawiony w ostatniej kolumnie tabeli 19.4. Jak widać poziom ryzyk R2 i R5 stanowi aż 48% całkowitego ryzyka – są to więc ryzyka kluczowe, na które trzeba będzie zwrócić szczególną uwagę w procesie łagodzenia ryzyk. Tabela 19.4. Priorytetyzacja ryzyk

Ryzyko

Priorytet

Poziom

Udział w całkowitym ryzyku

R2

1

1000

25%

R5

2

900

23%

R7

3

625

16%

R6

4

600

15%

R9

5

240

6%

R3

6

180

4%

R4

7

150

4%

R10

8

135

3%

R1

9

80

2%

R8

10

50

1%

R11

11

36

1%

Po trzecie, możemy określić poziom ryzyk w podziale na kategorie, tak jak na rysunku 19.7.

Największy, bo aż 75-procentowy udział w ryzyku mają zagrożenia związane z bezpieczeństwem i niezawodnością

Oznacza to, że proces testowy powinien skupić się głównie na testach bezpieczeństwa i niezawodności.

Rysunek 19.7. Poziom ryzyka według jego klasyfikacji

19.7. Łagodzenie ryzyka Po określeniu ryzyk, ich poziomu, kategoryzacji oraz klasyfikacji można przystąpić do kolejnego etapu – łagodzenia ryzyka. Polega on na przygotowaniu planów mających na celu zapobieżenie w możliwie jak największym stopniu wystąpieniu ryzyk oraz opracowaniu planów „awaryjnych” na wypadek, gdyby ryzyka te jednak się urzeczywistniły. łagodzenie ryzyka, kontrola ryzyka (ang. risk mitigation, risk control) – proces, w którym podejmuje się decyzje i implementuje metryki w celu redukcji ryzyka lub utrzymania go na określonym poziomie

19.7.1. Sposoby łagodzenia ryzyka Istnieją cztery główne sposoby łagodzenia ryzyka:

łagodzenie ryzyka (ang. risk mitigation) przez przedsięwzięcie czynności prewencyjnych, zapobiegających pojawieniu się ryzyka lub zmniejszających ich ewentualną dotkliwość; plany awaryjne (ang. contingency plans) mające na celu zredukować siłę oddziaływania ryzyka, które rzeczywiście nastąpi; transfer ryzyka (ang. risk transfer), czyli przeniesienie ryzyka na stronę trzecią (np. ubezpieczyciela), który będzie ponosił skutki ewentualnego wystąpienia ryzyka; zignorowanie i zaakceptowanie ryzyka, które polega po prostu na tym, że nie podejmuje się żadnych akcji do momentu wystąpienia tego ryzyka. Wybór metody

zależy

od rodzaju ryzyka, stopnia

jego

krytyczności,

istniejących zasobów, ale także od dodatkowych, potencjalnych ryzyk związanych z każdym sposobem łagodzenia ryzyka. Przykładem takiego dodatkowego ryzyka może być utrata pieniędzy bądź czasu na przeprowadzenie czynności, których jednak można było uniknąć. Pierwsza z wymienionych opcji – opcja łagodzenia – wydaje się być najbardziej optymalna, ponieważ pozwala zredukować szansę samego wystąpienia ryzyka. Niestety nigdy nie będziemy w stanie zminimalizować wszystkich zagrożeń tylko przez łagodzenie. Ryzyka mogą pojawić się w nieoczekiwanych momentach, zwłaszcza te, których istnienia nie przewidzieliśmy w fazie identyfikacji. Dlatego zwykle korzysta się z wszystkich czterech metod. Całe zarządzanie ryzykiem polega na znalezieniu odpowiedniego balansu, „złotego środka” między założonym poziomem jakości końcowego produktu a kosztem wysiłku maksymalizującego prawdopodobieństwo osiągnięcia tego celu. W kontekście testowania relację tę można ująć liczbowo. Opiszemy to dokładnie w podrozdziale 24.2. Należy pamiętać, że czynności łagodzące ryzykiem trzeba wykonać w odpowiednim czasie (zwanym przedziałem ufności2), aby skutecznie minimalizować prawdopodobieństwo ich wystąpienia. Jeśli np. ryzyko może wystąpić w fazie projektowania, to czynności łagodzące należy zacząć już w fazie wymagań albo jeszcze wcześniej. przedział ufności (ang. confidence interval) – w zarządzaniu ryzykiem projektowym – czas, podczas którego musi zostać podjęte działanie, aby

skutecznie zredukować wpływ ryzyka

19.7.2. Łagodzenie ryzyka przez testowanie Łagodzenie ryzyka można osiągnąć w szczególności przez testowanie. Każda wykryta awaria pozwala na usunięcie defektu, który był potencjalnym źródłem ryzyka. Bezbłędne wykonanie dobrze zaprojektowanych testów daje nam większą pewność, że żadne ze zidentyfikowanych uprzednio ryzyk nie wystąpi lub że prawdopodobieństwo jego wystąpienia jest mniejsze niż sądziliśmy przed wykonaniem testów. Poziom ryzyka wyznacza zakres i dokładność testowania. Im wyższy poziom ryzyka, tym dokładniej należy przetestować obszar związany z tym ryzykiem. Dokładność może np. polegać na zastosowaniu silniejszych lub bardziej formalnych metod projektowania testów. Dobrym przykładem ilustrującym to zagadnienie jest standard przemysłowy DO-178C [8], dotyczący systemów dla awioniki. Standard ten określa poziom krytyczności systemu na podstawie wielkości skutków, jakie może nieść ze sobą awaria tego systemu. Norma DO-178C wyróżnia pięć poziomów krytyczności (patrz tab. 19.5) i dla każdego z nich narzuca wymaganie spełnienia odpowiednich kryteriów pokrycia testami białoskrzynkowymi. Kryteria te są opisane w rozdziale 9. Innym przykładem standardu uzależniającego rodzaj testowania od poziomu ryzyka może być norma IEC 61508 „Functional safety of electrical/electronic/programmable electronic safety-related systems” [175]. Norma ta wyróżnia cztery tzw. poziomy nienaruszalności bezpieczeństwa (ang. Safety Integrity Levels, SIL – w odniesieniu do oprogramowania używa się także pojęcia Software Integrity Level). Każdy z poziomów wyznacza kryteria niezawodności systemu wyrażone w postaci współczynnika tolerowanego zagrożenia (ang. Tolerable Hazard Rate, THR), czyli dopuszczalnej częstości awarii na jednostkę działania systemu. Tabela 19.5. Poziomy krytyczności wg normy DO-178C

Poziom Nazwa

Opis

W ymagane osiągnięcie pokrycia

Awaria s ys temu może s powodować utratę s tatku powietrzneg o

Pokrycie MC/DC Pokrycie decyzji Pokrycie ins trukcji

A

Katas trofalny

B

Awaria s ys temu znacznie redukuje zdolnoś ć s tatku powietrzneg o lub załog i do reakcji na neg atywne zjawis ka, powodując poważną utratę Niebezpieczny marg ines u bezpieczeńs twa lub funkcjonalnoś ci, fizyczne niebezpieczeńs two lub poważne obrażenia niewielkiej liczby pas ażerów

Pokrycie decyzji Pokrycie ins trukcji

C

Awaria redukuje zdolnoś ć załog i do reakcji na neg atywne zjawis ka, s kutkując utratą marg ines u bezpieczeńs twa, obrażeniami pas ażerów lub koniecznoś cią wykonywania dodatkowych, obciążających załog ę czynnoś ci powodując u niej s tres

Pokrycie ins trukcji

Duży

Awaria nie redukuje znacząco bezpieczeńs twa s tatku powietrzneg o, pog ars za nieznacznie marg ines bezpieczeńs twa lub funkcjonalnoś ci s tatku,

D

Mały

powoduje nieznaczny wzros t Brak dodatkowych czynnoś ci obciążających załog ę (np. zmiana planu lotu) lub pewien dys komfort ps ychiczny u załog i lub pas ażerów

E

Awaria nie ma wpływu na poziom bezpieczeńs twa i nie Znikomy/Brak powoduje koniecznoś ci wykonywania dodatkowych czynnoś ci przez załog ę

Brak

poziom nienaruszalności bezpieczeństwa (poziom integralności oprogramowania) (ang. Safety Integrity Level, Software Integrity Level, SIL) – stopień, w jakim oprogramowanie spełnia lub musi spełniać zbiór wybranych przez interesariuszy cech oprogramowania lub charakterystyk systemu (np. złożoność oprogramowania, ocenione ryzyko, poziom zabezpieczeń, poziom bezpieczeństwa, pożądana wydajność, niezawodność lub koszt), które to cechy obrazują, jak ważne jest dane oprogramowanie dla jego interesariuszy W odniesieniu do oprogramowania norma IEC 61508 rekomenduje stosowanie odpowiednich technik testowania w zależności od poziomu SIL. Opisano to w tabeli 19.6, w której znak + oznacza, że dana technika jest rekomendowana, a ++ oznacza silną rekomendację. Pozostałe przykłady technik łagodzenia ryzyka przez testerów to: priorytetyzacja testów według poziomu ryzyka; wybór odpowiednich technik projektowania testów; przeprowadzanie przeglądów i inspekcji; przeprowadzanie przeglądów projektów testów; stosowanie wczesnego prototypowania; uzyskanie określonego poziomu niezależności;

wykorzystywanie umiejętności najbardziej doświadczonych osób; przeprowadzanie szkoleń z testowania czy tworzenia testowalnego kodu o wysokiej jakości; zdefiniowanie zakresu i intensywności retestów; zdefiniowanie zakresu i intensywności testów regresji; automatyzowanie projektowania i wykonywania testów. Tabela 19.6. Poziomy nienaruszalności bezpieczeństwa wg IEC 61508

Poziom SIL

SIL 1

SIL 2

SIL 3

SIL 4

THR dla pracy ciąg łej

[10–6, 10– 5)

[10–7, 10– 6)

[10–8, 10– 7)

[10–9, 10– 8)

THR dla pracy na żądanie

[10–2, 10– 1)

[10–3, 10– 2)

[10–4, 10– 3)

[10–5, 10– 4)

Tes towanie przeciążeń

+

+

++

++

Tes towanie wydajnoś ci

++

++

++

++

+

+

T echniki testowania

Grafy przyczynowos kutkowe AWB/klas y równoważnoś ci

+

++

++

++

Zg adywanie błędów

+

+

+

+

Tes towanie białos krzynkowe

+

+

++

++

+

+

+

Pos iew us terek

Niektóre z wymienionych czynności o charakterze prewencyjnym rozpoczynają się bardzo wcześnie w cyklu życia, nawet przed fazą testów dynamicznych. W testowaniu opartym na ryzyku czynności związane

z łagodzeniem ryzyka powinny następować w ciągu całego czasu trwania projektu, a nie tylko na samym początku lub samym końcu.

19.7.3. Estymacja kosztów łagodzenia ryzyka Kadra zarządzająca, po zaprezentowaniu jej sposobów łagodzenia ryzyka, ma zwykle nastawienie entuzjastyczne, ponieważ wszystko wygląda wspaniale, a powodzenie całego projektu spoczywa na kierowniku testów traktowanym jako ekspert w tej dziedzinie. Problemy zaczynają się, gdy mowa o kosztach łagodzenia. Zwykle są one za duże. Wtedy należy być przygotowanym do odpowiedzi na trudne pytania, na przykład: czy możemy to zastąpić testowaniem eksploracyjnym? ile zaoszczędzimy, łagodząc w taki a taki sposób kryteria wyjścia z fazy testów? co się stanie, jeśli nie przeprowadzimy testów wydajnościowych? Kierownik testów powinien być przygotowany na takie pytania. Najlepiej zrobić to, szacując koszty oraz potencjalne zyski, np. w terminach redukcji ryzyka czy wzrostu jakości. Mając zidentyfikowane ryzyka wraz z oszacowaniem ich poziomu, a także mapowanie testów na te ryzyka, można stworzyć symulator w arkuszu kalkulacyjnym, który w szybki sposób będzie potrafił wykonać tzw. analizę „what if”. Dzięki wykorzystaniu takiego symulatora kierownik testów będzie mógł szybko odpowiedzieć na pytania takie, jak te wymienione wcześniej. Kadra zarządzająca zwykle bardzo lubi tego typu narzędzia, a osobę wykorzystującą je postrzega jako profesjonalnego eksperta w swojej dziedzinie. Przykładowa symulacja jest pokazana na rysunku 19.8. W lewym górnym rogu są zapisane dane „konfiguracyjne”, opisujące efektywność i pracochłonność dla każdej z możliwych do zastosowania metod. W głównej tabeli jest przedstawiona symulacja kosztu oraz zysku pochodzącego z łagodzenia ryzyka. Tabela jest skonstruowana tak, aby obliczyć zysk z stosowania analizy ryzyka wobec każdej funkcjonalności. Na przykład poziom ryzyka związany z funkcjonalnością F1 wynosi 60 000 USD. Po zastosowaniu testowania czarnoskrzynkowego usuniemy 70% defektów z F1 i szacujemy, że w związku z tym poziom ryzyka obniży się również o 70%, czyli do poziomu 30% ⋅ 60 000 USD = 18 000 USD.

Rysunek 19.8. Przykład symulatora dla łagodzenia ryzyka Złagodzone ryzyko wynosi 70% ⋅ 60 000 USD = 42 000 USD. F1 liczy 4.5 KLOC, a ponieważ pracochłonność dla tej metody to średnio 3 osobodni/KLOC, zatem testy czarnoskrzynkowe dla F1 zajmą 4,5 KLOC ⋅ 3 osobodni/KLOC = 13,5 osobodni. Koszt jednodniowej pracy jednej osoby wynosi 300 USD, zatem koszt testów czarnoskrzynkowych dla F1 szacujemy na 300 USD/osobodzień ⋅ 13,5 osobodni = 4050 USD. Szacunkowy zysk, jaki otrzymujemy przy zastosowaniu analizy ryzyka do funkcjonalności F1 to zysk z minimalizacji ryzyka pomniejszony o koszt wykonanej analizy (w tym przypadku – testowania czarnoskrzynkowego), czyli 42 000 USD – 4050 USD = 37 950 USD. Dla funkcjonalności, wobec których zastosowano więcej niż jedną metodę zakładamy, że używa się ich sekwencyjnie. Na przykład dla F5, stosując inspekcję (efektywność 90%), z poziomu ryzyka 40 000 zostaje 10% ⋅ 40 000 = 4000. Ten poziom ryzyka stosuje się dla kolejnej metody, czyli testowania czarnoskrzynkowego (efektywność 70%), w wyniku czego z poziomu ryzyka zostaje 30% ⋅ 4000 = 1200 itd. Sumując zyski z minimalizacji ryzyka we wszystkich obszarach projektu i odejmując od tej wartości sumaryczny koszt wykonania analizy ryzyka, otrzymujemy ostatecznie kwotę 359 605 USD – 250 950 USD = 108 655 USD. Możemy

zinterpretować tę wartość, jako „wirtualny” zysk (kwota, jakiej nie stracimy w wyniku ponoszenia kosztów ryzyka) z analizy ryzyka. W takim symulatorze bardzo łatwo zmieniać dane wejściowe i obserwować jak np. zmieni się całkowity zysk z zastosowania konkretnego podejścia. Oczywiście czynimy tu wiele założeń, np.: ryzyko może być wykryte przez metody weryfikacji i walidacji; koszty powodowane przez defekty rozłożone są równomiernie itd. Nawet, jeśli założenia te nie do końca są spełnione, a dane nie są idealnie dokładne, tego typu narzędzia są bardzo wartościowe. Umożliwiają bowiem, przynajmniej w przybliżeniu, udzielenie odpowiedzi na wspomniane wcześniej pytania kadry zarządzającej, a nawet na przeprowadzenie analizy wrażliwości.

19.8. Monitorowanie ryzyka Gdy zaplanowaliśmy czynności prewencyjne oraz metody radzenia sobie z ryzykiem, które rzeczywiście nastąpi, przechodzimy do fazy monitorowania. Polega ono na ciągłej obserwacji aktualnego stanu systemu. Dzięki monitorowaniu możemy na bieżąco obserwować aktualny poziom ryzyka w produkcie, kontrolować, czy proces redukcji ryzyka przebiega zgodnie z planem, identyfikować nowo powstałe problemy oraz szacować poziom ryzyka rezydualnego. Wreszcie, przez pomiary możemy określić, czy uzyskaliśmy zadowalający poziom ryzyka. Proces monitorowania jest kluczowy dla kierownictwa, ponieważ produkty tego procesu, w postaci wszelkiego rodzaju raportów, są podstawą do decydowania o tym, czy produkt może przejść do kolejnej fazy lub czy może zostać przekazany klientowi. Podczas monitorowania jest zbieranych wiele różnego rodzaju danych. Po zakończeniu projektu dane te powinny zostać zarchiwizowane tak, aby można je było wykorzystać – jako dane historyczne – w przyszłych projektach prowadzonych w organizacji. Wszystkie zebrane dane stanowią nasze doświadczenie, opisują jak rzeczywiście przebiegał projekt w zadanych okolicznościach. Z tego powodu są niezwykle cenne szczególnie przy budowie modeli predykcyjnych. Na podstawie takich modeli możemy np. oszacować, jak szybko, w wyniku testowania, będzie następować redukcja ryzyka w nowym

projekcie, co pozwoli oszacować czas i koszt testowania. Więcej informacji o tego typu modelach Czytelnik znajdzie w rozdziałach 44–49.

19.8.1. Macierz identyfikowalności ryzyk Monitorowanie dotyczy nie tylko kwestii ilościowych, lecz także jakościowych. Na przykład powinniśmy monitorować wykonywanie czynności łagodzących ryzyko, np. fakt wykonania określonych testów czy przeglądów. Powinniśmy również kontrolować, czy czynności te skutkują założonymi efektami (np. poprawą jakości produktu wyrażoną jako zmniejszona gęstość defektów czy wzrost czasu między awariami). W kontekście testowania pomiar redukcji ryzyka jest możliwy dzięki istnieniu obustronnego powiązania (ang. traceability) między testami a zidentyfikowanymi ryzykami produktowymi. Przykład takiej macierzy identyfikowalności jest pokazany na rysunku 19.9. Znak X na przecięciu przypadku testowego i ryzyka oznacza, że dany test pokrywa to ryzyko. Na rysunku tym, oprócz przedstawienia powiązania między testami a ryzykami, jest również przedstawiony aktualny stan pokrycia ryzyk. Znak X na białym polu oznacza, że dany test nie został jeszcze wykonany. Pole szare oznacza, że test był wykonany, ale nie został zdany, czyli są konieczne naprawa oraz retest. Pole ciemne oznacza, że dany test został wykonany i zdany, a co za tym idzie – odpowiednie ryzyko zostało pokryte. Niektóre ryzyka mogą być związane z więcej niż jednym testem. W takim przypadku możemy mówić o częściowym pokryciu ryzyk. Na przykład, zakładając, że ryzyko R1 jest pokrywane trzema przypadkami testowymi: PT1, PT2 i PT3, w sytuacji z rysunku 19.9 ryzyko to jest pokryte w 66%, ponieważ testy PT1 i PT2 zostały wykonane i są zdane, natomiast test PT3 nie został jeszcze wykonany. Analizując taką macierz identyfikowalności z naniesionymi informacjami o bieżącym stopniu wykonania testów, możemy określić aktualny stan pokrycia ryzyk i raportować kierownictwu o poziomie ryzyka rezydualnego. Jeśli stosujemy

Rysunek 19.9. Macierz identyfikowalności ryzyka-testy podejście ilościowe do szacowania ryzyka, możemy również wyrazić aktualny postęp w wartościach liczbowych (np.: „w bieżącej wersji produktu ryzyko rezydualne wynosi 15%”). Na podstawie wspomnianych informacji kierownik testów może zdecydować o rozszerzeniu zakresu testów, jego redukcji, zawieszeniu, zakończeniu testowania lub o innych czynnościach związanych z procesem testowym. Zaletą takiego monitorowania w podejściu opartym na ryzyku jest to, że możemy w miarę dokładnie określić moment, w którym produkt można przekazać klientowi, mając formalne potwierdzenie o akceptowalnym poziomie ryzyka rezydualnego. kryteria zawieszenia (ang. suspension criteria) – kryteria używane do (tymczasowego) zatrzymania wszystkich lub części aktywności testowych na elementach testowych [5] kryteria wznowienia (ang. resumption criteria) – kryteria używane do wznowienia części lub całości aktywności testowych, które zostały uprzednio wstrzymane

19.8.2. Aktualizacja ryzyk oraz ich parametrów Należy pamiętać o ciągłej aktualizacji zarówno samych ryzyk, jak i ich poziomu. Czynności identyfikacji i analizy ryzyka nie powinny być wykonywane wyłącznie

na początku projektu, ale w sposób okresowy, np. w każdej iteracji, przy przejściu z jednej fazy do drugiej, po osiągnięciu kamienia milowego, po spotkaniu retrospektywnym dla fazy itp. Testy powinny być wykonywane zgodnie z priorytetyzacją ryzyk, tzn. testy pokrywające ryzyka o wyższym poziomie powinny być wykonywane wcześniej i podlegać bardziej szczegółowej ocenie niż testy pokrywające ryzyka marginalne. W związku z postępem projektu, lista ryzyk powinna być okresowo przeglądana i kierownik testów powinien dla każdego ryzyka produktowego przedyskutować następujące kwestie [171]: czy dane ryzyko zostało prawidłowo oszacowane? czy czynności łagodzenia ryzyka (np. wykonanie testów) zostały przeprowadzone? jakie są efekty czynności łagodzących ryzyko (np. wyniki testów)? czy w stosunku do danego ryzyka należy przeprowadzić dodatkowe czynności, np. więcej testów? czy można dane ryzyko usunąć z listy ryzyk? Należy również zdecydować, czy do listy ryzyk powinno się dodać nowe ryzyka. Dodanie nowych ryzyk może skutkować zmianami w priorytetyzacji testów. Wszystkie tego typu dyskusje powinny odbywać się na formalnych spotkaniach z udziałem kierownika testów oraz innych interesariuszy, bezpośrednio zaangażowanych w kwestie związane z ryzykiem produktu (np.: testerzy, programiści, klient). Przykładowe sytuacje, które mogą skutkować koniecznością dodania do listy nowych ryzyk to [171]: nowe lub zmienione wymagania; odkrycie nowych, krytycznych obszarów biznesowych pominiętych we wcześniejszych analizach; odkrycie, w wyniku testowania, obszarów produktu o wysokiej gęstości defektów lub odkrycie nieoczekiwanych defektów; wczesne wyniki testów (np. w wyniku przeglądu dokumentów lub projektu architektury) są bardziej optymistyczne lub bardziej pesymistyczne niż zakładano; ryzyko testów regresji związanych z naprawianymi defektami; zmiana w podejściu do wytwarzania oprogramowania;

doświadczenie związane z jakością nabyte we wcześniejszych fazach projektu.

19.8.3. Raportowanie Testowanie oparte na ryzyku daje możliwość dokładnego raportowania o stanie produktu pod kątem występujących w nim ryzyk. Kierownictwo często nie rozumie koncepcji ryzyka w testowaniu i nierzadko zdarza się, że kierownik projektu bardziej jest zainteresowany liczbą wykonanych testów niż poziomem ryzyka rezydualnego. Podejście testowania opartego na ryzyku oznacza również raportowanie oparte na ryzyku. Dlatego raporty powinny kłaść nacisk na kwestie związane z ryzykiem, a nie na liczbę zaprojektowanych, wykonanych czy zdanych przypadków testowych. Takie podejście ma również pozytywny wpływ na sam proces testowania. Ludzie dostosowują się do miar, którymi się ich ocenia. Gdyby kierownictwo kładło nacisk np. na liczbę zaprojektowanych i wykonanych przypadków testowych, natychmiast testerzy zaczęliby produkować wiele niskiej jakości testów, które nie weryfikowałyby zidentyfikowanych ryzyk produktowych. Doprowadziłoby to do niebezpiecznej sytuacji, gdyż kierownictwo byłoby zadowolone z osiągniętego celu, ale niekoniecznie oznaczałoby to redukcję ryzyka rezydualnego! Wszyscy mieliby złudne poczucie bezpieczeństwa oraz wysokiej jakości produktu, a prawdziwe problemy zaczęłyby się dopiero w momencie przekazania produktu klientowi – czyli o wiele za późno. Raportowanie wysokopoziomowe może wykorzystywać metody takie, jak macierz identyfikowalności z rysunku 19.9, ale może też być jeszcze bardziej wysokopoziomowe. Należy pamiętać, że poziom abstrakcji informacji zawartych w raporcie musi być odpowiedni do adresata raportu. Kierownictwo wyższego szczebla niekoniecznie musi być zainteresowane dokładnymi informacjami o poszczególnych testach. Czasami wystarczy jeden wykres czy tabela z kilkoma wartościami liczbowymi, aby dać kierownictwu wgląd w ogólny stan projektu. Przykłady wysokopoziomowych raportów są pokazane na rysunku 19.10. Na lewym górnym wykresie przedstawiono aktualne pokrycia ryzyk w podziale na ich poziom. Na wykresie kołowym z prawej strony zaprezentowano zbiorczą informację o tych ryzykach: proporcja ryzyk pokrytych (odpowiednio: problemów i ryzyk niepokrytych) to proporcja testów zdanych (odpowiednio: testów niezdanych i niewykonanych) do wszystkich testów. Na dolnym wykresie

przebiegu pokazano zmianę pokrycia w czasie. Aby nie „zaśmiecać” wykresu, przedstawiono informację tylko o dwóch rodzajach ryzyk: krytycznych i dużych, łącząc je w jedną kategorię. Jest to dosyć prosty, ale czytelny dla kierownictwa raport. Można np. na jego podstawie szybko określić aktualny pozom ryzyka rezydualnego lub sprawdzić, czy redukcja ryzyka postępuje zgodnie z planem. Raport z rysunku 19.10 pokazuje na przykład, że jeśli chodzi o redukcję ryzyk o poziomie krytycznym i dużym, początkowo proces szedł bardzo dobrze (wyprzedzaliśmy plan), ale obecnie jesteśmy opóźnieni w stosunku do planu i sytuacja ta trwa od dwóch miesięcy. Należy znaleźć przyczynę tych problemów i wprowadzić korekty do procesu tak, aby nadrobić opóźnienia. Tego typu raporty, choć nieskomplikowane i zawierające bardzo zagregowaną informację, często są wystarczające dla kierownictwa wyższego szczebla do podejmowania kluczowych czy strategicznych decyzji odnośnie do tworzonego produktu.

Rysunek 19.10. Przykład wysokopoziomowego raportu o ryzykach

19.9. Techniki analizy ryzyka W podrozdziale tym opiszemy kilka istniejących podejść do analizy ryzyka. Techniki te różnią się stopniem formalizacji, wykorzystywanymi metodami czy wymaganiami czasowymi. Z grubsza można je podzielić na dwie kategorie: metody „lekkie” (PRAM, SST, PRisMa) i „ciężkie” (hazard analysis, Cost of Exposure, FMEA, QFD, FTA). Jeśli kierownik testów decyduje się na stosowanie formalnej metody analizy ryzyka (o ile nie została ona narzucona z góry np. przez strategię testowania), to podczas wyboru konkretnej techniki należy wziąć pod uwagę kilka czynników: dostępność zasobów oraz kwalifikacje personelu – niektóre metody wymagają doświadczenia w ich stosowaniu; czas poświęcony na wdrożenie oraz stosowanie metody; koszt (np. dodatkowe szkolenia czy koszt wynikający z czasu, jaki członkowie zespołu poświęcają na wykonywanie czynności wymaganych przez metodę); dostępność wymaganych przez metodę danych. Techniki lekkie są stosowane częściej i przez to zwykle są technikami dojrzałymi, zweryfikowanymi przez praktykę. Wykorzystują wiedzę i doświadczenie wielu osób, mających różne perspektywy na jakość tworzonego systemu. Produkt wyjściowy tych metod (np. macierz ryzyka czy tabela analizy ryzyka) stanowi podstawę dla planu testów, a stąd również dla wszystkich czynności związanych z zarządzaniem i analizą ryzyka wykonywanych w późniejszych fazach projektu. Niektóre techniki (np. Systematic Software Testing) wymagają podania na wejście specyfikacji wymagań. Jeśli takiego dokumentu brak, metoda nie może zostać zastosowana. Inne techniki, takie jak PRAM czy PRisMa są oparte na podejściu mieszanym, które łączy w sobie strategie oparte na ryzyku i wymaganiach, przy czym te ostatnie nie muszą być reprezentowane formalnym dokumentem – wystarczy informacja od klienta. Zaletą technik silnie opartych na wymaganiach jest dobra kontrola nad pokryciem funkcjonalności oraz ryzyk z nią związanych, jednak istnieje wtedy niebezpieczeństwo pominięcia ryzyk niezwiązanych bezpośrednio z wymaganiami, np. dotyczących charakterystyk niefunkcjonalnych

oprogramowania. Kierownik testów musi zawsze pamiętać również o tym niefunkcjonalnym aspekcie systemu, niezależnie od tego, czy jest on ujęty w wymaganiach, czy nie. Wiele technik analizy ryzyka angażuje dużą grupę interesariuszy i wymaga, aby osiągnęli oni konsensus w kwestii oceny poziomu poszczególnych ryzyk. Zadanie sprawnego przeprowadzenia tego procesu należy do moderatora spotkań, na których ryzyka są identyfikowane i analizowane i gdzie taki konsensus powinno się osiągać. Może to być problematyczne, gdyż często osiągnięcie wspólnego stanowiska jest bardzo trudne. Moderator może się posłużyć metodami pracy grupowej, opisanymi w rozdziale 29.

19.9.1. PRAM (Pragmatic Risk Analysis and Management) Jest to metoda opisana przez Blacka w książce „Managing the Testing Process” [31]. PRAM zakłada, że efektywne testowanie oraz dobra komunikacja stanowią integralną część procesu zarządzania ryzykiem, co pomaga w osiągnięciu wysokiego poziomu jakości. Głównym narzędziem łagodzenia ryzyka jest testowanie. PRAM kładzie bardzo duży nacisk na dogłębną analizę ryzyka, w szczególności na to co testować oraz w jakiej kolejności to robić. Priorytetyzacja testów na podstawie tej analizy ma na celu wczesne wykrywanie najważniejszych defektów. W przypadku konieczności ograniczenia testów priorytetyzacja pozwala opuścić te najmniej istotne. W kwestii komunikacji PRAM wymaga, aby raportowanie odbywało się w formie zrozumiałej dla odbiorców raportów, a nie dla ich autorów (testerów). Podstawą do wszelkich działań jest szczegółowy plan testów. Kolejnym wymogiem jest to, aby systemy testowe były dobrze zaprojektowane. Ułatwia to łagodzenie ryzyka produktowego i zwiększa zaufanie, gdy test ostatecznie przejdzie, a defekty zostaną usunięte. Zespół testowy musi podążać za wyobrażeniem jakości przez klienta – proces testowy powinien uwzględniać przede wszystkim potrzeby użytkownika, tak aby końcowa jakość była jak najwyższa z punktu widzenia klienta. Innymi słowy, testerzy muszą wczuć się w rolę klienta, zrozumieć dogłębnie jego potrzeby i oczekiwania wobec systemu i na podstawie tej wiedzy projektować cały proces testowy. Stopień spełnienia tego „wyobrażenia klienta” PRAM nazywa wiernością (ang. fidelity). Systemy mogą charakteryzować się wysoką lub niską wiernością.

Rysunek 19.11. Systemy o wysokiej i niskiej wierności Koncepcja ta jest pokazana na rysunku 19.11. Przedstawiono na nim sytuację dwóch systemów, A i B. W systemie A (po lewej stronie) co prawda testy nie pokrywają całego obszaru produktu, ale za to wyobrażenia klienta o systemie pokrywają się prawie idealnie z tym, jak jakość tego systemu jest postrzegana przez testera. W rezultacie klient otrzyma system o wysokiej – ze swojego punktu widzenia (!) – jakości, gdyż jego wyobrażenie jakości było zbieżne z wyobrażeniem zespołu testowego. Odmienną sytuację mamy w przypadku projektu B (prawa strona rysunku). Sposób postrzegania jakości produktu przez testera jest zupełnie odmienny od punktu widzenia klienta. Oczekiwania obu stron są rozbieżne. W efekcie otrzymujemy produkt spełniający wszystkie wymagania jakościowe zespołu testerskiego, ale prawie żadnych wymagań klienta. Przydatność takiego systemu jest więc bliska zeru. Przykład ten pokazuje, jak ważna jest komunikacja między zespołem projektowym a klientem, zwłaszcza w aspekcie zrozumienia przez testerów potrzeb jakościowych klienta wobec tworzonego oprogramowania. W metodzie PRAM atrybuty ryzyka są definiowane klasycznie, jako prawdopodobieństwo oraz wpływ. Po etapie identyfikacji i analizy ryzyk należy się nimi w odpowiedni sposób zająć. Metoda PRAM wyróżnia tu cztery rodzaje aktywności:

alokacja wysiłku – podczas planowania, projektowania i wykonywania testów każdemu ryzyku należy przypisać pewien wysiłek związany z testowaniem, proporcjonalny do poziomu tego ryzyka; priorytetyzacja testów – ustalanie kolejności testów w zależności od poziomu ryzyka, które te testy pokrywają; optymalna redukcja suity testowej – w razie konieczności usunięcie tych testów, które wnoszą najmniej do redukcji ryzyka; raportowanie wyników testów odzwierciedlać przede wszystkim z poziomem ryzyka.

– raportowanie punkt widzenia

powinno związany

Jedną z cech metody PRAM jest możliwość korekty niedokładnych założeń wykorzystywanych we wczesnych fazach planowania, kiedy to oszacowania obarczone są dużym błędem. PRAM zaleca stosowanie strategii mieszanej w wykorzystywaniu reaktywnych strategii testowych, takich jak polowanie na błędy, ataki usterkowe czy testowanie eksploracyjne, omówionych w rozdziale 10.

19.9.2. SST (Systematic Software Testing) SST to kolejna z lekkich metodyk, w której podejście oparte na ryzyku stanowi tak naprawdę jedną z jej części. Metoda opisana jest w podręczniku [176], powstałym na podstawie materiałów kursów Systematic Software Testing oraz Test Management prowadzonych w Software Engineering Institute przez autorów książki, Craiga i Jaskiela. SST wykorzystuje podejście opisane w metodologii STEP (omówionej w p. 32.5.1) służącej do ulepszania procesu testowego i opisującej praktyczny sposób wdrożenia standardu IEEE 829 – Software Test Documentation [5] w ramach procesu testowego. SST opisuje całościowo podejście do testowania, dlatego tu skupimy się jedynie na omówieniu tych jej aspektów, które dotyczą strategii testowania opartego na ryzyku. Metoda SST jest dosyć podobna do opisanej wcześniej metody PRAM, co wynika poniekąd z faktu, że oba podejścia są podejściami lekkimi i całościowo ujmującymi proces testowy. SST kładzie nacisk na testowanie prewencyjne, czyli stoi na stanowisku, że testowanie może zwiększyć jakość oprogramowania, jeśli rozpocznie się odpowiednio wcześnie w cyklu życia oprogramowania. W szczególności SST zwraca uwagę na wczesne wykrywanie defektów

w wymaganiach oraz projektach architektonicznych oprogramowania. Tak jak w metodologii STEP, SST zakłada, że planowanie testów rozpoczyna się w fazie definicji wymagań, a projektowanie testaliów (testów, skryptów itd.) równolegle z projektowaniem i przed fazą kodowania. W metodzie SST testy są grupowane w suity testowe wraz z informacją o pokryciu, jakiego dostarczają. Pokrycie to można obliczyć dzięki istnieniu powiązań między przypadkami testowymi a wymaganiami, elementami projektowymi i kodem źródłowym. SST wyróżnia dwa rodzaje defektów: ukryty (ang. latent) – istniejący w oprogramowaniu defekt, który nie spowodował jeszcze awarii, ponieważ nie wystąpiły warunki umożliwiające wystąpienie tego defektu; zamaskowany (ang. masked) – istniejący w oprogramowaniu defekt, który nie spowodował jeszcze awarii, ponieważ inny defekt uniemożliwił wykonanie odpowiedniego fragmentu kodu. W kontekście testowania opartego na ryzyku, SST rozróżnia zarządzanie ryzykiem (kontrola i monitorowanie efektywności zastosowanych metod) od analizy ryzyka (identyfikacja, szacowanie i analiza ryzyka), czyli stosuje klasyczny model przedstawiony na rysunku 19.3, przy czym SST idzie tu jeszcze dalej i dzieli analizę ryzyka na dwie podstawowe aktywności: analizę ryzyka software’owego oraz analizę ryzyka w planowaniu i plany awaryjne. Schemat ten pokazany jest na rysunku 19.12.

Rysunek 19.12. Czynności w analizie ryzyka według metody SST Rozdzielenie tych dwóch aktywności pozwala na rozróżnienie między ryzykiem związanym z awarią dotyczącą cechy lub atrybutu systemu a ryzykiem związanym z wdrożeniem planu testów. Ryzyka planowania obejmują takie zdarzenia jak: daty dostarczenia produktów, dostępność personelu, budżet, dostępność środowiska i narzędzi, potrzeby szkoleniowe, zakres testowania, brak wymagań, założenia dotyczące ryzyk, zasoby, niska jakość oprogramowania itp. Czynniki te mogą wpływać negatywnie na harmonogram i powodować opóźnienia w projekcie. Według autorów SST istnieją cztery sensowne metody łagodzenia tych ryzyk: redukcja zakresu projektu; opóźnienie implementacji; dodanie zasobów; redukcja procesów jakościowych. Analiza ryzyka w metodzie SST składa się z 10 kroków: 1. Utworzenie zespołu do przeprowadzenia burzy mózgów (włączając w to klientów, użytkowników końcowych, programistów, testerów,

marketingowców i analityków biznesowych; zespół powinien być jak najbardziej interdyscylinarny). 2. Stworzenie listy cech i atrybutów systemu. 3. Określenie prawdopodobieństwa awarii cech i atrybutów systemu (stosując co najmniej skalę porządkową). 4. Ocena wpływu awarii cech i atrybutów na użytkownika (stosując co najmniej skalę porządkową). 5. Przypisanie wartości liczbowych prawdopodobieństwu i wpływowi. 6. Priorytetyzacja ryzyk (przez dodanie do siebie oceny prawdopodobieństwa 7.

i wpływu). Przegląd i modyfikacja wartości (uwzględniając np. złożoność cyklomatyczną danego modułu, analizę Pareto, nowe lub zmodyfikowane

cechy, wybór metody tworzenia oprogramowania, dostęp do środowiska, użyteczność). 8. Priorytetyzacja cech i atrybutów według priorytetu ryzyka. 9. Ustalenie granicy (ang. cut line) między cechami wymagającymi testowania i tymi, które testowaniu nie będą podlegać. 10. Łagodzenie ryzyka (wybranie ryzyk, które można złagodzić przez dodanie zasobów, zmianę metodologii bądź inne czynności prewencyjne). W ocenie poziomu ryzyka SST stosuje technikę podobną do tej używanej w metodzie FMEA (patrz p. 19.9.8). O ile prawdopodobieństwo i wpływ ocenia się na skali porządkowej, o tyle poziom ryzyka powstaje w wyniku dodania tych dwóch wartości, stając się tym samym wartością na skali interwałowej lub ilorazowej. Jest to zilustrowane w tabeli 19.7. Tabela 19.7. Obliczanie poziomu ryzyka w metodzie SST

Prawdopodobieństwo awarii

Poziom ryzyka

W pływ awarii

Niskie (1)

Średnie (2)

Duże (3)

Nis ki (1)

2

3

4

Ś redni (2)

3

4

5

Wys oki (3)

4

5

6

Prawdopodobieństwo i wpływ są ustalane na skali porządkowej, ale są przypisane im konkretne wartości liczbowe (1, 2, 3). Poziom ryzyka wyraża się wtedy wzorem: Poziom ryzyka = Prawdopodobieństwo + Wpływ Skale prawdopodobieństwa i wpływu nie są ilorazowe (tzn. nie mają „zera bezwzględnego”), więc nie możemy mówić, że ryzyko o poziomie 6. jest dwa razy większe niż ryzyko o poziomie 3. Poziomy możemy jedynie porównywać między sobą, co jest na szczęście wystarczające dla potrzeb priorytetyzacji.

19.9.3. Przykład: zastosowanie SST do systemu ELROJ Rozważmy analizę ryzyka dla systemu ELROJ opisanego w Dodatku A, przeprowadzoną według metody SST. Załóżmy, że zebrano interdyscyplinarny zespół i stworzono na podstawie wymagań następujący zbiór cech oraz atrybutów systemu, wraz z oceną ich prawdopodobieństwa oraz wpływu (kroki 1.–5.): Cechy: wyświetlanie rozkładu na ekranie; prawdopodobieństwo=wysokie, wpływ=wysoki; zarządzanie rozkładem (dodawanie i usuwanie kursów oraz linii); prawdopodobieństwo=średnie, wpływ=wysoki; komunikacja oprogramowania prawdopodobieństwo=wysokie, wpływ=niski; użyteczność (obsługa interfejsu prawdopodobieństwo=średnie, wpływ=wysoki;

z

ekranem; użytkownika);

wyświetlanie komunikatu na ekranie; prawdopodobieństwo =średnie, wpływ=średni. Atrybuty: bezpieczeństwo (możliwość modyfikacji wyświetlanych danych przez nieuprawnione osoby); prawdopodobieństwo=średnie, wpływ=wysoki; wydajność (szybkość przesyłania informacji z centrali do ekranów stojących na przystankach); prawdopodobieństwo=niskie, wpływ=średni.

Następnie obliczono priorytet i uszeregowaliśmy według niego wszystkie ryzyka (krok 6.). Podczas przeglądania tak stworzonej listy (krok 7.) zdecydowano, że ze względu na niską złożoność modułu odpowiadającego za obsługę komunikatów prawdopodobieństwo wystąpienia awarii związanej z wyświetlaniem komunikatu jest zbyt duże. Obniżono jego wartość z poziomu „średnie” do poziomu „niskie”, co równocześnie wpłynęło na obniżenie poziomu ryzyka. Ostateczna, spriorytetyzowana lista ryzyk (krok 8) jest przedstawiona w tabeli 19.8. Ryzyka o poziomie mniejszym niż 4 odcięto – w tabeli jest to zaznaczone pogrubioną linią. Zdecydowano, że ryzyka te nie będą podlegały testowaniu (krok 9.). Następnie określono akcje łagodzące ryzyko dla tych cech i atrybutów, dla których uznano to za wykonalne. Komunikacja z ekranem dotyczy kwestii sprzętowych, dlatego zespół uznał, że łagodzenie tego ryzyka nie wchodzi w zakres kompetencji testerów. Tabela 19.8. Lista ryzyk dla programu ELROJ

System ELROJ Cechy

Atrybuty

Prawdopodobieństwo W pływ Prioryt

Wyś wietlacz rozkładu jazdy

wys okie

wys oki 6

Zarządzanie rozkładem

ś rednie

wys oki 5

Użytecznoś ć (interfejs )

ś rednie

wys oki 5

Bezpieczeńs two ś rednie s ys temu

wys oki 5

Komunikacja z ekranem

wys okie

nis ki

4

Wyś wietlanie komunikatu Wydajnoś ć (trans fer danych)

nis kie

ś redni

3

nis kie

ś redni

3

19.9.4. PRisMa (Product Risk Management) Metoda PRisMa została stworzona przez van Veenendaala i opublikowana w [171]. Jest to bardzo lekka i prosta w zastosowaniu metoda. Głównym jej elementem jest tzw. macierz ryzyka produktowego (ang. product risk matrix), złożona z czterech części (kwadrantów) powstałych w wyniku podzielenia każdego z czynników ryzyka (prawdopodobieństwa oraz wpływu) na dwie grupy (wartości niskie oraz wysokie) i stworzeniu w ten sposób układu 2×2 możliwych kombinacji poziomu tych czynników. Macierz ta jest przedstawiona na rysunku 19.13a. Każdy kwadrant określa inny typ ryzyka, a co za tym idzie

Rysunek 19.13. Dwie postaci macierzy ryzyka produktowego w metodzie PRisMa – wymaga innego podejścia do jego łagodzenia. Oznaczenia kwadrantów (I, II, III, IV) są jedynie etykietami i nie mają znaczenia liczbowego. Liczba części macierzy ryzyka wynosi 4 z prostego powodu: gdyby każdy czynnik ryzyka podzielić na 3 obszary, macierz miałaby 3 ⋅ 3 = 9 części i trudno byłoby przypisywać ryzyka do aż tylu możliwych kategorii. Cztery części z jednej

strony są wystarczające dla klasyfikacji, a z drugiej – ich liczba sprawia, że proces kategoryzacji jest zrozumiały i łatwy do opanowania. Dla aplikacji o znaczeniu krytycznym do macierzy ryzyka dodaje się piątą część, opisującą ekstremalny poziom ryzyka biznesowego. Powodem tego jest fakt, iż w systemach o znaczeniu krytycznym awarie mogą powodować utratę zdrowia lub życia osób. Zauważmy, że poziom prawdopodobieństwa nie ma tu żadnego znaczenia – niezależnie od tego, czy jest ono wysokie czy marginalne, mamy do czynienia ze zbyt dużym ryzykiem, aby je różnicować z tego względu. Ryzyka, które znajdą się w polu V muszą być przetestowane w maksymalnym możliwym stopniu, używając wszystkich dostępnych sił i środków. Macierz ryzyka produktowego z naniesionymi na nią ryzykami daje interesariuszom czytelny wgląd w poszczególne zagrożenia. Graficzna prezentacja sprawdza się zwykle znacznie lepiej niż długa lista ryzyk z przypisanymi do nich wartościami prawdopodobieństwa, wpływu, poziomu itp. Jak mawia znane powiedzenie, jeden obrazek jest więcej wart niż tysiąc słów. Ludzie generalnie łatwiej radzą sobie z danymi prezentowanymi w postaci graficznej i lepiej je interpretują. Poszczególne kwadranty macierzy reprezentują różne spojrzenia na ryzyka produktowe. Kwadranty I oraz II dotyczą wysokiego ryzyka technicznego (prawdopodobieństwa). Tymi ryzykami zwykle zajmować się będziemy na poziomie testów jednostkowych oraz integracyjnych. Z kolei kwadranty II i IV reprezentują wysokie ryzyko biznesowe, dlatego te typy ryzyk są zwykle łagodzone przez testy wysokopoziomowe: systemowe oraz akceptacyjne. Należy jednak podkreślić, że jest to raczej wskazówka niż reguła. W każdym projekcie sytuacja może wyglądać zupełnie odmiennie. PRisMa, tak jak poprzednio opisane metody, wymaga identyfikacji i analizy ryzyka, jednak różni się od pozostałych metod tym, że nie definiuje wprost poziomu ryzyka. Ryzyko jest charakteryzowane prawdopodobieństwem i wpływem, które nie są ze sobą w żaden sposób łączone w jedną miarę. Dlatego ryzyko jest reprezentowane nie liczbowo, ale jako punkt na dwuwymiarowej macierzy ryzyka. Jeśli idzie o identyfikację ryzyka, PRisMa wykorzystuje dwie metody: burzę mózgów oraz metodę opartą na wymaganiach, przy czym w typowym wykorzystaniu tej techniki burza mózgów jest przeprowadzana jako wspomaganie metody opartej na wymaganiach w celu poszerzenia i zweryfikowania listy ryzyk uzyskanych za pomocą tej ostatniej.

Formalny proces PRisMa jest przedstawiony na rysunku 19.14. Zadania „nadzór i kontrola” oraz „ewaluacja” nie są unikalną częścią procesu, dlatego przedstawiono je w prostokątach o przerywanych liniach. Wysokość poszczególnych zadań na rysunku oznacza poziom organizacyjny, na którym typowo odbywa się zadanie. Na przykład faza Przygotowania zwykle ma miejsce na poziomie organizacji lub programu (zbioru projektów). Projekt przebiega zwykle od zadania Planowania do Ewaluacji, przy czym kroki Spotkanie inicjujące (ang. kick-off meeting) oraz Rozszerzona identyfikacja ryzyk są opcjonalne. Właściwa identyfikacja ryzyk odbywa się podczas etapu Planowania.

Rysunek 19.14. Proces PRisMa Metodologia PRisMa zaleca, aby wyznaczyć tzw. lidera zespołu PRisMa, który będzie odpowiedzialny za organizację i moderowanie spotkań, upewnianie się, czy zespół ma odpowiednie zasoby oraz weryfikację postępów zespołu w pracach nad szacowaniem ryzyka. Lider powinien również przeprowadzić niezbędne szkolenia z metodyki PRisMa dla osób o małym lub zerowym doświadczeniu w jej stosowaniu. PRisMa jest metodyką lekką, dlatego nadaje się do stosowania w procesach opartych na zwinnych technikach wytwarzania, takich jak Scrum czy XP. Jednym z głównych celów metodyk zwinnych jest łagodzenie ryzyka, stąd PRisMa idealnie może się wpasować w projekty agile’owe. Należy jednak wziąć pod uwagę, że na wdrożenie PRisMa trzeba poświęcić określoną ilość czasu i wysiłku. Wymaga ona

również dobrego przygotowania organizacji, w szczególności uzyskania wsparcia wszystkich członków zespołu lub firmy. Omówimy teraz poszczególne fazy procesu PRisMa.

w zakresie

Przygotowanie. Obejmuje zdefiniowanie procesów, identyfikację czynników ryzyka technicznego i biznesowego, ustalenie wag dla każdego czynnika, zdefiniowanie zakresu wykorzystywanej skali ocen oraz zdefiniowanie reguł postępowania w procesie rangowania i szacowania. Nie ma uniwersalnej listy czynników, które można by użyć przy ocenie prawdopodobieństwa lub wpływu ryzyka. PRisMa proponuje w kroku Przygotowania rozważenie w szczególności następujących czynników: Czynniki dla wpływu: obszary krytyczne (identyfikowane na podstawie analizy użycia systemu oraz tego, w jaki sposób system może ulec awarii); obszary widoczne (czyli takie, w których użytkownicy mogą bezpośrednio odczuć awarię, jeśli coś pójdzie nie tak, jak trzeba); najczęściej używane obszary (podział funkcji na użytkownicy używają zawsze, często, okazjonalnie

te, których lub rzadko

i oszacowanie wpływu na podstawie tej klasyfikacji); istotność z biznesowego punktu widzenia (tzn. jaka jest ważność poszczególnych cech systemu z punktu widzenia biznesowego celu jego działania); koszt zmian (zwykle wykorzystywany w tzw. systemach systemów). Czynniki dla prawdopodobieństwa: złożoność (np. w sensie złożoności cyklomatycznej, skomplikowanej logiki); liczba zmian (jest ona ważnym czynnikiem defektotwórczym [177]); nowe technologie i metody; presja czasu; brak doświadczenia; rozproszenie geograficzne zespołu; kod nowy vs. re-używalny; interfejsy; rozmiar;

historia defektów; jakość wymagań. Wagi czynników można ustalić według własnego uznania lub zastosować typowe wartości zdefiniowane w PRisMa, pokazane w tabeli 19.9. Tabela 19.9. Wagi czynników w PRisMa

W aga Uzasadnienie 0,5

Czynnik jes t ważny, ale mniej ważny niż inne czynniki w identyfikacji teg o, co należy tes tować

1,0

S tandardowa wag a (wartoś ć początkowa)

1,5

Czynnik jes t ważniejs zy w identyfikacji teg o, co należy tes tować

2,0

Czynnik jes t krytyczny w identyfikacji teg o, co należy tes tować

Jeśli chodzi o oceny, PRisMa oferuje trzy możliwe skale: od 1 do 3, od 1 do 5 lub zbiór wartości 0, 1, 3, 5, 9. Pierwsze dwie omówiliśmy w punkcie 19.6.3. Trzecia, tak jak druga, jest skalą typu Likerta zaczerpniętą z metody Quality Function Deployment omówionej w punkcie 19.9.10. Jest ona pewnego rodzaju kompromisem między wykorzystaniem dużej, dziesięciostopniowej skali a tym, że używając skal o dużym zasięgu, ludzie niechętnie wybierają wartości skrajne. Zdefiniowanie reguł postępowania jest ważne ze względu na konieczność stosowania całego zakresu wykorzystywanej skali. PRisMa stosuje techniki matematyczne (test chikwadrat) do statystycznej weryfikacji równomiernego wykorzystania wszystkich stopni skali w ocenie poszczególnych ryzyk. Ma to zapobiec powszechnie występującej tendencji do klastrowania wyników, tzn. grupowania wokół konkretnych wartości. Ponadto istnieją ogólne zasady związane z procedurą oceniania: Reguła oceny – każdy interesariusz zaangażowany w proces oceny musi ocenić każdy przydzielony do ewaluacji element. Reguła oceny końcowej – lider zespołu PRisMa musi obliczyć końcową ocenę każdego elementu na podstawie wszystkich ocen cząstkowych.

Reguła koła3 (ang. circle rule) – obszar koła jest zdefiniowany w centralnej części macierzy ryzyka. Elementy, które znajdują się w obszarze tego koła muszą zostać poddane procesowi uzgodnienia oceny, w wyniku którego nastąpi ostateczne przyporządkowanie ryzyka do jednego z kwadrantów (patrz rys. 19.15). Koło obejmuje obszar „niepewności”, ponieważ im bliżej środka macierzy znajduje się ryzyko, tym większa niepewność co do przynależności do określonego kwadrantu. Przypisanie ryzyka do jednego z czterech pól jest kluczowe, ponieważ skutkuje przyjęciem odmiennych strategii łagodzenia ryzyka. W narzędziach wspomagających proces PRisMa promień koła zdefiniowany jest na podstawie określonej przez użytkownika procentowej powierzchni, jaką ma zajmować. Na rysunku 19.15 widzimy, że przynależność elementu nr 3 musi zostać ustalona na spotkaniu uzgadniającym, ponieważ jako jedyny wpada w obszar koła.

Rysunek 19.15. Macierz ryzyka PRisMa i reguła koła Reguła uzgadniania. Wynik uznaje się za uzgodniony, jeśli rozkład ocen danego elementu ryzyka dokonanych przez wszystkich interesariuszy mieści się w założonym przedziale lub ma odpowiednio niską wariancję. Planowanie. W fazie planowania, przeprowadzanej typowo przez kierownika testów, wykonuje się następujące zadania: zdefiniowanie zakresu produktu, który będzie poddany analizie ryzyka;

dostrojenie czynników i reguł; zebranie niezbędnej dokumentacji, np. dokumentu wymagań czy projektu architektury; identyfikacja ryzyk produktowych – bazuje ona na zbiorze wymagań oraz innych dostępnych dokumentach, np. projekcie architektury; lista ryzyk może być rozszerzona lub uszczegółowiona w wyniku przeprowadzenia sesji burzy mózgów; identyfikacja i wybór interesariuszy – zespół analizy ryzyka powinien być możliwie interdyscyplinarny, umożliwiając różnorodne punkty widzenia na jakość systemu; nie powinien liczyć więcej niż 10 osób; przypisanie czynników do interesariuszy – nie każdy interesariusz musi oceniać poziom danego ryzyka; każdy powinien oceniać te aspekty systemu, które są ważne z jego punktu widzenia. Podczas

identyfikacji

ryzyk

produktowych

każde

ryzyko

musi

być

jednoznacznie opisane unikatowym identyfikatorem i wyrażone w języku zrozumiałym dla interesariuszy. Dla każdego ryzyka powinien być określony jego typ (np. funkcjonalność, użyteczność, niezawodność) oraz powiązanie z odpowiadającym mu wymaganiem. Spotkanie inicjujące (krok opcjonalny). Jego celem jest szczegółowe poinformowanie członków zespołu o planowanym procesie analizy ryzyka oraz uzyskanie odpowiedniego zaangażowania ludzi. Rozszerzona identyfikacja ryzyk (krok opcjonalny). Celem tego kroku jest rozszerzenie początkowego zbioru ryzyk określonego w fazie Planowania, jego ocena, a także wykrycie potencjalnych niezgodności między wymaganiami a ryzykami produktowymi. W tej fazie używa się techniki burzy mózgów, w szczególności jednej z jej odmian, mianowicie tzw. katastrofalnej burzy mózgów. W technice tej każdy z uczestników jest proszony o wyobrażenie sobie kilku najgorszych, katastrofalnych, czarnych scenariuszy związanych z analizowanym systemem. Podczas analizy w szczególności mogą pojawić się dwa typy problemów: jest ryzyko, ale nie ma wymagania – w takim przypadku należy dodać wymaganie (aby wykryć defekty możliwie wcześnie) lub usunąć ryzyko (jeśli nie chcemy testować więcej, niż jest to konieczne);

jest wymaganie, ale nie ma ryzyka – w takim przypadku należy rozszerzyć listę istniejących ryzyk (w celu uzyskania lepszego pokrycia testami) lub usunąć wymaganie (jeśli nie chcemy tworzyć więcej, niż jest to konieczne). Indywidualne przygotowania. Jego celem jest dostarczenie indywidualnych, niezależnych danych wejściowych do procesu analizy ryzyka oraz ocena czynników przypisanych poszczególnym ryzykom i przesłanie wyników do kierownika testów. PRisMa wymaga, aby rozkład poszczególnych ocen dla każdego czynnika ryzyka był równomierny, tzn. aby oceny nie skupiały się np. na granicach skali czy w jakimś jednym miejscu. Aby uzyskać taki rozkład, należy po kolei rozważać każde ryzyko i porównywać je z już ocenionymi (czy jest ważniejsze czy mniej istotne od innych). W ten sposób każde ryzyko otrzyma swoją pozycję w szeregu. Ryzyka najważniejsze otrzymują maksymalną ocenę, a najmniej istotne – najniższą. Przetwarzanie indywidualnych ocen. Celem tego kroku jest upewnienie się, że wszyscy uczestnicy przesłali swoje oceny, zebranie wszystkich ocen i stworzenie na ich podstawie pierwszej, roboczej wersji macierzy ryzyka. Należy również sprawdzić, czy nie zostały złamane reguły oceniania (w takim przypadku należy to udokumentować) oraz przygotować się do spotkania uzgadniającego. Spotkanie uzgadniające. Na spotkaniu konfrontuje się uczestników ze sposobem postrzegania ryzyk przez pozostałych członków zespołu. Dyskutuje się prawdopodobieństwa oraz wpływy poszczególnych ryzyk. W przypadku istnienia uwag dotyczących złamania reguł (patrz faza Przetwarzania indywidualnych ocen), należy te uwagi przedyskutować oraz poprawić lub uzupełnić błędne oceny. W fazie tej należy uzyskać możliwie najdalej idący konsensus dotyczący poziomu poszczególnych ryzyk oraz ich pozycji na macierzy ryzyka. W szczególności trzeba dojść do porozumienia w kwestii przypisania właściwych kwadrantów ryzykom znajdującym się w obszarze koła. PRisMa proponuje kilka technik uzyskiwania konsensusu: głosowanie; zaangażowanie eksperta; upoważnienie jednego z członków zespołu do podjęcia ostatecznej decyzji; przedyskutowanie sprawy;

wybór surowszej oceny (spowoduje to więcej prac nad łagodzeniem ryzyk, ale jest szybką metodą dojścia do porozumienia). Zdefiniowanie zróżnicowanych podejść testowania opartego na ryzyku. Celem tego kroku jest zdefiniowanie podejścia do testowania zgodnego z pozycją poszczególnych ryzyk na macierzy ryzyka. Podejście to musi być skuteczne (koncentracja na najważniejszych ryzykach) i efektywne (nie marnowanie czasu na długie testowanie ryzyk o niskim priorytecie). W fazie tej należy również upewnić się, że testerzy przeprowadzają czynności testowe w zgodzie z ryzykami produktowymi. Istnieje wiele technik testowania. W dokumencie „Test Management Quick Reference Card” opublikowanym przez Improve Quality Services [178] można znaleźć rozkład tych technik w poszczególnych kwadrantach macierzy ryzyka. Oczywiście każdy system wymaga innego podejścia w testowaniu, niemniej jednak rozkład ten można traktować jako sugestię czy źródło inspiracji. Jest on pokazany na rysunku 19.16. Poszczególne skróty pojęć z rysunku oznaczają: 0-switch – pokrycie 0-przełączeń (krawędziowe); >=1-switch – pokrycie 1-przełączeń (par krawędzi) i wyższe (ścieżek długości 3, 4 itd.); AWB – analiza wartości brzegowych; DrzKl – drzewa klasyfikacji;

Rysunek 19.16. Techniki projektowania testów a kwadranty macierzy PRisMa ECT – Elementary Comparison Test (testowanie decyzji, rozgałęzień itp.); GrP-S – grafy przyczynowo-skutkowe; KR – testowanie klas równoważności; PCT TMn – Process Cycle Test, Test Measure n (testowanie procesów biznesowych tak, aby pokryć wszystkie kombinacje przepływu przechodzącego przez n kolejnych decyzji; jest to metoda analogiczna do pokrycia ścieżek o długości n w testowaniu białoskrzynkowym); PIT – Program Interface Test (testowanie integracyjne interfejsów); PrzUż – testowanie przypadków użycia (P = przebieg podstawowy, A = przebieg alternatywny, W = przebieg z wyjątkiem);

RLT – Real Life Test (testowanie na podstawie profilu operacyjnego); Sem – testowanie semantyczne (ang. semantic testing), tzn. testowanie poprawności danych; Składnia – testowanie składni (ang. syntax testing), tzn. testowanie poprawności struktury; TExp – testowanie eksploracyjne; ZgadBłędów – zgadywanie błędów. podstawowe testowanie porównawcze (ang. Elementary Comparison Testing, ECT) – czarnoskrzynkowa technika projektowania przypadków testowych projektowanych tak, aby wykonać kombinację wejść, wykorzystując metodę testowania zmodyfikowanego pokrycia warunków wielokrotnych (MC/DC) [28] test cyklu procesu (ang. Process Cycle Test, PCT ) – czarnoskrzynkowa technika projektowania przypadków testowych, w której testy są projektowane w celu wykonania procesu lub procedury biznesowej. Polega na pokryciu wszystkich ścieżek o zadanej długości w diagramie przepływu procesu [28] Poza samymi technikami projektowania testów PRisMa proponuje jeszcze inne podejścia do testowania, takie jak: przeglądy i inspekcje, spotkania inicjujące projekt testów, przeglądy projektów testów, stosowanie odpowiedniego poziomu szczegółowości przypadków testowych, utrzymywanie określonego poziomu niezależności testerów, wykorzystanie doświadczonych pracowników, stosowanie testów potwierdzających i testów regresji oraz wykorzystywanie kryteriów zakończenia.

19.9.5. Przykład: zastosowanie PRisMa do systemu ELROJ Rozważmy ponownie system ELROJ opisany w Dodatku A. Stosujemy metodę PRisMa do analizy ryzyka w tym systemie. W fazie Przygotowania zdefiniowano następujące czynniki. Dla wpływu: widoczne obszary (WidOb) oraz istotność z biznesowego punktu widzenia (IstBiz) z wagami odpowiednio 1,0 oraz 2,0. Dla prawdopodobieństwa: złożoność (Złoż) oraz nowe technologie i metody (NTech), ponieważ zespół będzie pierwszy raz pracował nad systemem komunikującym się ze sprzętem (ekrany na przystankach). Wagi dla tych czynników określono odpowiednio jako 1,0 oraz 2,0. Ustalono, że wykorzystana będzie skala Likerta (od

1 do 5). Wykorzystane zostaną standardowe, ogólne zasady oceniania zdefiniowane w PRisMa. W fazie Planowania określono trzech interesariuszy: kierownika projektu, analityka biznesowego, oraz architekta systemu. Ponadto zidentyfikowano następujące ryzyka: 1) brak komunikacji systemu z ekranem; 2) niepoprawność wyświetlanych wyników na ekranie; 3) niewygodny interfejs użytkownika; 4) błędy w logice biznesowej systemu (zarządzanie liniami i kursami); 5) wolne działanie systemu. Interesariuszom przypisano następujące czynniki: Kierownikowi projektu: widoczne obszary, istotność biznesowa, złożoność, nowe technologie. Analitykowi biznesowemu: widoczne obszary, istotność biznesowa. Architektowi systemu: złożoność, nowe technologie. Analityk biznesowy nie powinien oceniać ryzyk pod kątem czynników technicznych (bo się na tym nie zna i jego ocena może być wypaczona). Podobnie architekt nie powinien wykorzystywać czynników biznesowych. W fazie indywidualnego przygotowania wszyscy trzej członkowie zespołu dokonali analizy ryzyk, oceniając je na podstawie przypisanych im czynników. Wyniki są zebrane w tabelach 19.10–19.12. Tabela 19.10. Szacowanie ryzyka przez kierownika projektu

Kierownik projektu

W pływ

Prawdopodobieństwo

Czynniki

W idOb IstBiz Złoż

NT ech

W agi

1,0

2,0

1,0

2,0

R1: brak komunikacji z ekranem

4

5

1

5

R2: niepoprawna informacja na ekranie

5

5

3

1

R3: niewyg odny interfejs

2

1

1

2

R4: błędy w log ice biznes owej 1

3

5

4

R5: wolne działanie s ys temu

4

4

4

2

Tabela 19.11. Szacowanie ryzyka przez analityka biznesowego

Analityk biznesowy

W pływ

Prawdopodobieństwo

Czynniki

W idOb IstBiz Złoż

NT ech

W agi

1,0

2,0

2,0

R1: brak komunikacji z ekranem

5

3

R2: niepoprawna informacja na ekranie

5

5

R3: niewyg odny interfejs

1

1

R4: błędy w log ice 3 biznes owej

2

R5: wolne 4 działanie s ys temu

3

1,0

te czynniki nie podleg ają ocenie, g dyż nie zos tały przypis ane analitykowi biznes owemu

Tabela 19.12. Szacowanie ryzyka przez architekta systemu

Architekt systemu

W pływ

Prawdopodobieństwo

Czynniki

W idOb

IstBiz

Złoż

NT ech

W agi

1,0

2,0

1,0

2,0

R1: brak komunikacji z ekranem

3

5

R2: niepoprawna informacja na ekranie

5

1

1

1

R4: błędy w log ice biznes owej

4

4

R5: wolne działanie s ys temu

1

3

te czynniki nie podleg ają ocenie, g dyż nie zos tały R3: niewyg odny przypis ane architektowi interfejs s ys temu

W kroku Przetwarzanie indywidualnych ocen kierownik testów uśrednia oceny, otrzymując wynik przedstawiony w tabeli 19.13. Tabela 19.13. Uśrednione oceny

Uśrednione oceny

W pływ

Czynniki

W idOb IstBiz Złoż

Prawdopodobieństwo NT ech

W agi

1,0

2,0

1,0

2,0

R1: brak komunikacji z ekranem

4,5

4,0

2,0

5,0

R2: niepoprawna informacja na ekranie

5,0

5,0

4,0

1,0

R3: niewyg odny interfejs

1,5

1,0

1,0

1,5

R4: błędy w log ice biznes owej 2,0

2,5

4,5

4,0

R5: wolne działanie s ys temu

3,5

2,5

3,5

3,0

Uśrednione oceny są mnożone przez odpowiadające im czynniki. W wyniku otrzymujemy ważoną ocenę. Rezultat jest przedstawiony w tabeli 19.14. Tabela 19.14. Ważona ocena ryzyk produktowych

Ryzyko

W pływ

R1: brak komunikacji z ekranem 4,5+8,0=12,5

Prawdopodobieństwo 2,0+10,0=12,0

R2: niepoprawna informacja na 5,0+10,0=15,0 4,0+2,0=6,0 ekranie R3: niewyg odny interfejs

1,5+2,0=3,5

1,0+3,0=4,0

R4: Błędy w log ice biznes owej

2,0+5,0=7,0

4,5+8,0=12,5

R5: Wolne działanie s ys temu

3,0+7,0=10,0

2,5+7,0=9,5

Każde z ryzyk jest nanoszone na macierz ryzyka. W wyniku otrzymujemy macierz przedstawioną na rysunku 19.17. Współrzędne każdego z ryzyk odpowiadają wartościom jego wpływu oraz prawdopodobieństwa. Wartości minimalne i maksymalne obu osi są wyznaczane na podstawie teoretycznych wartości skrajnych ocen, jakie można uzyskać. Minimalna ocena wpływu to 1,0 ⋅ 1 + 2,0 ⋅ 1 = 3, natomiast maksymalna to 1,0 ⋅ 5 + 2,0 ⋅ 5 = 15. Takie same wartości skrajne przyjmuje skala dla prawdopodobieństwa. Środkową wartością na obu skalach jest 9, zatem punkt o współrzędnych (9,9) stanowi środek macierzy. W fazie Spotkanie uzgadniające należy ustalić przynależność do odpowiedniego kwadrantu ryzyka R5 (wolne działanie systemu), które jako jedyne znalazło się w obszarze koła. Zespół ustalił, że ze względu na niedużą liczbę przetwarzanych

danych oraz dobrą przepustowość sieci ryzyko to cechować się będzie niskim prawdopodobieństwem. W rezultacie ryzyko przechodzi z kwadrantu II (wysoki wpływ, wysokie prawdopodobieństwo) do kwadrantu IV (wysoki wpływ, niskie prawdopodobieństwo). W ostatniej fazie (zdefiniowanie zróżnicowanych podejść testowania opartego na ryzyku) są ustalane metody łagodzenia ryzyka. W naszym przypadku będzie to ustalenie odpowiednich metod testowania. Wyniki tej fazy są przedstawione w tabeli 19.15.

Rysunek 19.17. Macierz ryzyka produktowego dla systemu ELROJ Tabela 19.15. Metody łagodzenia ryzyka dla systemu ELROJ

Ryzyko Kwadrant W pływ Prawdopodobieństwo

Metody łagodzenia

R1

wys oki wys okie

wczes ny prototyp s ys temu s prawdzający połączenie z ekranem

wys oki nis kie

tes towanie białos krzynkowe (kryterium MC/DC) oraz ins pekcje kodu

wys oki nis kie

tes ty wydajnoś ciowe, ins pekcja projektu bazy danych

nis ki

wys okie

tes towanie eks ploracyjne oraz tes towanie oparte na przypadkach użycia

nis kie

brak (poziom znikomy, nie ma potrzeby alokacji zas obów na tes towanie teg o ryzyka)

R2

R5

R4

R3

II

IV

IV

I

III

nis ki

W wyniku przeprowadzenia analizy ryzyka metodą PRisMa mamy zidentyfikowane ryzyka, określony ich typ oraz dobrze zdefiniowane akcje łagodzenia tych ryzyk. Przeprowadzenie takiego procesu pozwala w olbrzymim stopniu zredukować poziom ryzyka w systemie. Już sam fakt identyfikacji ryzyk (stworzenie macierzy ryzyka) jest dużym sukcesem, a i tak wciąż – jeśli chodzi o analizę ryzyka – wiele firm nie dochodzi nawet do tego etapu!

19.9.6. Analiza zagrożeń (hazard analysis) Analiza zagrożeń to metoda identyfikacji i łagodzenia zagrożeń, stosowana najczęściej w systemach o znaczeniu krytycznym, gdzie awaria może spowodować poważne straty, włącznie z utratą zdrowia lub życia. Zwykle analiza zagrożeń dotyczy kwestii bezpieczeństwa (ang. safety) systemu. bezpieczeństwo systemu (ang. system safety) – jest osiągane przez stosowanie zasad, kryteriów i technik z zakresu inżynierii oraz zarządania w celu osiągnięcia akceptowalnego poziomu ryzyka awarii (ang. mishap risk), uwzględniając ograniczenia operacyjnej efektywności, odpowiedniości, czasu i kosztu w ciągu całego cyklu życia systemu [179] analiza zagrożeń (ang. hazard analysis) – technika używana do charakteryzowania elementów systemu wpływających na pojawienie się ryzyka; wynik analizy zagrożeń rzutuje na wybór metod używanych do wytwarzania i testowania oprogramowania. Patrz także: analiza ryzyka Pojęcie „analizy zagrożeń” obejmuje tak naprawdę wiele różnych metod. Dwie z nich – FMEA oraz FTA – omówimy dokładniej w punktach 19.9.8 oraz 19.9.12. Inne przykładowe metody analizy zagrożeń bezpieczeństwa to: Preliminary Hazard List, Preliminary Hazard Analysis, Subsystem Hazard Analysis, System Hazard Analysis, Operating and Support Hazard Analysis, Health Hazard Assessment, Safety Requirements/Criteria Analysis (formalnie zdefiniowane i promowane w [179]); Event Tree Analysis;

Fault Hazard Analysis; Functional Hazard Analysis; Sneak Circuit Analysis; Petri Net Analysis; Markov Analysis; Barrier Analysis; Bent Pin Analysis; Hazard and Operability Analysis; Cause-Consequence Analysis; Common Cause Failure Analysis; Management Oversight Risk Tree Analysis; Software Safety Assessment. Istnieją również inne metody analizy zagrożeń stosowane w różnych dziedzinach przemysłu (np. spożywczego, chemicznego, samochodowego). W inżynierii oprogramowania najczęściej wykorzystywaną metodą jest FMEA lub jej odmiany (SFMEA, FMECA). W analizie zagrożeń wyróżnia się trzy kluczowe pojęcia: zagrożenia (ang. hazard), awarii (ang. mishap) oraz ryzyka (ang. risk). W celu uzyskania bezpiecznego systemu zagrożenia muszą być wyeliminowane lub łagodzone (redukcja ryzyka). Identyfikacja zagrożeń jest najważniejszą czynnością dla osiągnięcia tego celu. Awaria to inaczej wypadek lub nieplanowane zdarzenie skutkujące śmiercią, obrażeniami, utratą zdrowia, stratą sprzętu lub zanieczyszczeniem środowiska. Zagrożenie to każda rzeczywista lub potencjalna okoliczność powodująca powstanie ryzyka awarii. Ryzyko to wpływ oraz możliwość awarii wyrażona w terminach jej uciążliwości (ang. severity) oraz prawdopodobieństwa wystąpienia. Aby dobrze zrozumieć różnicę oraz zależności między zagrożeniem a awarią, można wyobrazić sobie te dwa pojęcia jako dwa odrębne stany tego samego zjawiska: stan „przed” i stan „po”. Przejście z jednego do drugiego, czyli z zagrożenia do awarii następuje w wyniku zaistnienia czynników ryzyka. Sytuację tę zilustrowano na rysunku 19.18. Awarie to zmaterializowane zagrożenia.

Rysunek 19.18. Związek między zagrożeniem, ryzykiem a awarią Ryzyko awarii oblicza się tak, jak to zdefiniowaliśmy wcześniej – jako iloczyn wpływu oraz prawdopodobieństwa. Różnica między metodami typu SST czy PRisMa a analizą zagrożeń jest dosyć subtelna. W analizie zagrożeń nie poprzestajemy na określeniu ryzyk, ale szukamy głębszych przyczyn, najlepiej przyczyn źródłowych powodujących wystąpienie tych ryzyk. Zagrożeniu przypisuje się prawdopodobieństwo 0 lub 1, bo albo zagrożenie występuje, albo nie. Wystąpienie awarii z kolei ma prawdopodobieństwo między 0 a 1. Jego wielkość określana jest na podstawie tzw. czynników zagrożeń (ang. Hazard Casual Factor, HCF), którymi są elementy zagrożeń (ang. Hazardous Elements, HE) oraz mechanizmy inicjujące (ang. Initiating Mechanisms, IM). Przykładem elementu zagrożeń może być fragment kodu o wysokim stopniu złożoności, a mechanizmem inicjującym – wywołanie tego kodu w określonych okolicznościach.

19.9.7. Koszt ekspozycji ryzyka (cost of exposure) Koszt ekspozycji ryzyka (ang. cost of exposure) to inaczej ilościowe określanie poziomu ryzyka. Przykład jej zastosowania pokazaliśmy wcześniej w podrozdziale 19.1 i punkcie 19.6.5. Koncepcja ta została zaczerpnięta ze świata finansów. Wielu ekspertów stoi na stanowisku, że o ile w finansach czy bankowości metoda ta jest bardzo pomocna, o tyle w inżynierii jakości oprogramowania ilościowe szacowanie ryzyka nie jest zbyt skuteczne i jako takie jest działaniem niezalecanym. Tak naprawdę powodzenie metody zależy od

stopnia precyzji, z jaką jesteśmy w stanie określić stratę poniesioną w wyniku zaistnienia

ryzyka

oraz prawdopodobieństwo

jego

wystąpienia. Niestety,

w praktyce szacunki te są zwykle obarczone zbyt dużym błędem. Niezależnie od tych krytycznych uwag, koszt ekspozycji jest metodą przydatną do wyboru czynności łagodzących ryzyko, stosowaną w tzw. analizie zysków i kosztów. Boehm [180] zdefiniował pojęcie wpływu redukcji ryzyka (ang. Risk Reduction Leverage, RRL) pozwalające ilościowo ocenić stosunek zysków do kosztów wynikający z przeprowadzenia danych akcji łagodzących. Miara ta wyraża zysk z inwestycji (ang. Return On Investment, ROI) polegającej na redukcji określonego ryzyka przy użyciu wartości oczekiwych, czyli koszt ekspozycji. RRL jest definiowany jako różnica między kosztem ekspozycji ryzyka (ang. – Risk Exposure, RE) przed zastosowaniem czynności łagodzących i po nim podzielona przez koszt tych czynności:

Rozważmy prosty przykład. Załóżmy, że zidentyfikowaliśmy ryzyko R o prawdopodobieństwie 25% i wpływie (stracie) w wysokości 300 000 USD i że możemy je złagodzić na trzy sposoby: 1) stosując dodatkową fazę testów – koszt to 150 000 USD; czynność ta redukuje prawdopodobieństwo do 4%, nie zmniejszając wartości straty; 2) dokonując transferu ryzyka i stosując kary umowne – koszt transferu to 20 000 USD; prawdopodobieństwo się nie zmieni, ale ze względu na kary umowne nastąpi redukcja wpływu do poziomu 180 000 USD; 3) przeprowadzając inspekcję – jej koszt to 2000 USD; powoduje ona redukcję prawdopodobieństwa do 15%, nie zmniejszając wartości wpływu. Wyniki analizy wpływu redukcji ryzyka są przedstawione w tabeli 19.16. Wartości REpo są obliczane jako iloczyny prawdopodobieństw i wpływów poszczególnych ryzyk. Znając koszt każdego wariantu oraz dane dotyczące oryginalnego ryzyka, możemy obliczyć RRL dla każdego z wariantów. Na przykład dla wariantu 1 wynosi on

Tabela 19.16. Wpływ redukcji ryzyka

Ryzyko

Prawdopodobieństwo przed

W pływ przed RE przed [USD] [USD]

R

0,25

300 000

75,000

Alternatywy: Prawdopodobieństwo po

W pływ po

RE po

Koszt

Wariant 1

0,04

300 000

12 000

150 000

Wariant 2

0,25

180 000

45 000

20 000

Wariant 3

0,20

300 000

60 000

2 000

Jeśli współczynnik RRL jest mniejszy od jedności, to odpowiedni wariant jest dla nas opłacalny, jeśli większy od jedności – nie jest opłacalny. Im bardziej RRL jest oddalony na lewo od 1, tym większy zysk z wdrożenia danego wariantu. Z naszej analizy wynika, że opłaca się wdrożyć czynności łagodzące ryzyko w wariancie pierwszym, czyli stosując dodatkową fazę testów.

19.9.8. FMEA (Failure Mode and Effect Analysis) Failure Mode and Effect Analysis (FMEA), czyli analiza przyczyn i skutków awarii to systematyczna metoda służąca do rozpoznania i oceny potencjalnych awarii systemu oraz poziomu tych zagrożeń. Została ona wprowadzona w 1949 roku przez armię Stanów Zjednoczonych jako formalna technika analizy i obecnie jest zdefiniowana jako standard MIL-STD-1629A [181]. Jest to metoda typu bottom-up, gdzie analizę ryzyk rozpoczyna się od najniższego poziomu obejmującego najdrobniejsze detale i na tej podstawie dokonuje ogólnej oceny powodzenia całego projektu lub ryzyk związanych z częścią systemu, która jest poddana analizie. FMEA ma możliwość włączenia do analizy wskaźników ryzyk (ang. failure rate) dla każdej potencjalnej awarii, co pozwala na przeprowadzenie ilościowej, probabilistycznej analizy. Technika ta umożliwia również analizę trybów awarii powodujących przejście systemu w niepożądany stan, co daje możliwość traktowania jej jako narzędzia do analizy zagrożeń. FMECA (Failure Mode, Effects

and Criticality Analysis) jest odmianą FMEA, w której przeprowadza się bardziej szczegółową analizę przyczyn awarii, uwzględniając w niej poziom krytyczności oraz ocenę sposobów wykrywania potencjalnych awarii. analiza przyczyn i skutków awarii (oprogramowania) (ang. (Software) Failure Mode and Effect Analysis, (S)FMEA) – systematyczne podejście do identyfikacji i analizy ryzyka polegające na wskazywaniu możliwych trybów (ang. mode) awarii i metod zapobiegających ich wystąpieniu lub łagodzących ich wystąpienia analiza przyczyn, skutków i krytyczności awarii (oprogramowania) (ang. – (Software) Failure Mode, Effect and Criticality Analysis, (S)FMECA) – rozszerzenie FMEA o analizę krytyczności, która jest używana do wyznaczania prawdopodobieństw wystąpienia stanów awarii z uwzględnieniem poziomu ich dotkliwości; wynikiem jest uwypuklenie stanów awarii z odpowiednio wysokim

prawdopodobieństwem

i

dotkliwością,

co

pozwala

na

ukierunkowanie działań zapobiegawczych tam, gdzie przyniosą największe korzyści Celem FMEA jest ocena efektów każdej możliwej przyczyny awarii tak, aby stwierdzić czy istnieje konieczność wprowadzenia zmian projektowych do określonych funkcjonalności ze względu na nieakceptowalny poziom niezawodności, bezpieczeństwa czy jakości działania systemu. FMEA oryginalnie została zaprojektowana do oceny wpływu awarii na niezawodność, ale w praktyce jest używana do identyfikowania wszelkich zagrożeń wynikających ze zidentyfikowanych trybów awarii. Nie zaleca się stosowania FMEA do analizy bezpieczeństwa, ponieważ metoda ta nie uwzględnia awarii powstałych w wyniku zaistnienia kombinacji różnych czynników. W takim przypadku lepszą metodą jest np. Fault Tree Analysis omówiona w punkcie 19.9.12. Przeprowadzenie FMEA pozwala na udzielenie odpowiedzi na następujące pytania [182]: co może ulec awarii? w jaki sposób może to ulec awarii? jak często będzie to ulegało awarii? jakie są konsekwencje awarii?

jak awaria wpływa na niezawodność/bezpieczeństwo systemu? W ogólnym zarysie FMEA składa się z następujących kroków: 1. Zdefiniowanie analizowanego systemu. Aby przeprowadzać jakąkolwiek analizę, musimy dokładnie znać system, jego architekturę, części składowe, wewnętrzne i zewnętrzne interfejsy itd. 2.

Zdefiniowanie

możliwych

typów

awarii

oraz

oszacowanie

ich

częstotliwości. 3. Analiza systemu. Identyfikacja potencjalnych przyczyn awarii oraz ich efektów. 4. Identyfikacja metod detekcji awarii oraz działań korekcyjnych, naprawczych bądź prewencyjnych. 5. Określenie przyczyn awarii, ich konsekwencji, predykcja niezawodności, lista zagrożeń i ryzyk, lista krytycznych elementów (ang. Critical Item List, CIL). FMEA przeprowadzana jest zwykle przy użyciu arkusza kalkulacyjnego, w którym uzupełnia się tabelę FMEA. Istnieje wiele różnych wersji takiej tabeli, które różnią się poziomem szczegółowości niektórych informacji. Tabela używana przez nas w przykładzie w punkcie 19.9.9 będzie zawierała następujące informacje: nazwa funkcji, w której może wystąpić awaria; możliwa awaria (ryzyko produktowe); możliwa przyczyna awarii; konsekwencje awarii; Pr. – prawdopodobieństwo wystąpienia awarii (ang. likelihood); W. – wpływ; Dotkl. – dotkliwość (ang. severity); w niektórych wersjach FMEA ten parametr się pomija; RPN – priorytet ryzyka (ang. Risk Priority Number), definiowany jako iloczyn: RPN = Pr ⋅ W ⋅ Dotkl;

metoda wykrywania i zalecane czynności. Prawdopodobieństwo, wpływ oraz dotkliwość można wyrażać na skali porządkowej. Najczęściej stosuje się tu skalę typu Likerta (od 1 do 5, gdzie 1 oznacza efekt znikomy, a 5 – olbrzymi). Priorytet ryzyka jest formalnie ujęty na skali interwałowej, ponieważ jest wynikiem mnożenia trzech liczb. Wartość współczynnika RPN pozwala na priorytetyzację zidentyfikowanych i oszacowanych ryzyk.

19.9.9. Przykład: zastosowanie FMEA do systemu ELROJ W przykładzie tym nie przeprowadzimy pełnej analizy, ponieważ zajęłoby to zbyt dużo miejsca. Przykład ma tylko ilustrować zastosowanie metody. W rzeczywistych projektach produktem wyjściowym FMEA jest bardzo obszerna lista awarii. Aby zastosować FMEA do systemu ELROJ (patrz Dodatek A), najpierw musimy przeanalizować system i jego strukturę. Pomocny w tym będzie wysokopoziomowy projekt architektury systemu ELROJ, przedstawiony na rysunku A.2 (s. 1011). ELROJ składa się z następujących komponentów: system plików; baza danych; aplikacja; GUI; ekran. Aplikacja ma różne funkcjonalności, takie jak: zarządzanie komunikatami; zarządzanie liniami autobusowymi; zarządzanie kursami; wyświetlanie informacji na ekranie; ustawianie daty. Dla każdego z komponentów i dla każdej z funkcji określamy możliwe przyczyny i skutki awarii wraz z oceną prawdopodobieństwa, wpływu i dotkliwości. Przykładowy wynik takiej analizy pokazany jest w tabeli 19.17.

Z przeprowadzonej analizy wynika, że ryzyka: fizycznego uszkodzenia systemu plików, braku komunikacji z ekranem, awarii ekranu oraz wyświetlanie błędnej informacji są awariami o największym priorytecie. Identyfikacja możliwych przyczyn pozwala na przeprowadzenie czynności, które powinny złagodzić lub wyeliminować te zagrożenia. FMEA pokazuje nam dużą rolę inspekcji formalnych oraz czynności związanych z kontrolami okresowymi infrastruktury. Okazuje się, że – przynajmniej w tym niewielkim, uproszczonym przykładzie – błędy związane z oprogramowaniem wcale nie są najważniejsze. Analiza pozwoliła nam dostrzec dużą rolę sprzętu i komunikacji w zapewnianiu jakości całego systemu. Ryzyka zduplikowania kursu oraz błędu zapisu do bazy danych mają najniższy priorytet ze względu na niskie prawdopodobieństwo ich wystąpienia lub zaniedbywalny poziom wpływu bądź dotkliwości. Akcje z nimi związane możemy zostawić na sam koniec lub – w przypadku braku czasu – opuścić. Tabela 19.17. FMEA dla programu ELROJ

Funkcja

Możliwa awaria

Możliwa przyczyna awarii

S ys tem plików

Fizyczne us zkodzenie

Mechaniczna Utrata danych

5

1

1

S ys tem plików

Błąd odczytu

Wyś wietlenie Błąd aplikacji niepoprawnej informacji

4

3

2

Baza danych

Błąd zapis u

Narus zenie klucza

5

3

3

Konsekwencje Pr. W . Dotk awarii

Brak możliwoś ci

operowania

Zarządz. kurs ami

Redundantna informacja Możliwoś ć Błąd aplikacji w bazie oraz – zduplikowania w module 2 być może – na kurs u addBus ekranie informacyjnym

Informacja Wyś wietl. niepełna lub informacji błędna

Zmniejs zenie Błąd aplikacji użytecznoś ci dla pas ażera

5

4

3

3

1

Ekran

Brak komunikacji z ekranem

Błąd s ieci

Irytacja pas ażerów

3

2

1

Ekran

Awaria ekranu

Mechaniczne Irytacja us zkodzenie pas ażerów

4

2

1

19.9.10. QFD (Quality Function Deployment) Quality Function Deployment po polsku tłumaczy się jako „wdrożenie funkcji jakości”, „rozwinięcie funkcji jakości” czy „dopasowanie funkcji jakości”. Jest to metodologia oparta na pracy interdyscyplinarnego zespołu służąca do wdrożenia „głosu klienta” (ang. voice of the customer) do produktu. Innymi słowy, chodzi o przełożenie wymagań, potrzeb i oczekiwań klientów na charakterystyki tworzonego systemu lub oprogramowania. Technika ta została po raz pierwszy

zastosowana w 1972 roku w Japonii, w koncernie Mitsubishi. W ciągu kilku lat zdobyła popularność również w Stanach Zjednoczonych, gdzie z powodzeniem została wdrożona w takich firmach jak Ford, General Motors, Hewlett-Packard, AT&T czy ITT. Metoda nie była oryginalnie zaprojektowana pod kątem rozwiązań informatycznych, ale można ją zaadaptować do procesu wytwarzania oprogramowania. Jako system zarządzania jakością, QFD charakteryzuje się następującymi cechami [183]: wykorzystuje elementy myślenia systemowego (tzn. traktuje proces wytwarzania jako system) oraz psychologię (zrozumienie potrzeb klienta, jego pojęcia „wartości” czy „jakości” produktu); jest oparta na wiedzy i poznaniu (skąd wiemy, jakie są potrzeby użytkownika? w jaki sposób decydować o tym, które cechy systemu zaimplementować?); uwzględnia aspekt konkurencyjności na poziomie strategicznym (umożliwia uzyskanie przewagi konkurencyjnej przez poznanie świadomych i nieświadomych potrzeb klienta, ich właściwego przetłumaczenia na język techniczny i priorytetyzacji oraz optymalizacji najistotniejszych cech systemu); jest całościowym systemem ukierunkowanym na zadowolenie klienta w ciągu całego cyklu produkcji systemu. Jako metoda zapewniająca właściwe wdrożenie potrzeb jakościowych klienta do produktu, QFD jest techniką opartą na ryzyku w tym sensie, że zwiększenie zadowolenia klienta jest równoważne z redukcją ryzyka związanego z jakością użytkową oraz jakością produktu (patrz rozdz. 15 i 16). QFD dowiodła swej skuteczności zarówno w osiąganiu celów krótkookresowych, jak i strategicznych. Jako technika pracy zespołowej przyczynia się do redukcji barier między różnymi zespołami w organizacji (np. analitycy vs. deweloperzy vs. testerzy vs. marketingowcy). Jeśli chodzi o korzyści długookresowe, QFD przez dostarczenie precyzyjnej informacji o potrzebach klienta i o tym, jak należy je przełożyć na „język techniczny” przyczynia się do skrócenia cyklów wytwórczych, obniżenia kosztów wytwarzania oprogramowania oraz zwiększenia produktywności [184]. Metoda QFD jest znana głównie z jednego ze stosowanych w niej narzędzi, mianowicie tzw. domu jakości (ang. House Of Quality, HOQ). Jest to macierz

reprezentująca zależności między wymaganiami klienta a charakterystykami technicznymi produktu, nazwana tak ze względu na swoją postać, kształtem przypominającą dom. Nie należy jednak utożsamiać QFD z domem jakości. QFD składa się z czterech zasadniczych faz: 1.

Dokumentowanie

wymagań

klienta,

możliwości

zwiększenia

konkurencyjności, miar produktu, miar produktów konkurencji oraz technicznych możliwości organizacji dla spełnienia potrzeb klienta. Otrzymanie dobrej jakości danych od klienta jest kluczowe dla powodzenia całego procesu QFD. Krok ten zwykle jest przeprowadzany przez dział marketingu. 2. Faza przeprowadzana przez dział techniczny. Tworzone są wstępne koncepcje produktu oraz częściowe specyfikacje. Części systemu uznane za najważniejsze z punktu widzenia klienta są przekazywane do kolejnej fazy – procesu planowania. 3. Proces planowania – w przypadku tworzenia oprogramowania krok ten przeprowadzany jest przez kierowników zespołów przy udziale pracowników działu jakości. Definiowane są procesy oraz ich parametry. 4. Faza planowania produkcji – określane są w niej wskaźniki służące do pomiaru procesu produkcji i harmonogramów. W tej fazie wykonuje się również czynności związane z analizą ryzyka – ocenia się procesy pod kątem tego, jak duże niosą ze sobą ryzyko i wdraża się proces monitorowania i nadzoru w celu łagodzenia tych ryzyk. Krok ten jest prowadzony wspólnie przez dział jakości oraz zespół wytwórczy.

Rysunek 19.19. Macierz QFD (tzw. dom jakości) Głównym elementem analitycznym QFD jest wspomniana już macierz jakości, przedstawiona na rysunku 19.19. Składa się na nią 9 elementów: I) wymagania, potrzeby i oczekiwania klienta; II) istotność każdego z wymagań wraz z oceną porównawczą konkurencji; III) charakterystyki techniczne tworzonego produktu;

IV) powiązania między potrzebami klienta a charakterystykami technicznymi; V) względna ocena charakterystyk technicznych; VI) korelacje charakterystyk technicznych, pokazujące zgodność lub antagonizm między charakterystykami; VII) pożądane atrybuty charakterystyk technicznych; VIII) techniczna ocena porównawcza z produktami konkurencji; IX) dodatkowe wymagania nałożone na produkt, związane z regulacjami prawnymi, kwestiami bezpieczeństwa itp.

19.9.11. Przykład: zastosowanie QFD do systemu ELROJ Przedstawimy teraz uproszczoną analizę QFD oraz pokażemy na tym przykładzie jej zalety. Załóżmy, że mamy dwóch klientów systemu ELROJ: bezpośredniego (operator systemu) oraz pośredniego (pasażer). Z klientem bezpośrednim pracownik działu marketingu wraz z analitykiem biznesowym przeprowadzili wywiad, w wyniku którego okazało się, że dla operatora dwie najważniejsze cechy systemu to łatwość obsługi oraz kontrola błędów (np. próba usunięcia nieistniejącego kursu lub dodania już istniejącej linii). Klient pośredni nie jest konkretną osobą, a co więcej klientów takich jest bardzo wielu, dlatego dział marketingu zdecydował się na przeprowadzenie ankiety wśród losowo wybranych użytkowników komunikacji miejskiej. Z ankiet wynika, że pasażerowie chcą przede wszystkim mieć szybką i aktualną informację na ekranach stojących na przystankach autobusowych. Wyniki ankiety zostały przekazane analitykom biznesowym, działowi jakości oraz deweloperom. Uznano za logiczne, że pasażerowie są ważniejsi od operatora systemu, dlatego potrzeby pasażerów mają wyższy priorytet niż potrzeby operatora (aktualna informacja = 4, szybkość = 3, łatwość obsługi = 2, kontrola błędów = 1). Następnie analitycy biznesowi zdefiniowali pięć charakterystyk technicznych systemu ELROJ: ergonomiczny interfejs, obsługę wyjątków, wydajny kod i bazę danych, szybkie łącze internetowe oraz wysokiej jakości interfejs programowo-sprzętowy do komunikacji między programem a ekranem. Stwierdzono, że ta ostatnia cecha koreluje słabo pozytywnie z szybkością łącza oraz z obsługą wyjątków, a cecha „wydajny kod i baza” koreluje negatywnie z obsługą wyjątków (im więcej kontroli, tym niższa efektywność) oraz pozytywnie z ergonomicznością interfejsu. Korelacje oznaczono, używając skali: –6, –3, –1, 1, 3, 6, gdzie –6 oznacza silnie negatywną, a 6 – silnie pozytywną korelację.

Wartości te oraz zależności przedstawiony na rysunku 19.20.

zostały

naniesione

na

„dom

jakości”

Ocenę poszczególnych wymagań klienta zdefiniowano jako sumę wartości znajdujących się w odpowiednim wierszu macierzy relacji wymaganiacharakterystyki, ważoną priorytetem wymagania. Na przykład ocena łatwości obsługi błędów wynosi 2 ⋅ (6 + 3 + 1) = 20. Znaczenie charakterystyk zdefiniowano jako sumę wartości z odpowiedniej kolumny tej samej macierzy ważonych przez priorytety odpowiadających im wymagań. Na przykład znaczenie charakterystyki „obsługa wyjątków” wynosi 3 ⋅ 2 + 6 ⋅ 1 + 3 ⋅ 4 = 6 + 6 + 12 = 24. Na podstawie tych wartości zdefiniowano priorytety charakterystyk, szeregując je według

Rysunek 19.20. Macierz QFD dla systemu ELROJ malejącej oceny ich znaczenia. Na końcu dokonano oceny podobnych do systemu ELROJ produktów dwóch konkurencyjnych firm, analizując cechy tych produktów pod kątem wymagań użytkowników. Macierz QFD zawiera bardzo wiele istotnych informacji. Oto niektóre z nich:

Priorytetyzacja wymagań klienta – najważniejszym wymaganiem jest szybkość transmisji danych; zauważmy, że chociaż aktualność informacji była najważniejszym wymaganiem, ostatecznie znalazła się na trzecim miejscu, za łatwością obsługi GUI. Wynika to stąd, że cechy techniczne oprogramowania mają niewielki wpływ na spełnienie tego wymagania. Priorytetyzacja

cech

systemu



po

analizie

zależności

między

wymaganiami a charakterystykami technicznymi najważniejszą cechą systemu okazała się obsługa wyjątków i na nią trzeba będzie położyć duży nacisk podczas prac projektowych. Wykorzystanie informacji o korelacji – obsługa wyjątków jest silnie negatywnie zależna od wydajności kodu i bazy, która to cecha ma również wysoki priorytet. Oznacza to potencjalny konflikt między jakością tych dwóch wymagań. Porównując względne oceny tych dwóch charakterystyk, możemy ustalić poziom rozwiązania kompromisowego (ang. trade-off), kładąc nacisk na jakość poszczególnych charakterystyk proporcjonalnie do ich ocen (24 i 14). Możliwość wykorzystania benchmarkingu – produkt konkurenta A lepiej się sprawdza w dostarczaniu aktualnej informacji, a produkt B w szybkim przekazie informacji. Możemy przeanalizować odpowiednie cechy tych produktów, aby porównać je z naszym, planowanym produktem i wdrożyć w organizacji dobre/najlepsze praktyki lub rozwiązania stosowane przez te firmy w ich produktach. najlepsze praktyki, dobre praktyki (ang. best practices, good practices) – zalecane metody lub nowatorskie praktyki, które przyczyniają się do lepszych wyników organizacji w konkretnym kontekście, zwykle uznawane za „najlepsze” przez inne podobne organizacje

19.9.12. FTA (Fault Tree Analysis) Analiza drzewa awarii (ang. Fault Tree Analysis, FTA) to formalna, systematyczna metoda służąca do określania przyczyn źródłowych (ang. root cause) awarii oraz określania prawdopodobieństwa wystąpienia niepożądanych zdarzeń. Technika ta sprawdza się bardzo dobrze w przypadku dużych, skomplikowanych

i złożonych systemów oraz systemów o znaczeniu krytycznym – pomaga w zrozumieniu i zapobieganiu potencjalnych problemów. Drzewo awarii to model reprezentujący w sposób graficzny zależności między kombinacjami

zdarzeń (zarówno

awaryjnych, jak

i

„normalnych”)

przy

wykorzystaniu aparatu logiki formalnej (a ściślej rzecz ujmując, bramek logicznych). Analiza drzewa awarii jest – w przeciwieństwie do metod typu bottom-up takich jak np. FMEA – metodą dedukcyjną. Wychodzi ona od głównego, podstawowego problemu, a następnie, za pomocą metody „top-down” uszczegóławia model, odnajdując przyczyny aktualnie zidentyfikowanych zdarzeń. Metoda schodzi więc w głąb, tworząc coraz głębsze poziomy drzewa awarii, docierając w końcu na poziom najniższy, reprezentujący przyczyny źródłowe. analiza drzewa usterek, analiza drzewa usterek oprogramowania (ang. Fault Tree Analysis, FTA, Software Fault Tree Analysis) – metoda używana do analizy przyczyn usterek (defektów) modelująca wizualnie, w jaki sposób związki logiczne między awariami, błędami człowieka i zewnętrznymi zdarzeniami mogą powodować powstawanie specyficznych defektów FTA jest metodą ilościową (choć można też wykorzystywać ją w sposób jakościowy). Określając prawdopodobieństwa zajścia poszczególnych zdarzeń i tworząc całe drzewo awarii, możemy określić: przyczyny źródłowe awarii; prawdopodobieństwo

wystąpienia

głównej

awarii

(zdarzenia

reprezentowanego wierzchołkiem drzewa); prawdopodobieństwo i znaczenie grup zdarzeń (tzw. zbiorów przekrojowych, ang. cut sets), których zajście powoduje wystąpienie głównej awarii; znaczenie ryzyka poszczególnych komponentów systemu; tzw. ścieżki awarii wysokiego ryzyka (ang. high risk fault paths) oraz mechanizmy ich działania. Wyniki FTA można reprezentować w czytelny, graficzny sposób i wykorzystywać je w komunikowaniu oraz wspieraniu decyzji dotyczących łagodzenia ryzyk. FTA może być użyta w dowolnym etapie cyklu życia, jednak

zaleca się, aby przeprowadzać ją odpowiednio wcześnie, najlepiej na wczesnym etapie projektowania. Notacja FTA jest bogata, ale na potrzeby prezentacji w książce będziemy wykorzystywać uproszczoną symbolikę. Szczegółowe informacje o metodzie, wraz z wykorzystaniem wszystkich jej możliwości Czytelnik znajdzie np. w [182]. Drzewo awarii składa się z wierzchołków oraz krawędzi. Wierzchołki stanowią zdarzenia, a z każdym z nich (oprócz zdarzeń w liściach drzewa) jest związana bramka logiczna połączona ze zdarzeniami na niższym poziomie. Zwykle w FTA wykorzystuje się tylko dwa typy bramek logicznych: AND i OR. Na rysunku 19.21 są przedstawione graficzne oznaczenia trzech bramek logicznych. Oprócz bramek AND i OR jest jeszcze pokazana tzw. bramka głosująca (ang. voting gate) typu m/n.

Rysunek 19.21. Notacja drzew awarii

Bramka AND wyśle na wyjściu sygnał tylko wtedy, gdy otrzyma na wejście sygnały z wszystkich zdarzeń do niej wchodzących. Bramka OR wyśle na wyjściu sygnał wtedy, gdy otrzyma sygnał wejściowy od co najmniej jednego zdarzenia wejściowego. Bramka głosująca m/n ma n wejść i wysyła sygnał wyjściowy tylko wtedy, gdy otrzyma co najmniej m spośród wszystkich n możliwych sygnałów wejściowych. W środkowej części rysunku 19.21 są pokazane dwa proste drzewa awarii. W lewym drzewie zdarzenie A połączone jest ze zdarzeniami B i C bramką AND. Zdarzenia A zajdzie tylko wtedy, gdy jednocześnie nastąpią zdarzenia B i C. Jeśli prawdopodobieństwa zajścia zdarzeń B i C wynoszą odpowiednio PB oraz PC, to prawdopodobieństwo wystąpienia zdarzenia A wynosi PA = PB ⋅ PC, przy czym zakłada się, że wystąpienia zdarzeń B i C są od siebie niezależne. W drzewie po prawej stronie mamy zdarzenie D połączone bramką OR ze zdarzeniami E i F. Jeśli prawdopodobieństwa zajścia zdarzeń E i F wznoszą odpowiednio PE oraz PF , to

prawdopodobieństwo zdarzenia D, z reguły włączeń i wyłączeń (patrz Dodatek C), wyniesie PD = PE + PF – PE ⋅ PF . Tak jak poprzednio, zakładamy, że zdarzenia E i F są od siebie niezależne. Gdyby zdarzenie D było połączone bramką OR z trzema zdarzeniami: E, F i G, to wtedy PD=PE+ PF + PG – PE ⋅ PF – PE ⋅ PG – PF ⋅ PG + PE ⋅ PF ⋅ PG . Ogólny wzór, dla dowolnej liczby składników, jest podany w Dodatku C.

Tworzenie drzewa awarii jest procesem iteracyjnym. Rozpoczyna się stworzeniem korzenia drzewa, reprezentującego główną awarię, a następnie rozbudowuje drzewo o kolejne poziomy. Przy tworzeniu każdej bramki logicznej obowiązuje ta sama logika i jest wykorzystywany ten sam zbiór pytań. Dla każdego zdarzenia X należy zidentyfikować zbiór zdarzeń, których zajście może wywołać X oraz zdefiniować sposób zajścia tego zdarzenia (czyli połączyć zdarzenia będące dziećmi X odpowiednią bramką logiczną). W każdym momencie tworzenia drzewa, przy identyfikacji zdarzeń odpowiadających zdarzeniu X wykorzystuje się trzy zasady: zasadę I-N-S – czyli udzielenie odpowiedzi na pytanie: co jest natychmiastowe (ang. immediate), konieczne (ang. necessary) i wystarczające (ang. sufficient) do zajścia zdarzenia X? zasadę SS-SC – czyli rozróżnienie, czy zdarzenie jest stanem systemu (ang. State-of-the-System), czy też stanem komponentu (ang. State-of-theComponent). W przypadku SC odpowiadającą bramką będzie OR.

W

przypadku

SS

zdarzenie

będzie

podlegać

dalszej

analizie

z wykorzystaniem zasady I-N-S, aby określić wejścia oraz typ bramki. zasadę P-S-C – czyli udzielenie odpowiedzi na pytanie: jakie są podstawowe (ang. primary), zewnętrzne (ang. secondary) oraz tzw. polecone (ang. command) powody zdarzenia? Pytanie to zmusza analityka do skupienia się na określonych czynnikach sprawczych. Każdy komponent może ulec awarii tylko na te trzy sposoby: podstawowy tryb awarii, zewnętrzny tryb awarii lub przez tzw. ścieżkę polecenia (ang. command path failure). Jeśli wystąpią co najmniej dwa spośród trzech typów P, S, C, to automatycznie stosuje się bramkę OR. Oznaczenia podstawowego oraz zewnętrznego trybu awarii pokazane są w dolnej części rysunku 19.21. Podstawowa awaria to wewnętrzna, inherentna awaria danego komponentu lub modułu (np. wykonanie błędnej instrukcji). Awarie podstawowe są od siebie niezależne, ze względu na swą inherentność. Zewnętrzna awaria jest spowodowana zewnętrznymi czynnikami (np. atak hakerski na oprogramowanie). Te awarie są od siebie zależne, bo np. atak hakerski może spowodować awarię więcej niż jednego komponentu. Awarie polecone są spowodowane przez zamierzone działania, które odbyły się w niewłaściwym czasie, np. za wcześnie lub za późno. Jeśli np. proces okresowego

tworzenia

kopii

zapasowej

uruchomi

się

przed

wylogowaniem użytkownika zamiast po, to użytkownik może po stworzeniu kopii wykonać jeszcze jakieś działania w systemie, które zostaną utracone przez wylogowanie go. Drzewa awarii pozwalają na określenie specyficznych zbiorów zdarzeń, zwanych zbiorami przecinającymi, pomocnych w analizie ryzyka. Zbiór przecinający to zbiór zdarzeń, których jednoczesne zajście powoduje wystąpienie głównej awarii. Z kolei minimalny zbiór przecinający to najmniejszy możliwy (w sensie liczby elementów) zbiór przecinający. Obliczanie minimalnych zbiorów przecinających wymaga znajomości algebry Boola i zagadnień związanych z minimalizacją wyrażeń logicznych. Więcej informacji na ten temat Czytelnik znajdzie w Dodatku C. Rozważmy drzewo awarii z rysunku 19.22. Modeluje ono zajście awarii głównej B1.

Rysunek 19.22. Przykładowe drzewo awarii Chcemy obliczyć zbiory przecinające i minimalne zbiory przecinające dla tego drzewa. Awaria B1 zajdzie przy jednoczesnym zajściu B2 i B3. Z kolei B2 zajdzie przy zajściu przynajmniej jednego spośród zdarzeń: A i B4. Analizując całe drzewo w ten sposób, możemy obliczyć algebraiczny warunek na zajście B1: B1 = B2 ⋅ B3 = (A + B4) ⋅ (C + B5) = (A + B + C) ⋅ (C + A⋅B) = AC + AAB + BC + BAB + CC + CAB = AC + AB + BC + AB + C + ABC

= AC + AB + BC + C + ABC Każdy ze zbiorów {A, C}, {A, B}, {B, C}, {C}, {A, B, C} jest zbiorem przecinającym. Na przykład wystąpienie samego zdarzenia C spowoduje – niezależnie od zajścia A oraz B – zajście B4 i B3. Zajście B4 spowoduje zajście B2, a jednoczesne zajście B2 i B3 powoduje zajście awarii głównej B1. Aby obliczyć minimalny zbiór

przecinający, musimy

zminimalizować

obliczone wyrażenie: AC + AB + BC + C + ABC = AB(1 + C) + C(A + B + 1) = AB + C Minimalnymi zbiorami przecinającymi są więc zbiory {A, B} oraz {C} Jeśli zdarzeniom przypiszemy prawdopodobieństwa (współczynniki niezawodności), z jakimi zachodzą, to możemy obliczyć prawdopodobieństwo awarii głównej, używając podobnego aparatu, jak przy wyliczaniu zbiorów przecinających, tyle, że tym razem będziemy działać na prawdopodobieństwach. Załóżmy, że niezawodności komponentów A, B i C wynoszą odpowiednio 0,9, 0,8 i 0,6, zatem prawdopodobieństwa ich awarii wynoszą odpowiednio 0,1, 0,2 i 0,4. Wykorzystując wzory na obliczanie prawdopodobieństw zdarzeń dla bramek AND i OR, mamy PB1 = PB2 ⋅ PB3 = (PA + PB4 – PA PB4)(PC + PB5 – PC PB5) = (PA + (PB + PC – PB PC) – PA (PB + PC – PB PC)) ⋅ (PC + (PA + PB – PA PB) – PC (PA + PB – PA PB)) = (0,1 + (0,2 + 0,4 – 0,08) – 0,1(0,2 + 0,4 – 0,08)) ⋅ (0,4 + (0,1 + 0,2 – 0,02) – 0,4(0,1 + 0,2 – 0,02)) = (0,62 – 0,052)(0,68 – 0,112) = 0,568 ⋅ 0,568 ≈ 0,322 W przypadku dużych drzew awarii, w których bramki mają wiele wejść, dokładne obliczenie prawdopodobieństwa staje się bardzo trudne lub wręcz niemożliwe. W takich przypadkach stosuje się podejście Monte Carlo, polegające na wielokrotnym losowaniu awarii poszczególnych komponentów, zgodnie z zadanymi prawdopodobieństwami, badając, czy w danym przypadku główna awaria nastąpiła, czy nie. Jeśli wśród N takich prób M zakończyło się

wystąpieniem awarii głównej, to szacuje się prawdopodobieństwo zajścia awarii głównej jako P = M/N.

19.9.13. Przykład: zastosowanie FTA do systemu ELROJ Załóżmy, że modelujemy awarię główną „niepoprawne działanie ekranu” w systemie ELROJ opisanym w Dodatku A. Zidentyfikowano następujące 4 bezpośrednie, wystarczające przyczyny niepoprawnego działania ekranu: awaria mechaniczna ekranu, brak zasilania, błąd transferu danych, błąd programu. Są to przyczyny wystarczające, dlatego łączymy je bramką OR. Brak zasilania oraz błąd transferu są awariami podstawowymi, w związku z czym stają się liśćmi drzewa awarii. Aby nastąpiła awaria mechaniczna, konieczny jest skok napięcia oraz awaria bezpiecznika. Konieczność tych warunków skutkuje połączeniem ich bramką AND, a fakt, że jedna jest awarią podstawową a druga zewnętrzną sprawia, że stają się one liśćmi drzewa. Błąd programu może być spowodowany błędnym działaniem funkcji getDisplayString lub błędem interfejsu program-ekran. Obie awarie są podstawowe, zatem stają się liśćmi drzewa. Ostateczna postać drzewa jest przedstawiona na rysunku 19.23.

Rysunek 19.23. Drzewo awarii dla systemu ELROJ Z danych historycznych wiemy, że poszczególne awarie występują w określonym przedziale czasu z następującymi prawdopodobieństwami: brak zasilania: 0,001;

błąd transferu danych: 0,005; skok napięcia: 0,02; awaria bezpiecznika: 0,003; błędne działanie funkcji getDisplayInfo4: 0,08; błąd interfejsu program-ekran: 0,05. Przyjmując

za

P,

Q,

R,

S odpowiednio

prawdopodobieństwa: awarii

mechanicznej ekranu, braku zasilania, błędu transferu danych oraz błędu programu, możemy obliczyć prawdopodobieństwo awarii głównej, korzystając ze wzoru włączeń-wyłączeń jako: Pr = P + Q + R + S – PQ – PR – PS – QR – QS – RS + PQR + PQS + PRS + QRS – PQRS. Łatwo policzyć, że P = 0,00006, S = 0,126, więc otrzymujemy Pr ≈ 0,131. Zamiast dokładnych rachunków moglibyśmy przeprowadzić estymację tego prawdopodobieństwa

metodą

Monte Carlo. Uruchamiając prosty

skrypt5

w języku R Statistical Package, przedstawiony na listingu 19.1, przeprowadziliśmy sto tysięcy prób Monte Carlo i otrzymaliśmy wynik 0,1332, który – jak widać – jest bliski wynikowi prawdziwemu.

liczbaAwarii = 0 N = 100000 for (i in 1:N) { if ((runif(1) Zarządzanie -> Raporty). Raporty s ą dos tępne tylko dla kadry zarządzającej. 21. Personel. Zes pół tes towy w projekcie ELROJ będzie s ię s kładać z: kierownika tes tów (podleg a Kierownikowi Projektu); analityka tes tów (podleg a kierownikowi tes tów; zadania: analiza wymag ań, uczes tniczenie w ins pekcji dokumentów i kodu, projektowanie tes tów, ws półpraca z kierownikiem tes tów w zakres ie raportowania i monitorowania pos tępów); 2 tes terów (podleg ają kierownikowi tes tów; zadania: automatyzacja wykonania tes tów przez tworzenie s kryptów tes towych, tworzenie uprzęży tes towych, przeprowadzanie tes tów niefunkcjonalnych, uczes tnictwo w ins pekcji dokumentów i kodu, ś cis ła ws półpraca z analitykiem tes tów); inżyniera ds . zarządzania konfig uracją (zas ób dzielony – 20% czas u pracy – formalnie przypis any do Działu Obs ług i Technicznej Projektów). S kład os obowy zes połu pozwoli na bezproblemowe przeprowadzenie proces u tes toweg o. Ws zys tkie zas oby s ą dos tępne i odpowiednio przes zkolone. 22. Harmonogram (wykres Gantta)

21.2.3. Jednopoziomowy plan testów (level test plan) Jednopoziomowy plan testów obejmuje swoim zakresem w zasadzie te same czynności, co główny plan testów, przy czym dotyczy konkretnego poziomu testowania, np. fazy testów integracyjnych. Jeśli zachodzi konieczność tworzenia jednopoziomowego planu testów, to zwykle zawiera on jedynie uszczegółowienia czy rozszerzenia głównego planu testów o zagadnienia związane z pojedynczym poziomem testowania. plan testów jednego poziomu, jednopoziomowy plan testów (ang. level test plan) – plan testu odnoszący się do jednego poziomu testowania plan testów dla fazy (ang. phase test plan) – plan testów odnoszący się do jednej fazy testowania Jednopoziomowy plan testów może bardziej szczegółowo opisywać np. podejście do testowania, np. techniki projektowania testów. Zazwyczaj jednak

dokument ten jest bardzo rzadko spotykany. W większości przypadków jedynym dokumentem dotyczącym zarządzania testowaniem jest główny plan testów. Norma ISO 29119-3 nie wyróżnia osobnego planu dla poziomu testów, z kolei norma IEEE 829 to czyni, określając następującą strukturę tego dokumentu: 1. Informacja o dokumencie a) unikatowy identyfikator; b) zakres (jaki obszar obejmuje swym zasięgiem niniejszy plan, co jest w nim uwzględnione, a co zostało wyłączone); c) odnośniki (określają listę dokumentów związanych z niniejszym planem, do których mogą następować odwołania w dalszych częściach dokumentu); d) umiejscowienie poziomu testów w całym procesie testowym; e) klasy testów i ogólne warunki testowe (opisują unikalną naturę danego poziomu testów; przykładami klasy testów mogą być: analiza wartości brzegowych, testowanie niepoprawnych wartości).

eksploracyjne,

wykorzystywanie

2. Szczegółowy plan dla poziomu testów a) elementy testowe i ich identyfikatory (opisują, co będzie podlegało testowaniu); b) macierz identyfikowalności testów; c) cechy, które mają być przetestowane; d) cechy, które mają nie być testowane; e) podejście (np.: techniki czarnoskrzynkowe, białoskrzynkowe, analiza, symulacja, inspekcja); f) kryteria zaliczenia testów (ang. pass/fail criteria); g) kryteria zawieszenia i wznowienia wykonywania testów; h) produkty fazy testowej (ang. test deliverables), np. właściwe dla danej fazy: plany, projekty, przypadki i procedury testowe, logi, raporty o incydentach, raporty o stanie testów. 3. Zarządzanie testowaniem (określenie infrastruktury, odpowiedzialności, zasobów, szkoleń, harmonogramu i ryzyk, jeśli nie zostało to uczynione w dokumencie wyższego poziomu, np. w planie testów)

a) planowane czynności i zadania; b) środowisko/infrastruktura (sprzęt, wykorzystywane oprogramowanie, środowisko testowe, narzędzia wspierające, bazy danych itp.); c) odpowiedzialność i władza (ang. authority) (określenie osób lub grup odpowiedzialnych za zarządzanie, projektowanie, przygotowanie, wykonanie, raportowanie oraz weryfikację wyników testów na danym poziomie, a także za usuwanie defektów i dostarczanie elementów testowych); d) powiązania między zaangażowanymi podmiotami (opisuje środki oraz rodzaj komunikacji między zaangażowanymi podmiotami; powiązania mogą być przedstawione w formie graficznej, jako diagram przepływu informacji); e) zasoby i ich alokacja; f) szkolenia; g) harmonogramy, szacunki, koszty; h) ryzyka i sposoby ich łagodzenia. 4. Ogólne a) procedury zapewniania jakości

(jeśli

nie zostały

zdefiniowane

w dokumencie wyższego poziomu lub jeśli wymagane jest ich uszczegółowienie dla danego poziomu testów); b) metryki; c) pokrycie testowe (opisuje wymagane pokrycie, np. kodu dla testów jednostkowych, wymagań dla testów systemowych bądź innych miar pokrycia testami elementów testowych); d) słownik pojęć; e) historia zmian w dokumencie. Cechy testowane, nietestowane oraz podejście (punkty 2c, 2d i 2e powyższego planu) często występują wspólnie w postaci tzw. macierzy testów (ang. test matrix).

21.2.4. Przykład jednopoziomowego planu testów Qualium IT PLAN T EST ÓW JEDNOST KOW YCH

dla s ys temu ELROJ 0.9 Id: JPT-ELROJ_0.9-1.0 W ersja: 1.0 Data: 2014-08-05 Autor: S ławoj S yty (S tars zy Analityk Tes tów), Tomas z Nowak (Prog ramis ta Java) Zaakceptowane przez: Jan Nowakows ki (Kierownik Tes tów) 1 Zakres planu. Plan dotyczy tes towania jednos tkoweg o na poziomie 13 zdefiniowanych w projekcie s ys temu ELROJ metod. 2 Odnośniki. [DC] – „ S tandardowe tes ty dla funkcji daty i czas u” , wers ja 1.6. [S WN] – S pecyfikacja Wymag ań Niefunkcjonalnych dla s ys temu ELROJ, wers ja 1.0. 3 Umiejscowienie poziomu testów. Tes ty jednos tkowe nas tępują po zdefiniowaniu i zaakceptowaniu projektu nis kieg o poziomu i s ą wykonywane przez deweloperów przy ws parciu zes połu tes toweg o. 4 Elementy testowe i ich identyfikatory. Element tes towy

Identyfikator

setInfo(String info)

ET1_S etInf

getInfo()

ET2_GetInf

removeInfo()

ET3_RemInf

addLine(int line)

ET4_AddLin

removeLine(int line)

ET5_RemLin

addBus(int line, int mm)

ET6_AddBus

getAllBusLines()

ET7_AllBus

getAllBusesTimes(int line)

ET8_AllBTim

getNextBusTime(int line, int mm)

ET9_NexBus

addBusInterval(int line, int firstTimemm, int interval, int count)

ET10_Bus Int

removeBus(int line, int mm)

ET11_RemBus

getDisplayString(int actTimemm, int displayLines)

ET12_Dis S tr

setActualDate(int day, int month, int year)

ET13_S etDat

5 Macierz identyfikowalności testów. Macierz ta umożliwia obus tronne ś ledzenie między wymag aniami a elementami tes towymi. Element tes towy

R01

R02

R03

R04

R05

R06

ET1_S etInf

X

X

ET2_GetInf

X

X

ET3_RemInf

X

X

ET4_AddLin

X

ET5_RemLin

X

ET6_AddBus ET7_AllBus

X X

R07

ET8_AllBTim

X

ET9_NexBus

X

ET10_Bus Int

X

ET11_RemBus

X

ET12_Dis S tr

X

ET13_S etDat

X

6 Cechy, które mają być przetestowane. Tes towaniu podleg a ws zys tkie 13 metod. 7 Cechy, które mają nie być testowane. Tes towaniu nie podleg ają metody s łużące do obs ług i komunikacji s ieciowej, bazodanowe oraz do komunikacji oprog ramowania z ekranem. 8 Macierz testów i podejście testowe Element tes towy

Podejś cie

setInfo(String info)

Tes towanie czarnos krzynkowe Tes towanie wartoś ci błędnych (znaki s pecjalne, wartoś ci eks tremalne)

getInfo() removeInfo() addLine(int line) removeLine(int line)

addBus(int line, int mm) getAllBusLines()

Tes towanie obciążeniowe Tes towanie czarnos krzynkowe Analiza wartoś ci brzeg owych Tes towanie obciążeniowe Analiza wartoś ci brzeg owych

getAllBusesTimes(int line) getNextBusTime(int line, int mm) addBusInterval(int line, int firstTimemm, int interval, int count)

Tes towanie czarnos krzynkowe Tes towanie eks ploracyjne Metoda Categ ory-Partition

Analiza wartoś ci brzeg owych Tes towanie CRUD

removeBus(int line, int mm)

getDisplayString(int actTimemm, int displayLines)

Tes towanie wydajnoś ci Categ ory-Partition lub drzewa klas yfikacji + tes towanie pairwis e Tes towanie CRUD

setActualDate(int day, int month, int year)

S tandardowy zes taw tes tów dla weryfikacji funkcji czas u i daty [DC]

9 Kryteria zaliczenia testów. Tes ty funkcjonalne s ą uznane za zaliczone, jeś li ich wynik jes t zg odny z wynikiem oczekiwanym. Tes ty wydajnoś ci s ą uznane za zaliczone, jeś li wynik mieś ci s ię w dopus zczalnych g ranicach okreś lonych w dokumencie [S WN]. 10 Pokrycie testowe. Dla każdeg o z modułów wymag ane jes t co najmniej 90% pokrycie ins trukcji oraz 80% pokrycie decyzji, przy czym dla modułu getDisplayString pokrycie decyzji ma być co najmniej 90%.

21.2.5. Raport o stanie testów (test status report) Raport o stanie testów dostarcza informacji o aktualnym statusie testowania przeprowadzonego w ustalonym czasie. ISO 29119-3 [4] słusznie podkreśla, że

raport ten nie zawsze musi mieć formę pisemną. Na przykład w projektach prowadzonych według metodologii zwinnych stan testów może być omawiany na spotkaniach przed lub po przeprowadzonej iteracji. Podczas takich dyskusji można wspomagać się informacjami zawartymi na tablicach aktywności lub tzw. wykresie spalania. raport o stanie testów, raport o postępie testów (ang. test status report, test progress report) – dokument zawierający podsumowanie aktywności testowych i osiągniętych wyników, tworzony regularnie, aby raportować postęp prac testowych w stosunku do założeń i przedstawiający ryzyka oraz alternatywy wymagające podjęcia decyzji zarządczych Oto przykładowa struktura raportu o stanie testów: a) okres raportowania (za jaki okres są prezentowane dane); b) postęp odniesiony do planu testów (wszelkie poważniejsze odchylenia powinny być opisane, wraz z powodami tych odchyleń, akcjami naprawczymi oraz efektami tych akcji); c) czynniki blokujące postęp; d) miary; e) nowe i zmienione ryzyka; f) zaplanowane testowanie (opis planowanych czynności testowych na następny okres raportowania).

21.2.6. Przykład raportu o stanie testów Qualium IT Raport za okres: od 2014-08-01 do 2014-08-31 RAPORT O ST ANIE T EST ÓW Osoba odpowiedzialna: Rados ław S ikorka dla s ys temu ELROJ 0.9 # tes tów Moduł

pokrycie

zaproj. wykon. zdanych niezd. ws trz. ins tr. dec.

ET10_Bus Int

0

0

0

0

0





ET11_RemBus 4

3

3

0

0

78%

66%

ET12_Dis S tr

26

21

11

10

0

62%

58%

ET13_S etDat

5

5

1

4

0

100% 90%

RAZEM

35

29

15

14

0

77%

68%

21.2.7. Raport końcowy z testowania (test completion report) Raport końcowy z testowania podsumowuje wszystkie zadania wykonane w ramach procesu testowego. Może obejmować cały projekt lub dotyczyć wybranego podprocesu testowego.

raport końcowy z testowania, raport z testów, sumaryczny raport z testów, raport oceny testów (ang. test completion report, test report, test summary report, test evaluation report) – sumaryczny dokument przedstawiający działania testowe i ich rezultaty; zawiera także ocenę testowanych elementów pod względem zgodności z kryteriami wyjścia [5] Oto przykładowa struktura raportu z zakończonego testowania: a) podsumowanie (opisuje, co było przetestowane, a także ograniczenia, w ramach których proces testowy był prowadzony); b) odchylenia od planu (w szczególności może podawać również informację o ryzyku rezydualnym); c) ocena komplentości testów (w jakim stopniu testowanie spełniło założone kryteria, np. w terminach pokrycia strukturalnego, wymagań bądź ryzyk); d) czynniki opóźniające postęp (wraz ze sposobami radzenia sobie z nimi); e) miary; f) ryzyko rezydualne (ryzyka pozostałe w programie, niezłagodzone, a także ryzyka odkryte po ukończeniu testowania); g) produkty procesu testowego (plan testów, przypadki testowe itp.); h) reużywalne produkty procesu testowego (np. skrypty lub dane testowe); i) wyciągnięte wnioski (ang. lessons learned) (opis wniosków będących efektem spotkania poprojektowego).

21.2.8. Przykład raportu końcowego z testowania

S zacunkowy poziom ryzyka rezydualneg o: wpływ/prawdopodobieńs two b. małe małe ś rednie duże b. duże b. mały

3

mały

1

ś redni duży b. duży

1

1 1

S umaryczne pokrycie/cel: ins trukcji: 94%/90% decyzji: 91%/90% 3 Szacowana liczba błędów polowych: 7 (model Rayleig ha), 10 (analiza mutacyjna) 4 W nioski na przyszłość. S zkolenie z zakres u technik projektowania tes tów s kutkowało wyraźnym zwięks zeniem jakoś ci tes tów. Warto org anizować je jako obowiązkowe dla nowo przyjmowanych tes terów.

Projekt s tworzenia włas neg o narzędzia do obs ług i incydentów nie zos tał ukończony i s tracono z teg o powodu 3 os obomies iące czas u. Powodem były rozbieżne oczekiwania interes arius zy co do funkcjonalnoś ci produktu i brak motywacji deweloperów do teg o odg órnie narzuconeg o projektu. S ug es tia: używać opens ource’owych narzędzi. Mają niżs zą funkcjonalnoś ć niż ta żądana przez kierownictwo IT, ale za to s ą darmowe i można je s zybko wdrożyć.

21.3. Dokumenty dynamicznych procesów testowych 21.3.1. Specyfikacja testów (test design specification) Specyfikacja testów wskazuje cechy, które mają podlegać testowaniu oraz warunki testowe wyprowadzone z podstawy testów. Jest to więc dokument fazy projektowania testów. specyfikacja testów (ang. test specification) – dokument zawierający specyfikację projektu testów, specyfikację przypadków testowych i/lub specyfikację procedury testowej

specyfikacja projektu testów (ang. test design specification) – dokument specyfikujący warunki testowe (elementy pokrycia) dla elementu testowego, szczegółowe podejście do testów oraz identyfikujący powiązane przypadki testowe wysokiego poziomu [5] Przykładowa struktura specyfikacji testów wygląda następująco: 1. Zbiory cech (ang. feature sets) a) ogólny opis (jakie zbiory cech występują w systemie); b) unikatowy identyfikator dla każdego zbioru cech; c) cel (co chcemy osiągnąć, wykorzystując ten zbiór cech); d) priorytet dla testowania (jeśli konieczny); e) szczegółowa strategia testowania dla każdego zbioru cech; f) śledzenie między zbiorami cech a elementami podstawy testów (ta idenyfikowalność może być zawarta w macierzy testów). 2. Warunki testowe a) ogólny opis (jakie warunki testowe będą podlegać testom); b) unikatowy identyfikator dla każdego warunku testowego; c) opis poszczególnych warunków testowych (w języku naturalnym lub za pomocą modelu); d) priorytet dla testowania (jeśli konieczny); e) śledzenie między warunkami testowymi a zbiorami cech lub elementami podstawy testów (ta identyfikowalność może być zawarta w macierzy testów).

21.3.2. Przykład specyfikacji testów Qualium IT Data: 2014-08-01 SPECYFIKACJA T EST ÓW Osoba odpowiedzialna: Julius z Mickiewicz dla s ys temu ELROJ 0.9 Id: S T-ELROJ_0.9-1.0 1 Zbiory cech i warunki testowe.

ELROJ 1. Oprog ramowanie 1.1. Komunikat (WT: setInfo, getInfo, removeInfo) 1.2. Data (WT: setActualDate) 1.3. Tablica kurs ów (WT: getDisplayString, getNextBusTime) 2. GUI oraz interfejs GUI-oprog ramowanie 2.1. Opcja „ zarządzaj liniami” 2.1.1. funkcja dodawania linii (WT: formularz dodawania linii, addLine) 2.1.2. okno wyś wietlania linii (WT: okno wyś wietlania linii, getAllBusLines) 2.1.3. funkcja us uwania linii (WT: formatka us uwania linii, removeLine) 2.1.4. funkcja zmiany numeru linii (WT: formularz zmiany numeru linii) 2.2. Opcja „ zarządzaj kurs ami” 2.2.1. funkcja dodawania kurs u (WT: formularz dodawania kurs u, addBus) 2.2.2. funkcja dodawania wielu kurs ów (WT: formularz dodawania wielu kurs ów, addBusInterval) 2.2.3. funkcja us uwania kurs u (WT: formatka us uwania kurs u, removeBus) 2.2.4. funkcja wyś wietlania kurs ów (WT: okno wyś wietlania kurs ów, getAllBusesTimes) 2.2.5. funkcja zmiany kurs u (WT: okno zmiany kurs u) 2.3. Opcja „ komunikat”

2.3.1. funkcja us tawiania komunikatu (WT: formularz us tawiania komunikatu) 2.3.2. przycis k us uwania komunikatu (WT: przycis k „ us uń komunikat” ) 2.4. Opcja „ zapis /odczyt” 2.4.1. opcja zapis u rozkładu na dys k (WT: funkcje transform2xml oraz save) 2.4.2. opcja odczytu rozkładu z dys ku (WT: funkcje load oraz transform2timetable) 2.5. Opcja „ komunikacja z ekranem” 2.5.1. funkcja aktualizacji rozkładu jazdy (WT: okno aktualizacji, funkcja updateTimetable) 2.5.2. funkcja wyłączenia wyś wietlania rozkładu (WT: przycis k „ wyłącz rozkład” ) 2.5.3. funkcja wyś wietlania w trybie s pecjalnym (tylko komunikaty) (WT: setInfo w połączeniu z formatką wyś wietlania w trybie s pecjalnym) 3. Ekran 3.1. Interfejs komunikacyjny ekran-oprog ramowanie (WT: API ScreenCom) 3.2. Oprog ramowanie panelu konfig uracyjneg o ekranu (WT: prog ram SConfig, panel s terujący w ekranie (GUI))

21.3.3. Specyfikacja przypadku testowego (test case specification) Specyfikacja przypadku testowego opisuje jeden lub kilka przypadków testowych dla określonych elementów testowych. specyfikacja przypadków testowych (ang. test case specification) – dokument specyfikujący zbiór przypadków testowych (cel, wejścia, czynności testowe,

oczekiwane rezultaty i wstępne warunki wykonania) dla elementu testowego [5] Przykładowa następująco:

struktura

specyfikacji

przypadku

testowego

wygląda

1. Elementy pokrycia testowego a) ogólny opis (jakie elementy pokrycia testowego są definiowane dla jakich warunków testowych; elementy pokrycia testowego są wyprowadzane przez zastosowanie odpowiedniej techniki projektowania testów do warunków testowych; np. technika podziału na klasy równoważności wygeneruje elementy pokrycia w postaci klas równoważności); b) unikatowy identyfikator dla każdego elementu pokrycia testowego; c) opis elementów pokrycia (co ma być pokryte, np. dla metody podziału na klasy równoważności można opisać, czy pokrywane są poprawne, czy niepoprawne klasy równoważności); d) priorytet (jeśli konieczny); e) śledzenie (identyfikuje związek elementu pokrycia z warunkiem testowym lub zbiorem cech; identyfikacja ta może być opisana w macierzy testów). 2. Przypadki testowe a) ogólny opis (definiuje przypadki wyprowadzone z elementów pokrycia; liczba przypadków testowych będzie zależna od przyjętego kryterium pokrycia zdefiniowanego np. w planie testów); b) unikalny identyfikator dla każdego przypadku testowego; c) cel (jaki jest cel danego przypadku testowego? często cel ujęty jest w nazwie przypadku); d) priorytet (jeśli konieczny); e) śledzenie (wiąże przypadek z elementem pokrycia, wymaganiem lub innym elementem podstawy testów); f) warunki wstępne (co musi zachodzić przed wykonaniem testu); g) wejście; h) oczekiwane wyjście; i) rzeczywiste wyniki i rezultat testu (mogą być opisane w specyfikacji procedury testowej, w dokumencie „otrzymane wyniki” lub w dokumencie wyniku testu).

21.3.4. Przykład specyfikacji przypadku testowego Przykładowa specyfikacja przypadku testowego dla systemu ELROJ jest pokazana na rysunku 3.5.

21.3.5. Specyfikacja procedury testowej (test procedure specification) Specyfikacja procedury testowej opisuje kolejność wykonywania przypadków testowych. Może również zawierać informacje dotyczące specjalnych wymagań (np. dla środowiska testowego), które muszą być spełnione przed wykonaniem danej suity testów. Schemat wykonywania procedur testowych jest określany mianem harmonogramu wykonania testu. harmonogram wykonania testu (ang. test execution schedule) – schemat wykonania procedur testowych; procedury testowe są zawarte w harmonogramie wykonywania testów, w ich kontekście i kolejności, w jakiej mają być wykonane Przykładowa organizacja specyfikacji procedury testowej może wyglądać następująco: 1. Zbiory testów a) ogólny opis (jak poszczególne przypadki testowe są budowane w zbiory testów o wspólnym celu testowania); b) unikatowy identyfikator dla każdego zbioru testów; c) cel (jaki jest cel poszczególnych zbiorów testów); d) priorytet (jeśli konieczny); e) zawartość/śledzenie (lista identyfikatorów przypadków testowych wchodzących w skład poszczególnych zbiorów testów). 2. Procedury testowe a) ogólny opis procedur wyprowadzonych ze zbiorów testów; b) unikatowy identyfikator dla każdej procedury testowej; c) cel (jaki jest cel poszczególnych procedur testowych); d) priorytet (jeśli konieczny); e) konfiguracja początkowa (opisuje niezbędne do wykonania czynności przed uruchomieniem danej procedury testowej; zwykle zawiera opis

warunków wstępnych dla pierwszego przypadku testowego); f) uruchamiane przypadki testowe (lista przypadków w kolejności ich uruchomienia); g) związek z innymi procedurami (opisuje zależności między tą a innymi procedurami testowymi, które np. wymuszają wykonanie całych procedur testowych w określonej kolejności); h) konfiguracja końcowa (opisuje niezbędne do wykonania czynności, jakie należy wykonać po zakończeniu procedury testowej, np. rozmontowanie środowiska testowego, wyczyszczenie bazy danych).

21.3.6. Przykład specyfikacji procedury testowej

21.3.7. Wymagania co do danych testowych (test data requirements)

Dokument wymagań co do danych testowych określa warunki, jakie muszą spełniać dane wykorzystywane w procedurach bądź przypadkach testowych. Przykładowa struktura takich wymagań wygląda następująco: a) ogólny opis (opisuje dane wymagane do uruchomienia procedur testowych; może również zawierać wymagania dotyczące jakości tych danych); b) unikatowy identyfikator; c) opis (określa nazwę, możliwy/dopuszczalny zakres wartości, format i strukturę każdej danej; może zawierać wymagania co do anonimowości lub zaciemniania (ang. obfuscation) danych); d) odpowiedzialność (kto odpowiada za udostępnianie danych i kontrolę ich poprawności); e) czas (kiedy i na jak długo dane są potrzebne); f) resetowanie (określa, czy i w jakich okolicznościach dane mają być resetowane); g) archiwizacja/rozporządzanie danymi (opisuje, kiedy i w jaki sposób dane mają być archiwizowane lub przetwarzane po zakończeniu testowania).

21.3.8. Przykład wymagania co do danych testowych

21.3.9. Wymagania co do środowiska testowego (test environment requirements) Wymagania co do środowiska testowego to dokument analogiczny do wymagań dotyczących danych testowych. Jak sama nazwa wskazuje, dotyczy on środowiska testowego, czyli całej infrastruktury niezbędnej do przeprowadzenia testów. Dokument ten może być bardzo istotny w przypadku projektów silnie opartych na sprzęcie, np. związanych z oprogramowaniem telekomunikacyjnym współpracującym ze stacjami przekaźnikowymi. Zwykle jednak nie ma potrzeby generowania wymagań na środowisko jako odrębnego dokumentu. Kwestie związane z elementami środowiska testowego omawiane są najczęściej w planie testów lub w wymaganiach dla poszczególnych przypadków bądź procedur testowych. Można wyróżnić następujące elementy środowiska: sprzęt (ang. hardware); oprogramowanie pośredniczące (ang. middleware), np. aplikacyjne, silniki bazodanowe czy systemy transakcyjne; oprogramowanie (ang. software); urządzenia peryferyjne, np. drukarki, skanery, plotery;

serwery

środki komunikacji, np. dostęp przez www, usługi sieciowe; narzędzia; bezpieczeństwo; środowisko fizyczne (np. wymagania dotyczące wielkości pomieszczeń, dopuszczalnego poziomu hałasu, promieniowania, natężenia ruchu); akcesoria (np. specjalne dokumenty, takie jak karty kredytowe, karty do głosowania, karty odpowiedzi do testu wielokrotnego wyboru, celowo uszkodzone płyty DVD). Przykładowa struktura wymagań co do środowiska testowego wygląda następująco: a) ogólny opis (opisuje elementy środowiska wymagane do wykonania procedur testowych; swym zakresem obejmuje zarówno fazę przygotowawczą, fazę wykonania procedur, jak i fazę po wykonaniu procedur); b) unikalny identyfikator dla każdego elementu środowiska; c) dokładny opis każdego elementu środowiska wraz z wymaganiami co do tych elementów; d) osoba odpowiedzialna za dostarczenie i nadzór nad elementami środowiska; e) czas, na który potrzebne będą poszczególne elementy środowiska.

21.3.10. Przykład wymagań co do środowiska testowego Qualium IT W YMAGANIA CO DO ŚRODOW ISKA T EST OW EGO dla s ys temu ELROJ 0.9 Id: WS T-ELROJ_0.9, wers ja 1.0 Data: 2014-07-02 Osoba odpowiedzialna: Janina Nowakows ka (s enior config uration manag ement eng ineer)

Lp. Id

1

2

3

Element ś rodowis ka

Wymag ania

Czas

S ymulator S oftware’owy S creenS imulator Tes ty s ymulator ES 001 w wers ji zg odnej jednos tkowe elektroniczneg o z fizycznym modelem i integ racyjne ekranu wykorzys taneg o ekranu

ES 002 Ekran

Model CityS creen S -779 wraz z oprog ramowaniem dla modułu komunikacji

Tes ty s ys temowe i akceptacyjne

ES 003 Baza MyS QL

S krypty umożliwiające tworzenie konfig uracji bazy dla danych tes towych opis anych w Wymag aniach na dane tes towe

Cały czas trwania projektu

21.3.11. Raport o gotowości danych testowych (test data readiness report) Raport ten opisuje status gotowości/dostępności poszczególnych danych testowych. Dla każdej danej testowej zawiera jej unikatowy identyfikator (opisany w wymaganiach na dane testowe) oraz status tej danej. Status może informować tylko, czy dana jest gotowa do użycia, czy nie, ale w szczególności może również opisywać odchylenia danych od wymagań, np. w terminach wartości, struktury czy objętości.

21.3.12. Raport o gotowości środowiska testowego (test environment readiness report)

Raport ten opisuje status gotowości/dostępności poszczególnych elementów środowiska. Dla każdego elementu środowiska zawiera jego unikatowy identyfikator (opisany w wymaganiach na środowisko testowe) oraz status tego elementu. Status może informować tylko, czy dany element jest gotowy do użycia czy nie, ale w szczególności może również opisywać odchylenia od wymagań, np. w terminach wersji elementu lub opóźnienia w jego dostarczeniu. Tego typu wymagania często spotyka się w systemach krytycznych, o wysokich wymaganiach co do niezawodności czy też o wysokiej integracji danych.

21.3.13. Otrzymane wyniki (actual results) Dokument ten opisuje otrzymane, rzeczywiste wyniki testu. Wyniki te mogą być jedną liczbą lub łańcuchem znaków, ale mogą mieć również bardziej skomplikowaną strukturę. Ponadto, niektóre projekty wymagają bardziej restrykcyjnego podejścia do logowania wyników, np. logowania wszystkich akcji wykonanych w systemie lub wręcz nagrywania całego działania programu, w celu umożliwienia późniejszego dokładnego odtworzenia tego wykonania. Niektóre przypadki testowe podczas wykonania mogą produkować dane będące pośrednimi wynikami. Takie dane mogą być zapisywane osobno np. w dzienniku wykonania testów. Jeśli są zapisywane razem z wynikami końcowymi, to należy jasno rozróżnić, które wyniki są pośrednie, a które końcowe. Zwykle dokument opisujący otrzymane wyniki nie stanowi osobnego artefaktu procesu testowego. Najczęściej jego zawartość jest po prostu logowana w dzienniku wykonania testów.

21.3.14. Wynik testu (test result) Wynik testu to dokument opisujący to, w jaki sposób zakończyło się wykonanie każdego z testów – czy test został zdany, czy też nie. Innymi słowy, dokument ten opisuje porównanie wyników oczekiwanych z rzeczywistymi dla każdego przypadku testowego. Wynik testu jest zapisywany zwykle w dzienniku wykonania lub w innym miejscu i nie jest traktowany jako osobny dokument.

21.3.15. Dziennik wykonania testów (test execution log)

Dziennik wykonania testów, zwany często po prostu logiem testów to zapis wszystkich czynności występujących podczas uruchamiania i wykonywania testów w ramach jednej lub wielu procedur testowych. Dziennik często jest plikiem tekstowym zawierającym mniej lub bardziej ustrukturalizowaną informację na temat uruchamiania czy przebiegu testów. Często wykorzystuje się specjalne skrypty przetwarzające taki plik, które usuwają mniej istotne dane i przekształcają pozostałe informacje do czytelnej postaci. Przykładowa struktura dziennika wykonania testów wygląda następująco: a)

ogólne informacje (zawierają spis najistotniejszych zdarzeń zaobserwowanych podczas wykonywania testów, np. nagły spadek dostępnych zasobów lub wydajności, błędy wykonania skryptów testowych, zbyt długi czas trwania testu);

b) unikalny identyfikator dla każdego zdarzenia (zwykle jest to kolejna liczba naturalna); c) czas (ang. timestamp), w którym nastąpiło zdarzenie; d) opis zdarzenia; e) wpływ zdarzenia na wykonanie testu lub na otrzymany wynik. Na rysunku 21.2 jest pokazany fragment przykładowego dziennika dla testów (źródło: http://blogs.msdn.com/b/billbar/archive/2009/06/09/vsts-

obciążeniowych

2010-load-test-feature-saving-test-logs.aspx).

Rysunek 21.2. Przykładowy dziennik wykonania testów

21.3.16. Raport o incydencie (test incident report) Incydent to każde zdarzenie wymagające dalszego zbadania. Zdarzenia takie są zapisywane w raporcie o incydencie. Więcej informacji o zarządzaniu incydentami Czytelnik znajdzie w rozdziale 27. W szczególności, w podrozdziale 27.4 jest opisana zawartość raportu o defekcie.

21.3.17. Raport z sesji testowania eksploracyjnego (exploratory testing session report) Testowanie eksploracyjne jest nieustrukturalizowaną formą testowania opartą na doświadczeniu i intuicji. Tester ma dużą swobodę w wyborze formy oraz technik testowania. Mimo to testowanie to można również dokumentować. Raport z sesji testowania eksploracyjnego powinien zawierać następujące elementy: identyfikator dokumentu; nazwisko i imię testera/testerów przeprowadzającego/ych sesję; kartę testu (ang. test charter); podział zadań w ramach sesji; opis przebiegu sesji i uwagi;

znalezione problemy; znalezione defekty.

21.3.18. Przykład raportu z sesji testowania eksploracyjnego Qualium IT RAPORT Z SESJI T EST OW ANIA EKSPLORACYJNEGO S ys tem ELROJ 0.9 ID: ET-Elroj-006 T ester: Karol Nowakows ki Karta testu: Przetes tować zarządzanie liniami i kurs ami Podział zadań w ramach sesji: Czas trwania: 1h (s es ja krótka) Przyg otowanie do s es ji: 0% Projektowanie i wykonanie tes tów: 80% Badanie problemów i błędów: 20% Opis sesji: Przetes towałem ws zys tkie pods tawowe operacje związane z dodawaniem i us uwaniem linii oraz kurs ów, s tos ując różne s cenarius ze dotyczące liczby linii i kurs ów, kolejnoś ci dodawania oraz różnych s ekwencji wykonywanych akcji. Znalezione problemy: Podczas dodawania is tniejąceg o już kurs u znikał on z rozkładu jazdy (!). Działo s ię tak w każdym przypadku, niezależnie od liczby

is tniejących linii czy kurs ów, dlateg o problem ten jes t łatwy do zreprodukowania. Z ws tępneg o dochodzenia, przeprowadzoneg o ws pólnie z prog ramis tą Janem Malinows kim w ramach tej s es ji wynika, że prawdopodobną przyczyną błędu może być fakt, że dodawanie kurs u zmienia jeg o flag ę (jes t/nie ma w rozkładzie), zamias t us tawiać ją na „ jes t” . Dlateg o powtórne dodanie kurs u mająceg o flag ę „ jes t” może ją zmienić na flag ę „ nie ma” .

1 CTO to angielski skrót od Chief Technical Officer, czyli Dyrektor ds. Technologii. 2 Zarządzanie incydentami jest opisane w rozdziale 27. 3 Podobnie jak niesławny współczynnik „testers to developers ratio” [416] czy określanie, jaka część budżetu projektu ma być przeznaczona na testowanie.

22. Szacowanie testów

Szacowanie jest klasyczną czynnością w zarządzaniu każdym projektem. W przypadku projektu testowego jest wykorzystywane do: określenia pracochłonności projektu (ile czasu, zasobów, wysiłku będzie potrzebne do wykonania danych czynności?); określenia kosztochłonności projektu (ile będzie kosztować wykonanie zaplanowanych zadań?). Szacowanie pozwala na efektywną alokację zasobów oraz lepszą organizację projektu. Jest również podstawą do podjęcia decyzji o ograniczeniu zakresu projektu, jeśli estymowany koszt lub czas jest zbyt duży. Szacunki są oczywiście tylko przybliżeniem rzeczywistych wartości (bo tych jeszcze nie znamy), dlatego istotne jest, aby były tak dokładne, jak to tylko możliwe. Aby estymacja była dobra, w trakcie tworzenia musi mieć następujące charakterystyki [173]: jest oparta na wiedzy przedmiotu szacowania;

i

doświadczeniu ekspertów w zakresie

jest wspierana przez osoby, które będą wykonywały szacowaną pracę; jest możliwie dokładna w określaniu kosztów, zasobów, zadań i ludzi; bazuje na najbardziej prawdopodobnych kosztach, wysiłkach i czasach trwania poszczególnych zadań. Szacowanie, zwłaszcza w inżynierii oprogramowania, nie jest zadaniem łatwym. Problemy w estymowaniu wynikają nie tylko z natury tej dziedziny, lecz także z tzw. przyczyn politycznych. Niemniej istnieją dobre praktyki w naukach o zarządzaniu, które pozwalają uzyskiwać możliwie dobre szacunki.

szacowanie testów (ang. test estimation) – proces aproksymacji kosztu, czasu, wysiłku lub nakładu pracy, jaki będzie związany z testowaniem Jeśli organizacja w ogóle wykorzystuje szacowanie testów jest ważne, aby w trakcie projektu mierzyć poziom wszystkich czynników mogących mieć wpływ na estymowaną wartość oraz rzeczywistą wartość po zakończeniu danego projektu czy zadania. Tak zebrane dane historyczne są niesłychanie przydatne w tworzeniu lepszych modeli predykcyjnych. Stanowią również cenną informację, którą można wykorzystać w korekcie estymacji w przyszłych projektach. Rozważmy następujący prosty przykład: organizacja mierzyła koszt testów w czterech projektach, A, B, C i D. Wartości estymacji, parametry projektów oraz rzeczywisty koszt są ujęte w tabeli 22.1. Tabela 22.1. Przykładowe szacowanie kosztów testów z korektą

Szacunkowy Szacunkowa T echnologia koszt Projekt liczba wykorzystywana testów testów w projekcie (USD)

Rzeczywisty koszt testów (USD)

A

450

Java

450 000

480 000

B

600

Java

600 000

575 000

C

400

C++

400 000

580 000

D

500

C++

500 000

785 000

E

600

C++

900 000

930 000

Przy projekcie A oszacowano, że koszt jednego testu wyniesie około 1000 USD. Szacunki okazały się w miarę dokładne, dlatego takie same estymacje poczyniono w kolejnych projektach B, C i D. O ile w przypadku projektu B przewidywania znów w miarę dobrze się sprawdziły, o tyle przy projektach C i D wystąpił problem niedoszacowania – rzeczywisty koszt testowania był około półtora razy większy niż w projektach A i B. Liczba testów we wszystkich projektach jest tego samego rzędu wielkości (od 400 do 600), więc wielkość projektu może nie mieć wpływu na

te różnice. Z tabeli wiemy, że projekty C i D wykorzystywały inną technologię niż A i B, co jest prawdopodobną przyczyną tych rozbieżności. Dlatego, w przypadku projektu E, pisanego w tej samej technologii, co C i D wprowadzono korektę – koszt jednego testu podniesiono do kwoty 1500 USD. Tym razem oszacowania były bliższe rzeczywistości.

22.1. Czynniki wpływające na szacowanie Aby nasze szacunki były jak najbliższe rzeczywistości, musimy wziąć pod uwagę możliwie dużo czynników wpływających na szacowaną wielkość. Black [173] wymienia cztery podstawowe grupy takich czynników: czynniki procesowe; czynniki materiałowe; czynniki ludzkie; czynniki opóźniające. Przykłady czynników związanych z procesem, w którym działa zespół testowy: stopień, w jakim aktywności testowe przenikają projekt; wyraźne oddzielenie zespołu testowego od reszty organizacji; dobre zarządzanie zmianą; wybrany cykl życia projektowania lub utrzymywania oprogramowania, z uwzględnieniem poziomu dojrzałości procesu testowego; skuteczne i przeprowadzane na czas naprawy błędów; realistyczne harmonogramy i budżety; dostarczane na czas, wysokiej jakości artefakty testowe (skrypty, raporty, przypadki testowe, środowisko testowe itp.) właściwe przeprowadzenie testowania we wczesnych fazach testowych. Czynniki materiałowe pochodzą z samej natury projektu, wykorzystywanych narzędzi, dostępnych zasobów i tym podobnych:

istniejące, wykorzystywane automatyzacji testowania;

i

o

wysokiej

jakości

narzędzia

jakość środowiska, procesu, przypadków i narzędzi testowych; odpowiednie, dedykowane i bezpieczne środowisko testowe; oddzielone od produkcyjnego środowisko testowe; dostępność wyroczni testowych; dostępna i wysokiej jakości dokumentacja testowa i projektowa (wymagania, projekty, plany itp.); reużywalne systemy testowe oraz dokumentacja podobnych projektów;

z poprzednich,

podobieństwo projektu (w tym projektu testowego) do projektów wcześniejszych. Czynniki związane z ludźmi i zespołem: wysokiej klasy kierownictwo wyższego szczebla, menedżerowie oraz liderzy zespołów; zaangażowanie kierownictwa w jakość, w szczególności w odniesieniu do procesu testowego; realistyczne oczekiwania interesariuszy; umiejętności, doświadczenie i postawa członków zespołu; stabilność zespołu (w tym brak rotacji pracowników); zdrowe relacje międzyludzkie w zespole; kompetentni pracownicy stanowiący wsparcie środowiska testowego; docenienie w całej organizacji testowania, zarządzania wydaniami, zarządzania konfiguracją, administracji systemowej i innych mniej zauważalnych, ale kluczowych dla powodzenia przedsięwzięcia obszarów; wykorzystanie doświadczonych kontraktorów i konsultantów; uczciwość, transparentność, zaangażowanie, otwartość, dzielenie się wiedzą; dostępność szkoleń. Przykłady czynników opóźniających: wysoka złożoność procesów, projektu, organizacyjnej lub środowiska testowego;

technologii,

struktury

duża liczba interesariuszy procesu testowego lub całego projektu; wiele zespołów, zwłaszcza rozproszonych geograficznie; potrzeba formowania testowego;

i

szkolenia

nowo

powstającego

zespołu

dostępność określonego sprzętu i oprogramowania; jakość dostawców; decyzja o automatyzacji części czynności testowych (może dać zwrot z inwestycji w długim okresie, ale krótkookresowo paradoksalnie prawie zawsze jest powodem opóźnień); wymóg tworzenia szczegółowych, przypadków testowych;

dokładnie udokumentowanych

duży nacisk na tworzenie dokumentacji, zwłaszcza w formacie, z którym zespół testowy jest niezaznajomiony; zależności między czasem dostarczenia poszczególnych komponentów (szczególnie ważne w testach integracyjnych); „kruche” dane, np. wrażliwe na czas; wymaganie na wysoką jakość systemu (np. w przypadku projektów o znaczeniu krytycznym); duży i skomplikowany testowany system; brak danych historycznych lub przemysłowych w szczególności projektów wysoce innowacyjnych).

(dotyczy

Bardzo ważne jest, aby dokonując szacowania brać pod uwagę wymienione czynniki. Prawie na pewno większość z nich wystąpi w naszym kolejnym projekcie. Fakt wystąpienia każdego z czynników należy zapisywać tak, aby w danych historycznych pozostał jakiś ślad, że nastąpiło takie wydarzenie. Te informacje pozwolą na dokładniejsze zbadanie, jak ilościowo poszczególne czynniki wpływają na poszczególne parametry projektu, takie jak: czas, budżet, zasoby itp.

22.2. Techniki szacowania Istnieje wiele metod szacowania. Dalej prezentujemy kilka z nich, opisując jednocześnie okoliczności, w jakich dana technika powinna się sprawdzić. Część z tych metod opiszemy dokładnie w dalszej części książki.

22.2.1. Intuicja, zgadywanie, doświadczenie Jest to metoda najprostsza do przeprowadzenia i jednocześnie najbardziej podatna na błędy. Szacowanie polega na zgadywaniu, przy czym należy wspierać się swoją intuicją oraz doświadczeniem. Metoda polecana zwykle jako „ostatnia deska ratunku”, gdy nie możemy wykorzystać żadnego innego podejścia.

22.2.2. Estymacja przez analogię W tej metodzie szacunki oparte są na wartościach z innych, podobnych projektów. Używając analogii należy pamiętać o czynnikach, które mogą zawyżać lub zaniżać nasze szacunki, tak jak w przykładzie z tabeli 22.1.

22.2.3. Struktura podziału prac (Work Breakdown Structure, WBS) Metoda polega na dokonaniu podziału projektu na mniejsze części. Podział ten przeprowadza się tak długo, aż otrzymamy podzadania na tyle niewielkie, że możemy przeprowadzić wobec nich w miarę dokładne szacowanie. Przykład zastosowania struktury podziału prac do oszacowania liczby przypadków testowych dla systemu ELROJ z Dodatku A jest pokazany na rysunku 22.1.

Rysunek 22.1. Struktura podziału prac dla liczby testów systemu ELROJ

Białe elementy drzewa oznaczają części systemu, procesu testowego lub funkcjonalności, których nie dzielimy już dalej. Dla każdego elementu estymujemy liczbę testów jego dotyczącą, np. szacujemy, że przetestowanie funkcjonalności związanej z zarządzaniem kursami (dodawanie, usuwanie itp.) będzie wymagać stworzenia ośmiu przypadków testowych. Elementy oznaczone na szaro to części, które podlegają dalszemu podziałowi. Liczby na szarych polach odpowiadające tym wierzchołkom drzewa to suma wyników wszystkich jego dzieci. Na przykład, szacunkowa liczba testów modułowych oprogramowania wynosi 5 + 5 + 8 + 23 = 41. Sumując, przechodzimy w górę drzewa, aż znajdziemy się w jego korzeniu, którego liczba odpowiada szacunkowej estymacie. W naszym przypadku przewidujemy, że cały system ELROJ będzie wymagał stworzenia 95 przypadków testowych. Szacowanie dotyczyło tylko i wyłącznie liczby przypadków testowych. Aby estymować koszt lub czas tego testowania, należy wziąć pod uwagę różne typy tych testów. Na przykład projektowanie, implementacja i wykonanie testu systemowego będzie zwykle zajmowało więcej czasu niż w przypadku testu jednostkowego. struktura podziału prac, WBS (ang. Work Breakdown Structure, WBS) – układ elementów pracy i ich wzajemnych związków oraz związków z produktem końcowym [197]

22.2.4. Estymacja grupowa Estymacja grupowa wykorzystuje szacunki wielu osób, które następnie są uśredniane1. Istnieje wiele metod estymacji grupowej, np. metoda delficka, rozszerzona metoda delficka, poker planistyczny2. Dokładne omówienie różnych metod estymacji znajduje się w rozdziale 29. Załóżmy, że zebraliśmy 5 ekspertów z zakresu testowania i zapytaliśmy niezależnie każdego z nich, ile testów powinno być zaplanowanych dla systemu ELROJ. Otrzymaliśmy następujące odpowiedzi: Ekspert 1: 102 Ekspert 2: 230 Ekspert 3: 91 Ekspert 4: 90 Ekspert 5: 100

Po uśrednieniu tych wyników otrzymujemy liczbę 1/5 ⋅ (102 + 230 + 91 + 90 + 100) = 122,6. Zauważmy jednak, że ocena eksperta 2 znacznie odbiega od pozostałych. Różne techniki estymacji grupowej w odmienny sposób radzą sobie z takimi sytuacjami. Można – w przypadku zbyt dużych rozbieżności wyników – przeprowadzić dyskusję, w której ekspert udzielający skrajnej odpowiedzi uzasadnia swoją odpowiedź. Następnie można przeprowadzić kolejną turę „głosowania”, do momentu uzyskania akceptowalnego poziomu różnic między poszczególnymi wynikami. Inna metoda mogłaby polegać np. na wzięciu mediany, czyli wartości dzielącej posortowany zbiór otrzymanych wartości na dwie połowy. W naszym przypadku posortowane odpowiedzi wyglądają następująco: 90, 91, 100, 102, 230. Wartością środkową jest 100 i taki jest końcowy wynik. Jak widać, jest on bardziej zbliżony do tego uzyskanego za pomocą struktury prac niż rezultat polegający po prostu na uśrednieniu wszystkich wyników. Wynika to stąd, że średnia jest dużo bardziej niż mediana wrażliwa na wartości odstające. Inną odmianą techniki grupowej jest tzw. estymacja trzypunktowa. Polega ona na tym, że każdy ekspert podaje trzy oceny: pesymistyczną, najbardziej prawdopodobną oraz optymistyczną. Następnie uśrednia się wyniki (lub bierze medianę) w obrębie tych trzech kategorii, otrzymując trzy średnie dla ocen: pesymistycznej p, najbardziej prawdopodobnej n i optymistycznej o. Na koniec uśrednia się te trzy wartości, najczęściej według wzoru:

gdzie E jest końcowym, ostatecznym szacunkiem. Na przykład, gdyby naszych pięciu ekspertów podało następujące oceny (gdzie przez wartość optymistyczną rozumiemy najmniejszą możliwą liczbę przypadków do zaprojektowania):

Eks pert

Wartoś ć optymis tyczna (min)

Wartoś ć najbardziej prawdopodobna

Wartoś ć pes ymis tyczna (max)

1

70

102

145

2

150

230

300

3

45

91

110

4

40

90

150

5

30

100

150

mediany 45

100

150

ostateczna estymacja wyniosła by

Zespół ekspertów szacuje zatem

całkowitą liczbę testów na około 99. estymacja trzypunktowa (ang. three point estimation) – metoda estymacji testów wykorzystująca oszacowane wartości dla „najlepszego”, „najgorszego” i „najbardziej prawdopodobnego” przypadku w sprawie, którą oceniamy, aby zdefiniować poziom pewności otrzymanego wyniku estymacji

22.2.5. Dane przemysłowe Przemysłowe dane testowe są bardzo przydatne w szacowaniu testów. Są to wartości parametrów związanych z testowaniem, obliczone na podstawie rzeczywistych projektów. Jeśli zupełnie nie mamy pojęcia, jak oszacować dany parametr, to dane przemysłowe mogą być dobrym punktem wyjścia. Najbardziej znane dane przemysłowe pochodzą od Jonesa. Na przykład w książce [198] podaje on wartości parametrów przemysłowych przedstawionych w tabeli 22.2. Tabela 22.2. Średnie przemysłowe parametrów dotyczących testowania

Czynność, zadanie, parametr

W artość

Czas trwania projektu (w os obomies iącach)

(FP/150) ⋅ FP 0.4

Wys iłek dla s tworzenia 1 przypadku tes toweg o

1 os obog odzina

Liczba potrzebnych przypadków tes towych

FP 1.2

Oczekiwana liczba defektów

FP 1.25

Ś rednia liczba defektów na punkt funkcyjny (US A)

5,00

Źródło defektów (wymag ania:projekt:kod:dokumentacja:reg res ja)

3:4:5:2:1

Efektywnoś ć us uwania defektów dla ins pekcji formalnej projektu

65%

Efektywnoś ć us uwania defektów dla formalnej ins pekcji kodu

60%

Efektywnoś ć us uwania defektów dla dobrze zaprojektowanej s uity tes towej

30%

Ś rednia efektywnoś ć us uwania defektów w projekcie (US A)

85%

Tego typu dane można wykorzystać do porównania naszego projektu ze średnią wartością przemysłową i zobaczyć, jak nasza organizacja wypada na tle innych. Można również użyć ich do szacowania. Na przykład, jeśli w wymaganiach odkryliśmy 30 defektów, to możemy spodziewać się, że w fazie projektowej będzie ich około 40, uwzględniając stosunek 3:4 tych wartości. Jones w swoich badaniach zamiast linii kodu używa punktów funkcyjnych, które są niezależne od użytego języka programowania. Aby przeliczyć punkt funkcyjny na liczbę linii kodu, należy skorzystać ze specjalnej tabeli przeliczników PF/LOC dla określonego języka. Wiele takich tabel jest dostępnych w internecie. Więcej o punktach funkcyjnych Czytelnik znajdzie w punkcie 41.2.1.

22.2.6. Analiza punktów testowych (Test Point Analysis) Technika APT pochodzi z metodologii TMap [28]. Jest ona bardzo ciekawą, choć nietrywialną i niełatwą do przeprowadzenia metodą estymacji wysiłku testowego. Nawet, jeśli Czytelnik nie zamierza bezpośrednio jej stosować, to warto przestudiować APT, aby zobaczyć, w jaki sposób można budować modele predykcyjne, używając danych historycznych. Analiza tej metody pozwala również zobaczyć, jakie czynniki według niej wpływają na wielkość wysiłku testowego.

analiza punktów testowych, APT (ang. Test Point Analysis, TPA) – formuła umożliwiająca szacowanie czasu i wysiłku testowania oparta na punktach funkcyjnych [28] Szacowanie testu czarnoskrzynkowego zależy od trzech elementów: rozmiaru testowanego systemu, strategii testowej oraz poziomu produktywności. Pierwsze dwa elementy wyznaczają wielkość pracy testera w terminach punktów testowych. Mnożąc ją przez poziom produktywności, otrzymamy czas potrzebny do wykonania tej pracy. Przy obliczaniu tych trzech komponentów należy uwzględnić różne czynniki wpływające na zmianę wyniku pomiaru. Ogólny schemat procedury przeprowadzania APT jest pokazany na rysunku 22.2.

Rysunek 22.2. Schemat APT Aby przeprowadzić Analizę punktów testowych, należy mieć dostęp do projektu systemu, dzięki któremu będziemy w stanie podzielić system na funkcje czy też funkcjonalne obszary. Dynamiczne punkty testowe określają liczbę testów dynamicznych, związanych z uruchamianiem programu. Na liczbę tę mają wpływ dwa rodzaje czynników: jakościowe oraz zależne od funkcji. W podejściu APT wykorzystuje się 4 charakterystyki jakościowe z normy ISO 9126 [3]:

funkcjonalność Qf

bezpieczeństwo Qb użyteczność Qu wydajność Qw

Charakterystyki te muszą być mierzalne, tzn. musi istnieć odpowiednia technika ich testowania. Każdą z nich ocenia się na pięciopunktowej skali: 0, 3, 4, 5, 6, przy czym wartością domyślną jest 4. Wartość ta określa wysiłek, jaki należy włożyć w testowanie tej właśnie charakterystyki podczas testów dynamicznych. Na podstawie ocen oblicza się czynnik zależny od jakości, Qd :

Czynniki zależne od funkcji to: ważność dla użytkownika W, czyli jak bardzo dana funkcja jest istotna z punktu widzenia użytkownika; stosowana tu reguła inżynierska mówi, że około 25% funkcji powinno mieć stopień ważności „wysoki” (W = 12), tyle samo – kategorię „niski” (W = 3), a 50% – kategorię „średni” (W = 6). Domyślna wartość to 6; intensywność użycia I, czyli jak często dana funkcja jest używana. I = 12, jeśli funkcja jest używana w sposób nieprzerwany w ciągu dnia, I = 4, jeśli jest używana wielokrotnie w ciągu dnia, I = 2, jeśli jest używana tylko kilka razy w ciągu dnia lub tygodnia. Domyślna wartość to 4; stopień powiązania S (ang. interfacing), czyli wartość określająca, w jakim stopniu zmiana w danej funkcji wpłynie na cały system. Bardzo pomocna w definiowaniu tego stopnia jest macierz CRUD. Możliwe wartości to: S = 2 jeśli stopień powiązania jest niski, S = 4 jeśli średni oraz S = 8 jeśli wysoki. Domyślna wartość do 4; złożoność Z, czyli jak skomplikowana jest struktura algorytmu. Możliwe wartości to: Z = 3 jeśli funkcja zawiera nie więcej niż 5 warunków3, Z = 6 jeśli zawiera od 6 do 11 warunków, Z = 12 jeśli zawiera więcej niż 11 warunków. Wartość domyślna to 6; jednolitość J, która wynosi 1 (wartość domyślna) lub 0,6. Tę drugą wartość stosuje się, gdy do testowania funkcji można wykorzystać już istniejącą specyfikację (np. gdy funkcja jest kopią innej funkcji lub jest do

niej bardzo podobna). Wtedy zakłada się, że funkcja ta wnosi jedynie 60% swoich punktów funkcyjnych do całkowitej sumy. Na podstawie ocen powyższych charakterystych jest wyliczany czynnik zależny od funkcji f, Df : Na podstawie wartości Qd , Df oraz FPf – liczby punktów funkcyjnych dla funkcji f – oblicza się liczbę dynamicznych punktów testowych TPf według wzoru TPf = Qd ⋅ Df ⋅ FPf Statyczne punkty testowe Qs zlicza się na podstawie ocen charakterystyk

statycznych, czyli testowanych statycznie. Na przykład bezpieczeństwo mogłoby być testowane przy użyciu listy kontrolnej. Każda charakterystyka statyczna Qi wnosi do sumy Qs wartość 16, przy czym masymalna możliwa wartość Qs nie może przekroczyć 96: Qs = min{96, ∑ iQi} Całkowita liczba punktów testowych TP wyliczana jest ze wzoru: gdzie FP jest całkowitą liczbą punktów funkcyjnych systemu (minimum 500). Liczbę podstawowych godzin testowych PG oblicza się, mnożąc całkowitą liczbę punktów testowych przez dwa czynniki: produktywności P oraz środowiska E: PG = TP ⋅ P ⋅ E Czynnik produktywności P określa liczbę godzin testowych wymaganych dla jednego punktu testowego. Im jest on wyższy, tym więcej czasu potrzeba na przetestowanie jednego punktu testowego. Współczynnik P jest określany na podstawie doświadczenia, wiedzy i umiejętności zespołu testerskiego. W praktyce powinien on mieścić się między 0,7 a 2. Liczba godzin testowych zależy również od czynników środowiskowych: E = En + Etd + Ept + Esd + Est + Et gdzie: En oznacza stopień, w jakim testowanie jest zautomatyzowane za pomocą narzędzi; możliwe oceny: 1 (testowanie wykorzystuje język zapytań, np. SQL oraz narzędzie typu capture-replay), 2 (testowanie

wykorzystuje język zapytań, ale nie narzędzie typu capture-replay), 4 (brak narzędzi). Domyślna wartość to 4; Etd oznacza jakość testowania we wczesnych fazach. Jakość i zakres tego testowania może wpłynąć na zmniejszenie ilości funkcjonalności, którą należy przetestować lub na osłabienie kryteriów wymaganego pokrycia; możliwe oceny: 2 (plan testów dostępny i zespół testowy jest z nim zaznajomiony), 4 (plan testów dostępny), 8 (brak planu testów). Domyślna wartość: 8; Ept oznacza jakość podstawy testów (ang. test basis), na której będą oparte testy. Jakość ta wpływa na czas faz przygotowania i projektu testów; możliwe oceny: 3 (wykorzystuje się standardy dokumentowania i wzory dokumentów oraz przeprowadza inspekcje), 6 (wykorzystuje się standardy dokumentowania i wzory dokumentów), 12 (dokumentacja nie została stworzona przy użyciu określonych standardów i wzorów). Domyślna wartość: 12; Esd opisuje środowisko deweloperskie, w którym system jest realizowany. Od środowiska zależy stopień zapobiegania różnym błędom oraz niewłaściwym metodom pracy; możliwe oceny: 2 (system tworzony przy użyciu języka 4 generacji ze zintegrowaną bazą danych zawierającą wiele więzów), 4 (system tworzony przy użyciu języka 4 generacji, w możliwej kombinacji z językiem 3 generacji), 8 (system tworzony wyłącznie przy użyciu języka 3 generacji, takiego jak np. COBOL, Pascal itp.). Domyślna wartość: 4; Est opisuje stopień uprzedniego wykorzystania środowiska testowego, w którym aktualnie pracujemy. W sprawdzonym środowisku pojawia się mniej problemów opóźniających prace testerskie; możliwe oceny: 1 (środowisko było wcześniej wielokrotnie wykorzystywane), 2 (testy wykonywane będą w nowym środowisku, podobnym do wcześniej używanego), 4 (testy będą wykonywane w zupełnie nowym środowisku). Domyślna wartość: 1; Et opisuje stopień, w jakim istniejące testalia umożliwiają przeprowadzenie testu. Istnienie reużywalnych testaliów przyspiesza proces projektowania i wykonywania testów; możliwe oceny: 1 (dostępny do użycia ogólny zestaw danych, np. w postaci tabel oraz przypadki testowe dla danego testu), 2 (dostępny do użycia ogólny

zestaw danych), 4 (brak możliwych do użycia testaliów). Domyślna wartość: 4. Na całkowitą liczbę godzin testowych mają wpływ czynności związane z zarządzaniem, monitorowaniem i kontrolą procesu testowego. Narzut na zarządzanie NZ jest opisany wzorem

gdzie: Trz odzwierciedla rozmiar zespołu testowego i związany z tym narzut komunikacyjny zabierający pewną ilość czasu; możliwe wartości: 3 (zespół liczy nie więcej niż 4 osoby), 6 (zespół liczy od 5 do 10 osób), 12 (zespół liczy ponad 10 osób). Domyślna wartość: 6; Tnz odzwierciedla wykorzystanie narzędzi zarządzania w zakresie planowania i kontroli. Narzędzia takie pomagają w automatyzacji części pracy menedżera i tym samym ułatwiają przebieg całego procesu; możliwe wartości: 2 (są dostępne zarówno system rejestrowania czasu oraz system do zarządzania defektami), 4 (jest dostępny tylko jeden z w/w systemów), 8 (nie używane są żadne narzędzia automatyzacji zarządzania). Domyślna wartość: 4. Domyślny narzut na zarządzanie wynosi 10% (6+4), ale może wynosić 5–20%. Całkowita liczba godzin testowych jest obliczana ze wzoru TH = PG ⋅ NZ Mając określoną całkowitą liczbę godzin testowych, możemy ją rozbić na poszczególne fazy procesu testowego4 w następujący sposób: przygotowanie (plan, projekt): 10%; specyfikacja testów: 40%; wykonanie i raportowanie: 45%; faza zamykania i raport końcowy: 5%. Metoda APT jest przykładem dosyć skomplikowanego i rozbudowanego modelu, choć i tak nie uwzględnia wszystkich możliwych czynników (np. testowania zakontraktowanego). Model taki ma największą wartość dla

organizacji dopiero po zebraniu wystarczającej ilości danych historycznych, które pozwolą dostroić go do specyfiki tej organizacji.

22.2.7. Modele matematyczne (parametryczne) Model parametryczny ma postać y = f(x1, x2, …, xn) + e gdzie y jest przewidywaną wartością (tzw. zmienną zależną), xi to tzw. zmienne niezależne, czyli czynniki wpływające na wartość y, f jest funkcją, która opisuje zależność między zmienną zależną a czynnikami, a ε oznacza błąd modelu. Model matematyczny buduje się na podstawie danych historycznych, dopasowując do nich kształt funkcji f. Przykładem modelu matematycznego jest model APT opisany w punkcie 22.2.6. Innym przykładem jest np. model oczekiwanej liczby defektów opisany w tabeli 22.2. Ma on następującą postać: y = x1,25 gdzie y to szacowana liczba defektów, a x – liczba punktów funkcyjnych programu. Znając liczbę punktów funkcyjnych w naszym programie, możemy ją podstawić do wzoru i otrzymać predykcję liczby defektów w kodzie źródłowym, np. dla programu o wielkości 50 punktów funkcyjnych model przewiduje, że będzie w nim 501.25 ≈ 133 defektów. Modele parametryczne służące do szacowania wysiłku omówione zostaną w rozdziale 44. Szczegółowe kwestie dotyczące budowania modeli matematycznych zdecydowanie wykraczają poza zakres tej książki. Informacje na ten temat można znaleźć np. w [199].

22.3. Negocjacje i redukcja zakresu testów Negocjacje są zwykle nieodłączną częścią planowania i szacowania testów. Prawie zawsze trzeba przekonać kogoś – kierownika, menedżera, dyrektora, sponsora projektu itd. – że budżet o który prosimy, będzie wykorzystany z pożytkiem dla projektu. Nie zawsze możemy dostać tyle, ile byśmy chcieli, ale czasem możemy otrzymać więcej zasobów (pieniędzy, czasu), jeśli przedstawimy tzw. business case dla naszego projektu, czyli uzasadnienie dla jego przeprowadzenia. Takie uzasadnienie może mieć w szczególności formę:

dowodu słuszności koncepcji (ang. proof-of-concept), gdy wykonaliśmy już jakąś część pracy, prototyp bądź fragment systemu i możemy pokazać (udowodnić), że nasze rozwiązanie działa i pozwoli osiągnąć założone cele, jeśli zostanie zbudowane w całości; symulacji, w której na podstawie danych wejściowych jest przeprowadzana symulacja procesu zakończona obliczeniem kluczowych dla interesariuszy miar; symulacje najczęściej wykonuje się, aby oszacować koszt testowania lub wybrać najbardziej opłacalny spośród kilku możliwych wariant rozwiązania; próby przekonania przez rozmowę i wykorzystywanie mniej technicznych argumentów, a także: znajomości, umiejętności miękkich czy pozycji w organizacji. Wszystkie wymienione rozważania ograniczenia zakresu testów. Bardzo

odnoszą się również do kwestii często koszty prac projektowych

przekraczają planowane (i dostępne) kwoty, co skutkuje potrzebą ograniczenia zakresu projektu. Kierownik testów w takiej sytuacji może starać się przekonać kierownictwo wyższego szczebla, że ograniczanie testowania nie jest najlepszym pomysłem. Jeśli jednak wiadomo, że nie unikniemy cięć należy przeprowadzić analizę wpływu, która pozwoli nam spriorytetyzować (np. pod względem poziomu ryzyka) zadania, które trzeba ograniczyć lub wręcz z nich zrezygnować. Można również pójść na kompromis w zakresie obniżenia kryteriów zakończenia testów, kryteriów pokrycia itp. Podczas negocjacji bardzo ważne są umiejętności miękkie kierownika testów. Jest to dziedzina, w której bardzo przydają się zdolności negocjacyjne, cierpliwość, umiejętność słuchania, planowania taktycznego i strategicznego, przekonywania do swoich racji, zdobywania przychylności innych ludzi. Niestety nie są to cechy, których da się od początku do końca wyuczyć – albo się je ma, albo nie.

1 Często zamiast średniej wykorzystuje się medianę, która jest mniej wrażliwa na wartości skrajne. 2 Jest to metoda bardzo popularna w szacowaniach dla projektów agile’owych. Wykorzystuje się w niej specjalną talię kart z liczbami oznaczającymi ocenę złożoności. Każdy członek zespołu dysponuje takim samym zestawem kart, np.: 1, 2, 3, 5, 8, 13, 21, 99, z którego wybiera się jedną i kładzie na środek stołu wartością do dołu. Następnie wszystkie karty ze środka odsłania się i uśrednia lub bierze medianę. 3 Na przykład instrukcja if A and B then C zawiera dwa warunki, A i B, ponieważ można ją zapisać jako podwójną instrukcję if: if A then { if B then C }. Instrukcja case z n wariantami liczy się jako n – 1 warunków. 4 Analiza punktów testowych pochodzi z metodyki TMap, dlatego również i poszczególne fazy dotyczą procesu testowego według TMap.

23. Nadzór i kontrola postępu testów

23.1. Wprowadzenie Nadzór i kontrola postępu testów jest jednym z najważniejszych zadań kierownika testów. Zadaniem tej fazy jest ciągłe monitorowanie stanu procesu testowego i reagowanie, gdy odbiega on od planu. Aby poprawnie realizować nadzór i kontrolę, kierownik testów musi mieć dane o stanie procesu. Informacja ta jest uzyskiwana w postaci pomiaru różnego rodzaju metryk, dzięki czemu możemy: obliczyć zwrot z inwestycji (ang. Return of Investment, ROI), tzn. czy proces testowania przynosi nam wymierne korzyści; ocenić i porównać różne możliwe sposoby postępowania; ocenić i kontrolować wydajność procesu testowego; ocenić i kontrolować poprawę procesu testowego; zbudować system „wczesnego ostrzegania” przed problemami; zbudować modele predykcyjne, np. przewidujące końcową jakość produktu, pozostały czas do ukończenia testowania itp.; porównać nasz proces z procesami konkurencji. Można wyróżnić następujące kategorie metryk: metryki projektu – opisujące postęp w kierunku zdefiniowanych uprzednio celów bądź kryteriów wyjścia dla projektu, takich jak odsetek wykonanych czy zdanych testów; metryki produktu – opisujące atrybuty wytwarzanego oprogramowania, np. gęstość defektów czy złożoność cyklomatyczna poszczególnych modułów;

metryki procesu – mierzące zdolność procesu testowego lub wytwórczego, np. odsetek defektów znalezionych/usuniętych w danej fazie projektu; metryki dotyczące ludzi – mierzące produktywność, dostępność, wydajność czy efektywność poszczególnych ludzi bądź zespołów, np. liczba osobogodzin poświęcona na daną czynność testową. Niektóre metryki mogą należeć do dwóch lub więcej kategorii. Na przykład częstotliwość pojawiania się defektów może być traktowana jako metryka projektowa (cel: brak nowych defektów w ciągu 1 tygodnia), produktowa (jej niska wartość może oznaczać wyższą jakość produktu, skoro testowanie nie jest w stanie wykryć więcej błędów), a także jako wskaźnik wydajności procesu (np. testowanie

może

znajdować

większość

defektów

we

wczesnych

fazach

wykonywania testów). wskaźnik wydajności, kluczowy wskaźnik wydajności KPI (ang. performance indicator, key performance indicator) – metryka wysokiego poziomu określająca poziom skuteczności i/lub efektywności, wykorzystywana do śledzenia i kontroli postępującego wytwarzania, np. poślizg w wytwarzaniu oprogramowania [197] wskaźnik wydajności testu (ang. test performance indicator) – metryka wysokiego poziomu mierząca skuteczność i/lub efektywność, używana do prowadzenia i kontrolowania postępu testowania; przykładem może być np. odsetek wykrytych błędów Metryk dotyczących ludzi należy używać ze szczególną uwagą. Kierownictwo bardzo często popełnia błąd, traktując metryki procesowe jako metryki dotyczące ludzi i ocenia poszczególnych członków zespołu na ich podstawie. Takie postępowanie zawsze kończy się źle, prowadzi bowiem do obniżenia morale oraz efektywności pracy, zwiększa prawdopodobieństwo konfliktu oraz powoduje tzw. utratę ducha zespołu. Jeśli metryki dotyczące ludzi są wykorzystywane podczas czynności technicznych, nie powinny być raportowane wprost do kierownictwa wyższego szczebla. Jeśli muszą być one wykorzystywane, to lepiej opisywać za ich pomocą całe zespoły, a nie poszczególne osoby.

Kierownik testów prezentuje zebrane metryki na różnych spotkaniach mających na celu omówienie postępu projektu, w szczególności – postępów w testowaniu. Kierownik musi umieć interpretować i analizować raporty, dane, wykresy. Na ich podstawie podejmowane są ważne decyzje projektowe, dlatego metryki do tych raportów powinny być zbierane w sposób poprawny i nie mogą być obciążone zbyt dużym błędem. Więcej informacji na temat teorii pomiaru podajemy w rozdziale 39. W szczególności, podczas tworzenia programu zbierania metryk należy zwrócić uwagę na następujące kwestie: wybór i definicja metryk – do pomiarów należy wybrać wyłącznie te metryki, które będą niezbędne dla dalszych działań; każda metryka powinna być zdefiniowana w sposób operacyjny, tzn. opisujący sposób jej pomiaru, dzięki czemu pomiar będzie spójny: dwie osoby mierzące tą samą rzecz otrzymają takie same wyniki; zbieranie metryk – należy dążyć do tego, aby w jak największym stopniu zautomatyzować proces zbierania metryk oraz obliczania metryk złożonych (definiowanych na podstawie innych metryk); automatyzacja zapobiega popełnianiu błędów; należy śledzić również wszelkie odchylenia od „standardowych” czy spodziewanych wyników – dziwne trendy bądź duża wariancja wyników mogą sugerować istnienie jakiegoś problemu w procesie testowym lub w procedurze zbierania metryk; raportowanie – tak jak zbieranie powinno być w jak największym stopniu zautomatyzowane; zawartość i stopień ogólności poszczególnych raportów powinien być dostosowany do odbiorców tych dokumentów; raporty powinny być czytelne oraz wykorzystywać graficzne formy prezentacji danych; należy upewnić się, że każdy wytwarzany raport będzie wykorzystywany – produkowanie raportów, których nikt nie czyta, jest stratą czasu i zasobów; poprawność metryk – zbierane dane powinny odzwierciedlać rzeczywistość i nie być obciążone zbyt dużym błędem czy poziomem niepewności; poprawność powinna być sprawdzana, zanim metryki zostaną wykorzystane do raportowania oraz analiz. Testowanie polega na tworzeniu testów w celu wykonania programu z intencją znajdowania defektów tak, aby pokryć jak najwięcej jego obszarów i tym samym

zminimalizować pozostające w produkcie ryzyko, zwiększając tym samym pewność co do wysokiej jakości tworzonego oprogramowania. Można zatem wyróżnić pięć podstawowych wymiarów, w których monitoruje się postęp testów. Są one przedstawione na rysunku 23.1.

Rysunek 23.1. Wymiary postępu testowania W kolejnych podrozdziałach omówimy przykładowe metryki dla każdego z tych wymiarów. Cztery pierwsze wymiary: ryzyko, defekty, testy i pokrycie

można ująć ilościowo. Piąty – pewność – jest bardziej subiektywny, dlatego jego pomiar odbywa się zwykle za pomocą ankiet, wywiadów czy kwestionariuszy. Pewność można również wyrazić jako funkcję pozostałych wymiarów, choć najczęściej jest ona raportowana jako miara subiektywna. Często kierownicy testów skupiają się na kilku metrykach (np. defekty i testy), a następnie produkują olbrzymią liczbę raportów, przedstawiając te metryki oraz ich pochodne na wiele różnych sposobów. Takie podejście zwykle nie ma sensu – przede wszystkim nikt nie będzie czytał kilkudziesięciu raportów. Ponadto, ich treść nie stanowi zbyt dużej wartości dodanej, skoro wszystkie tak naprawdę przedstawiają to samo, być może w innej tylko formie. Dobre podejście do stosowania metryk w procesie kontroli i nadzoru polega na tym, aby wybrać niewielki zbiór metryk pokrywających wszystkie wymiary przedstawione na rysunku 23.1 i przedstawić je w możliwie zwartej, czytelnej formie w niewielkiej liczbie raportów.

23.2. Przykłady metryk W dalszych podrozdziałach będziemy się odwoływać do przykładowego projektu, w którym zidentyfikowano 6 ryzyk. Zaprojektowano 10 testów, które pokrywają te ryzyka tak, jak przedstawiono to w tabeli w górnej części rysunku 23.2 (na przykład ryzyko R1 pokryte jest czterema testami: T1, T3, T6 i T7; test T9 pokrywa dwa ryzyka: R4 i R6 itd.).

Rysunek 23.2. Macierz identyfikowalności testy – ryzyka oraz historia testów przykładowego projektu W dolnej tabeli z rysunku 23.2 pokazano, jak w kolejnych tygodniach przebiegał proces testowy. Dla każdego tygodnia mamy dane o zaplanowanych testach do wykonania oraz o tym, które testy zostały wykonane, w podziale na zdane i niezdane. Dla testów niezdanych jest podana liczba defektów wykrytych wskutek ich wykonania. Ponadto, mamy informację o tym, ile defektów zostało usuniętych w poszczególnych tygodniach. Zakładamy, że skuteczność usuwania defektów wynosi 100%, to znaczy każde usunięcie jest skuteczne i nie wprowadza nowych defektów.

23.2.1. Metryki ryzyka produktowego Przykładowe metryki ryzyka produktowego to: procent ryzyk pokrytych przez zdane testy;

procent ryzyk, dla których co najmniej jeden test nie przeszedł; procent ryzyk nieprzetestowanych w całości; procent pokrytych ryzyk podzielonych według kategorii ryzyka; procent ryzyk zidentyfikowanych po wstępnej analizie ryzyka; liczba zidentyfikowanych ryzyk w czasie. Do obliczania pierwszych czterech metryk jest potrzebne śledzenie testów do ryzyk. Dla przykładu z rysunku 23.2 możemy stworzyć raport o ryzykach pokazany na rysunku 23.3.

Rysunek 23.3. Raport z pokrycia ryzyk Planowany procent pokrytych ryzyk wynika z planowanego wykonania testów. W metryce % pokrytych ryzyk zakładamy, że każde pokrycie części ryzyka przez jakiś test ma taki sam wpływ jak inne. Mamy 18 krzyżyków w macierzy identyfikowalności, więc każdy z nich wnosi 1/18 ≈ 5,56% pokrycia. Ryzyko pokryte w całości to takie, dla którego wykonano wszystkie odpowiadające mu testy z macierzy identyfikowalności (np. w tygodniu 2 mamy jedno pokryte ryzyko, ponieważ w dwóch pierwszych tygodniach zostały zdane testy T1, T2 i T4, a ryzyko R3 jest w całości pokryte przez test T2). Z raportu wynika, że postęp pokrycia ryzyk nie odbiega istotnie od planu. Jedynie w tygodniach 1 i 4 były drobne opóźnienia, jednak od tygodnia nr 5 wszystko szło zgodnie z planem.

23.2.2. Metryki defektów Przykładowe metryki dotyczące defektów to: skumulowana liczba zgłoszonych defektów; skumulowana liczba naprawionych defektów; skumulowana liczba otwartych defektów (w naprawie); średni czas między awariami (MTBF); średni czas bezawaryjnej pracy (MTTF); średni czas do nieplanowanego uruchomienia systemu; częstotliwość zgłoszenia defektów/awarii; zgłoszone defekty w podziale na: miejsce (moduł, element testowy), fazę wprowadzenia, wykrycia, usunięcia, przyczynę źródłową, źródło (np. specyfikacja wymagań, testy regresji), priorytet/dotkliwość, kategorię (nowy/duplikat/odrzucony jako nie defekt); średni czas naprawy (różnica między czasem naprawy a czasem zgłoszenia); liczba źle wykonanych napraw. Średni czas między awariami (ang. Mean Time Between Failures, MTBF) oraz średni czas bezawaryjnej pracy (ang. Mean Time To Failure, MFFT) są najczęściej spotykanymi w literaturze metrykami opisującymi niezawodność systemu. Są również dosyć dobrze zbadane teoretycznie. Jednak ich stosowanie nastręcza pewne trudności związane m.in. z problematyczną definicją awarii, różnic przy testach jednego i wielu systemów oraz kosztów związanych z dokładnym monitorowaniem parametrów kolejnych awarii w czasie w projektach komercyjnych [200]. Dlatego np. w IBM wykorzystuje się metrykę średniego czasu do nieplanowanego uruchomienia systemu (ang. Mean Time to Unplanned IPL, MTI) jako metryki niezawodności oprogramowania. Więcej na temat metryk niezawodności Czytelnik znajdzie w rozdziale 46. Dla przykładu z rysunku 23.2 możemy stworzyć raport o defektach pokazany na rysunku 23.4.

Z raportu wynika, że od czwartego tygodnia nie zgłoszono żadnych nowych błędów. Powodem może być osiągnięcie wysokiego poziomu jakości tworzonego oprogramowania albo jakieś problemy w zespole testowym. Kierownik testów powinien upewnić się co do powodów tego stanu rzeczy. Może np. przeanalizować raport z przypadków testowych

Rysunek 23.4. Raport o defektach (patrz p. 23.2.3), aby sprawdzić, czy w okresie dwóch ostatnich tygodni były planowane, implementowane i wykonywane jakieś przypadki testowe. Jeśli tak, należy przeanalizować ich niską skuteczność (z raportu wynika bowiem, że w tym czasie nie znaleziono żadnych nowych defektów). Zastanawiający jest również brak zgłoszeń w drugim tygodniu projektu. Wyjaśnienie tego faktu może być trywialne (np. urlop testerów), ale może sygnalizować również jakieś poważniejsze problemy. Inny przykład raportu dotyczącego defektów pokazano na rysunku 23.5. Najwięcej, bo aż 19 spośród wszystkich 31 wykrytych defektów było umiejscowionych w module B. Może on wymagać dodatkowego sprawdzenia, np. przez przeprowadzenie inspekcji jego kodu bądź dodatkowej fazy testów. Decyzję taką kierownik testów powinien podjąć na podstawie analizy skumulowanej liczby defektów modułu B, połączonej z analizą skumulowanej liczby przypadków

testowych dotyczących tego modułu. Rozważmy dwie sytuacje pokazane na rysunku 23.6. W obu liczba wykonanych przypadków testowych

Rysunek 23.5. Raport o defektach w podziale na ich umiejscowienie

Rysunek 23.6. Dwa raporty o testach i defektach dla modułu B jest taka sama. Na wykresie po lewej stronie skumulowana liczba defektów wyraźnie stabilizuje się, zmierzając asymptotycznie do pewnej wartości. Oznacza

to, że z biegiem czasu, mimo dostarczania nowych przypadków testowych, nowe defekty nie są znajdowane. Na wykresie po prawej stronie jest dokładnie na odwrót – nowe przypadki testowe powodują wciąż wykrywanie nowych defektów. Sytuacja pierwsza pozwala stwierdzić, że większość defektów w module B została wyeliminowana i nie musimy kontynuować testów. Sytuacja druga pokazuje, że moduł wciąż nie jest wystarczająco dobrze przetestowany.

23.2.3. Metryki przypadków testowych Przykładowe metryki dla przypadków testowych to: całkowita liczba zaprojektowanych, zaimplementowanych, wykonanych, zdanych, niezdanych, zawieszonych, pominiętych przypadków testowych; liczba testów w podziale na moduły produktu oraz poziomy i fazy testowania; stan testów regresji i testów potwierdzających; liczba godzin planowanych na testy vs. liczba rzeczywistych godzin; dostępność środowiska testowego (jako % czasu, w którym środowisko jest udostępnione testerom); liczba testów w podziale na wykonywane automatycznie i ręcznie; średni czas wykonywania jednego testu. Dla przykładu z rysunku 23.2 możemy stworzyć raport o defektach pokazany na rysunku 23.7.

Rysunek 23.7. Raport o przypadkach testowych Inny przykład raportu o testach, uwzględniający wysiłek oraz podział testów ze względu na charakterystyki jakościowe, jest pokazany na rysunku 23.8. Z raportu wynika, że wysiłek zaplanowany na czwartą iterację został nieco przeszacowany (o ok. 8%). Testy funkcjonalności oraz dokumentacji zajęły więcej czasu niż planowano. Pozostałe obszary wymagały mniejszego lub identycznego z zaplanowanym wysiłku. Na koniec iteracji mamy 6 niezdanych testów, z czego 4 dotyczą funkcjonalności. Nie wykonano 3 testów wydajności (należy sprawdzić, dlaczego) oraz pominięto jeden test bezpieczeństwa (być może np. ze względu na zmianę wymagań).

Rysunek 23.8. Raport o testach uwzględniający wysiłek i podział na cechy jakościowe

23.2.4. Metryki pokrycia Przykładowe metryki pokrycia to: liczba (procent) modułów;

pokrytych

wymagań,

elementów

projektowych,

liczba (procent) pokrytych ryzyk; liczba

(procent)

pokrytych

konfiguracji

(np.

dla

testowania

kombinatorycznego); liczba pokrytych przypadków użycia; stopień pokrycia kodu, warunków, rozgałęzień, decyzji oraz innych elementów pokrycia dla testów białoskrzynkowych; stopień pokrycia klas równoważności, wartości brzegowych, stanów maszyny stanowej oraz innych elementów pokrycia dla testów czarnoskrzynkowych. Przykładowy raport z pokrycia jest przedstawiony na rysunku 23.9. Widzimy, że moduł B został całkowicie przetestowany pod względem pokrycia instrukcji i decyzji. Oczywiście nie oznacza to, że wszystkie błędy zostały znalezione. Kierownik testu może wykorzystać skumulowany raport o defektach, aby zobaczyć, jaka jest dynamika znajdowania usterek w tym module. Z kolei

moduł A zdecydowanie wymaga dalszego testowania, ponieważ prawie 40% decyzji oraz 60% warunków nie zostało pokryte przez ani jeden test. Jeśli sytuacja ta nie ulegnie poprawie w ciągu najbliższych dni, to należy się zastanowić, czy odpowiednie instrukcje i warunki są w ogóle osiągalne. Być może trzeba będzie przeprowadzić analizę statyczną bądź inspekcję tego modułu.

Rysunek 23.9. Raport z pokrycia

23.2.5. Metryki pewności Na podstawie metryk pewności kierownictwo decyduje, czy produkt jest już gotowy do wydania klientowi. Określenie tej gotowości jest sprawą wysoce nietrywialną. Nierzadko jakaś część oceny zawiera element subiektywny. Kan [200] proponuje, z własnego doświadczenia, rozważenie w tej kwestii przynajmniej następujących zagadnień: stabilność, niezawodność i dostępność systemu; liczbę defektów; nierozwiązane problemy krytyczne; wstępną ocenę klienta;

inne parametry jakości istotne dla danego produktu, wymagań klienta i rynku (np. łatwość użytkowania, wydajność, bezpieczeństwo czy skalowalność). stabilność (ang. stability) – zdolność produktu oprogramowania do unikania niespodziewanych zachowań w wyniku modyfikacji w oprogramowaniu [3]. Patrz także: pielęgnowalność Teoretycznie, produkt powinien być uznany za niegotowy do wydania, jeśli przynajmniej jedna metryka wewnątrzprocesowa nie osiąga założonych wartości. W praktyce jednak bardzo rzadko zdarza się, aby wszystkie metryki mieściły się w dopuszczalnych granicach. Wtedy należy odpowiedzieć sobie na następujące pytania [200]: co powoduje niewłaściwą wartość metryki? jaki wpływ ma niespełnienie założeń na jakość operacyjną produktu? co można zrobić, aby zmniejszyć ryzyko możliwych problemów? jak dalece od założeń odbiega wartość danej metryki?

23.3. Zarządzanie testowaniem opartym na sesji Testowanie eksploracyjne, opisane w podrozdziale 10.5, jest o wiele mniej ustrukturalizowaną metodą niż techniki formalnego projektowania przypadków testowych. Istnieje jednak metoda efektywnego zarządzania nim na podstawie danych ilościowych, nazwana zarządzaniem testowaniem opartym na sesji (ang. session-based test management) [201]. Podstawowym pojęciem wykorzystywanym w tej technice jest sesja, czyli czas, w którym tester przeprowadza testowanie eksploracyjne na podstawie karty testu (ang. test charter). Sesja trwa zwykle od około 45 minut do kilku godzin, następnie tester wypełnia raport z sesji. Z punktu widzenia czynności kontroli i nadzoru, podczas testowania opartego na sesji można używać następujących metryk: liczba ukończonych sesji; liczba zaplanowanych i odbytych sesji w ustalonym czasie; liczba znalezionych problemów; pokryte obszary funkcjonalne;

liczba osób uczestniczących w sesji; % czasu sesji poświęcony na przygotowanie do testowania; % czasu sesji poświęcony na testowanie; % czasu sesji poświęcony na badanie problemów. Analiza danych z powyższych metryk pozwala na ocenę efektywności tej formy testowania oraz niezbędnego wysiłku. Bach [201] zauważa, że zarządzanie testowaniem opartym na sesji ma największą wartość, jeśli po zakończeniu sesji kierownik testów przeprowadzi rzeczową rozmowę z testerem na temat odbytej sesji. Rozmowa taka nie powinna trwać więcej niż 20 minut. Bach proponuje następującą listę kontrolną, ułatwiającą kierownikowi przeprowadzenie rozmowy. karta testu czy przejrzałeś zaakceptowane karty testu związane z bieżącą sesją? czy zakres twoich testów odpowiada temu opisanemu w kartach testu? czas trwania czy rzeczywisty czas sesji był zbliżony do planowanego? czy sesja przebiegała w sposób ciągły i nieprzerwany? sesja czy przestrzegane były reguły przeprowadzania sesji? czy przestrzegana w ramach sesji?

była

kolejność

wykonywanych

czynności

pliki z danymi czy były wykorzystywane zewnętrzne pliki z danymi? jeśli tak, czy były oryginalne, czy powtórnie użyte, czy stworzone „w locie”? jeśli były modyfikowane, to dlaczego? czy do opisania testów można użyć jakiegoś kryterium pokrycia? uwagi czy sekcja z uwagami jest kompletna? czy uwagi, odnoszące się do karty testu, odpowiadają na pytanie „co się zdarzyło w tej sesji”? czy w uwagach jest zawarta informacja o pokryciu, wyroczniach i strategii?

czy jakieś fragmenty uwag mogą być powtórnie użyte w przyszłych sesjach? czy sekcja z uwagami zawiera jakieś błędy lub niejasności? defekty czy istnieje wystarczająca ilość informacji do zreprodukowania błędu? czy sekcja o defektach zawiera jakieś błędy lub niejasności? problemy jeśli nie znaleziono problemów, czy oznacza to pewność, co do ich braku, brak żadnych pytań pozostawionych bez odpowiedzi, brak przeszkód podczas testowania? czy są problemy wymagające podjęcia jakichś dalszych czynności? czy sekcja o problemach zawiera jakieś błędy lub niejasności? ogólne czy metryki oparte na tej przeprowadzone testowanie?

sesji

będą

wiernie

opisywać

czy wyniki sesji sugerują potrzebę kolejnej sesji w tym obszarze? czy ta sesja powinna być poprawiona lub rozszerzona?

24. Biznesowa wartość testowania

24.1. Wprowadzenie Testowanie nie stanowi na pierwszy rzut oka żadnej wartości dodanej. Wręcz przeciwnie – może się wydawać, że generuje ono jedynie koszty, bo produkt po przetestowaniu nie zmienia się, a testowanie wymaga przecież poniesienia sporych często nakładów. Jednak testowanie niesie ze sobą bardzo dużą wartość. Polega ona na tym, że oszczędzamy na kosztach, które ponieślibyśmy, gdyby produkt nie był ulepszony dzięki informacji zwrotnej od testerów. Ta nieoczywista, pośrednia w gruncie rzeczy wartość jest przyczyną, dla której wielu menedżerów nie potrafi właściwie docenić testowania. Często nie zwraca się uwagi na jakość i doświadczenie zatrudnianych inżynierów jakości (bo lepsi kosztują więcej), czas przeznaczony na testy jest pierwszy w kolejce do obcięcia w przypadku konieczności skrócenia projektu, inwestycje w środowisko testowe mają najniższy priorytet, itp. itd. Ulepszanie, o którym mowa może dotyczyć trzech aspektów [35]: tworzonego produktu; decyzji dotyczących produktu; procesów wykorzystywanych zarówno w testowaniu, jak i produkcji oprogramowania. Kierownik testów powinien znać wartość testowania oraz umieć przekonać zarówno członków swojego zespołu, jak i dyrektorów wyższego szczebla do tego, że inwestycja w testowanie jest opłacalna. Może temu służyć tzw. business case oparty na metodzie kosztu jakości opisanej w podrozdziale 24.2. Zysk z testowania jest możliwy dzięki temu, że koszt usuwania defektów rośnie z czasem. Zysk ten

wystąpi, jeśli koszt wykrycia defektów w fazach wcześniejszych nie przekroczy ewentualnych strat spowodowanych odkryciem i koniecznością naprawy defektu w fazach późniejszych bądź u klienta.

24.2. Koszt jakości Koszt jakości (ang. Cost of Quality, Cost of Software Quality, CoQ, CoSQ) jest techniką opisaną przez Jurana [202] pozwalającą ilościowo wyrazić koszt złej jakości produktu. Dlatego metodę tę często nazywa się kosztem złej jakości. Najprościej koszt ten określić za pomocą czasu lub wysiłku, ale najczęściej ujmuje się go w wartości pieniężnej. Według Krasnera [203], „koszt jakości oprogramowania to technika księgowa użyteczna w rozumieniu ekonomicznych kompromisów (ang. trade-off) związanych z dostarczaniem oprogramowania wysokiej jakości”. CoQ wyróżnia dwie główne kategorie kosztów: związanych z kontrolą oraz z awariami (rys. 24.1). koszt jakości (ang. cost of quality) – całkowity koszt związany z jakością, na który składają się koszty działań prewencyjnych, wykrycia, awarii wewnętrznych i zewnętrznych

Rysunek 24.1. Model kosztu jakości Generalnie im wyższe koszty kontroli, tym niższe koszty awarii, jednak niestety działa tu tzw. prawo malejących przychodów1 (ang. law of diminishing returns), to znaczy coraz większe nakłady na kontrolę dają coraz mniejsze zyski z zapobiegania awariom. Dlatego należy znaleźć optymalny punkt – złoty środek – miedzy wielkością kosztów kontroli, jakie decydujemy się ponieść, a zyskami wynikającymi z zapobiegania awariom. Omówmy teraz dokładniej cztery kategorie kosztów w CoQ. Koszty prewencji (ang. prevention costs) – dotyczą wszelkich czynności wykonywanych w celu zapobiegania powstawania defektów w produkcie. Przykładowe koszty prewencji dotyczą: przeprowadzania szkoleń, planowania jakości, oceny dostawcy, analiz przyczyny źródłowej, oceny zdolności procesu, ulepszania procesu.

Koszty wykrycia (ang. detection costs, appraisal costs) – są związane z analizowaniem produktów i usług w celu identyfikacji znajdujących się

w

nich

defektów.

Przykładowe

koszty

wykrycia

dotyczą:

przeprowadzania przeglądów i inspekcji, testowania, audytów produktów, procesów i serwisów, pomiaru jakości produktu oraz innych czynności dotyczących weryfikacji i walidacji oprogramowania. Koszty awarii wewnętrznej (ang. internal failure costs) – ponoszone w związku z usuwaniem defektów wykrytych podczas procesu wytwórczego, przed przekazaniem oprogramowania do klienta. Przykładowe koszty awarii wewnętrznej dotyczą: wytworzenia oprogramowania, które nie będzie używane, procesu zarządzania incydentami, debugowania, poprawiania defektu, przebudowy oprogramowania, przeprowadzenia ponownych przeglądów po poprawie, testów regresji oraz testów potwierdzających. Koszty awarii zewnętrznej (ang. external failure costs) – ponoszone w związku z usuwaniem błędów polowych, tzn. wykrytych po przekazaniu oprogramowania do klienta. Koszty te generalnie są związane z tymi samymi czynnościami, co w przypadku kosztów awarii wewnętrznej, a ponadto dotyczyć mogą: gwarancji, SLA (ang. Service Level Agreement), kar umownych, postępowań sądowych, strat poniesionych przez klienta, wydawania poprawek do oprogramowania, wsparcia technicznego, utraty reputacji lub wizerunku, niezadowolenia klienta, strat spowodowanych zmniejszeniem sprzedaży oraz utraty udziału w rynku. Rozważmy następujący przykład: koszt całego projektu wynosi 100 000 USD, z czego 10 000 USD to koszty szkolenia testerów, 35 000 USD to koszty testowania i inspekcji, 12 000 USD – koszty usuwania defektów podczas produkcji oprogramowania, a 6 000 USD – koszty usunięcia kilku awarii wykrytych przez klienta oraz wprowadzenia poprawek do aplikacji. Całkowity koszt jakości wynosi więc 10 000 USD + 35 000 USD + 12 000 USD + 6 000 USD = 61 000 USD, co stanowi 61% całkowitych kosztów projektu. Gdybyśmy zwiększyli koszty testowania, prawdopodobnie znaleźlibyśmy więcej błędów, przez co klient odkryłby mniej awarii i być może całkowity koszt byłby mniejszy. Im wyższa jakość, tym większy koszt jej zapewnienia, ale jednocześnie – tym mniejszy koszt awarii, ze względu na mniejszą liczbę pozostałych błędów.

Klasyczne podejście do analizy CoQ (patrz górny wykres na rys. 24.2) zakłada, że istnieje teoretyczny, optymalny poziom kosztów minimalizujący całościowy koszt jakości. W praktyce jednak punkt ten bardzo ciężko wyznaczyć, ponieważ wiele kosztów zewnętrznej awarii, takich jak niezadowolenie klienta czy utrata wizerunku, jest trudnych do zmierzenia. Współczesne podejście (dolna część rys. 24.2) jest oparte na empirycznych danych, które zdają się potwierdzać tezę, że ulepszanie procesu oraz techniki zapobiegania defektom zwiększają efektywność kosztów. Wyniki te sugerują, że można uzyskać niemal optymalny poziom jakości dla skończonego kosztu [204]. Na rysunku pokazano, że koszty wewnętrznej i zewnętrznej awarii można zredukować niemal do zera, przez co całkowity koszt jakości będzie praktycznie równy kosztowi prewencji i wykrywania defektów. Krasner [203] opisuje wyniki trwającego 3 lata badania 15 projektów w Raytheon Electronic Systems mającego na celu zbadać związek między kosztem jakości a poziomem dojrzałości procesu CMM2. Na najniższym poziomie dojrzałości (CMM 1) koszt jakości

Rysunek 24.2. Klasyczne i współczesne spojrzenie na ekonomiczny model kosztu jakości stanowił 55–67% całkowitych kosztów projektu. Gdy organizacja osiągnęła poziom trzeci (CMM 3), udział kosztów jakości spadł do 40%. Pod koniec

trzyletniego okresu udział ten spadł jeszcze bardziej, do ok. 15% całkowitych kosztów. Dane CoQ powinny być zbierane dla każdego projektu. Dzięki temu mogą być potem porównane z danymi historycznymi, wzorcowymi lub pochodzącymi od konkurencji. Można również przedstawić ich zmianę w czasie. Pozwala to: zidentyfikować nieefektywne obszary, które można będzie poprawić przez ulepszanie procesu tak, aby zredukować całkowity koszt wytwarzania; ocenić wpływ ulepszenia procesu (np. czy narzucenie większego formalizmu w postaci rygorystycznych inspekcji formalnych przyczynia się do redukcji kosztów jakości?); dostarczyć informację kompromisów między

dla przyszłych, opartych kosztami a wymaganiami

na ryzyku integralności

produktu [77].

24.3. Wartość ekonomiczna oprogramowania i problemy z nią związane Pojęcie „wartości ekonomicznej” oprogramowania, tak jak pojęcie jakości, jest niejasne i to z tych samych powodów. Każdy interesariusz projektu ma inne oczekiwania co do oprogramowania, zatem patrzy na wartość oprogramowania z innego punktu widzenia. Dla dostawcy najważniejszy będzie zysk, uzyskanie przewagi konkurencyjnej, czas wprowadzenia produktu na rynek (ang. time to market) czy ROI (ang. Return of Investment, czyli zwrot z inwestycji). Dla nabywcy wartość oprogramowania może oznaczać obniżenie kosztów operacyjnych organizacji, zwiększenie sprzedaży czy osiągnięcie większych zysków z powodu wykorzystania innowacyjnego modelu biznesowego przy użyciu oprogramowania. Gdy popatrzymy na oprogramowanie z punktu widzenia procesu jego produkcji, z jednej strony mamy nakłady ponoszone w związku z tworzeniem tego produktu, z drugiej zaś – poziom jego jakości. Cały problem polega na tym, aby minimalizować koszty przy maksymalizacji jakości. Skoro oprogramowanie jest rezultatem procesu produkcyjnego, projekty informatyczne powinniśmy analizować za pomocą klasycznych metod ekonomiki produkcji. Niestety, nawet

w obecnych czasach wciąż zbyt wiele projektów kończy się niepowodzeniem. Jednym z powodów jest błędne rozumienie ekonomiki produkcji w odniesieniu do oprogramowania. Trzy główne powody wypaczające jej rozumienie są następujące: Niekompletność danych historycznych. Estymację kosztu często prowadzi się na podstawie danych historycznych, ale dane te równie często są niekompletne i nie zawierają wszystkich poniesionych kosztów. Dlatego szacunki dla bieżącego projektu zwykle są alarmujące, bo pokazują, że w porównaniu do projektów historycznych wydajemy o wiele więcej pieniędzy, co wcale nie musi być prawdą. Często nie zbiera się wszystkich danych historycznych, a jedynie te dotyczące wybranych wycinków czy aspektów projektu. Aby uniknąć tworzenia fałszywych analiz, zwykle przeprowadza się wywiady z członkami zespołów projektowych. Na strukturę podziału prac bieżącego projektu odwzorowuje się dane historyczne podobnego projektu. Dla każdego zadania w tej strukturze, którego odpowiednika nie ma w danych historycznych, członkowie zespołu muszą określić, czy zadanie to w projekcie historycznym wystąpiło, czy nie. Jeśli tak, to proszeni są o ocenę kosztów tego zadania. Używanie metryki linii kodu (LOC). Metryka LOC narusza podstawowe zasady ekonomiki wytwarzania, ponieważ każdy język programowania ma inną „efektywność”: to, co możemy napisać w 4 liniach kodu języka typu C# czy Java, może zająć 30 linii w Asemblerze. Wobec tego nie można porównywać produktywności wyrażanej w LOC na jednostkę czasu dla programów pisanych w różnych językach programowania. Oczywiście można stosować tablice przeliczników między językami, jednak takie działanie zawsze jest obarczone dużym błędem. Innym problemem związanym z LOC jest to, że różne osoby rozumieją różnie pojęcie linii kodu. Komentarze, nagłówki funkcji, fizyczne linie w pliku z kodem źródłowym, logiczne instrukcje kodu, klamry wyznaczające bloki instrukcji – to tylko kilka przykładów rodzajów informacji, które mogą lub nie muszą być zliczane jako linie kodu. Zamiast LOC lepiej jest wykorzystywać punkty funkcyjne, które nie tylko są niezależne od przyjętego języka oprogramowania, ale umożliwiają również wczesną estymację praco- i kosztochłonności,

ponieważ można je obliczyć na podstawie dokumentacji projektowej, a nie tylko z gotowego kodu. Punkty funkcyjne spełniają podstawowe założenia ekonomiki produkcji. Metryka kosztu usunięcia defektu (ang. cost-per-defect). Jest to jedna z najstarszych miar jakości oprogramowania, mierząca godziny związane z naprawą defektów podzielone przez liczbę znalezionych defektów, przemnożone przez koszt jednej godziny pracy. Chociaż poprawna z matematycznego punktu widzenia, metryka ta ma zasadnicze, bardzo niebezpieczne wady. Ze względu na koszty stałe (np. koszt testowania i inspekcji), koszt usunięcia defektu zawsze będzie niższy dla projektów o większej liczbie błędów. Wraz ze wzrostem jakości liczba wykrytych defektów maleje, a więc koszt usunięcia jednego defektu rośnie – w granicy aż do nieskończoności, gdy liczba defektów osiągnie zerową wartość. Druga wada tej metryki polega na nieuwzględnieniu zysków, jakie osiągamy z wzrostu jakości. Lepsza jakość może polegać na tym, że pewne prace wykonujemy szybciej i lepiej. Jednak zyski z oszczędności na czasie nie są uwzględniane podczas obliczania kosztu usunięcia defektu. Większość defektów znajdowanych jest w początkowych fazach projektu, dlatego obserwujemy sztuczny wzrost metryki kosztu usunięcia defektu w czasie.

24.4. Dług technologiczny w kontekście testowania Dług technologiczny (ang. technical debt) to metaforyczne pojęcie oznaczające koszty, jakie zaciągamy przez niestaranne, niedbałe tworzenie systemu. Źle zaprojektowany, stworzony „na kolanie” kod może działać dziś, ale za pewien czas, gdy okaże się, że musimy go modyfikować, utrzymywać i poprawiać, koszty tych czynności będą o wiele większe niż te, które ponieślibyśmy na początku, inwestując w lepszą jakość kodu. Jedna z bardziej oczywistych form zaciągania długu technologicznego jest związana z brakiem testowania lub niedbałym jego przeprowadzeniem. Mechanizm ten działa podobnie jak w świecie finansów: gdy spłacamy pożyczkę, poza pożyczoną kwotą musimy spłacić jeszcze odsetki. Każda niedbałość, każdy brak pokrycia testami ryzyka, funkcjonalności czy fragmentu kodu działa jak

odsetki od długu. Im więcej odsetek i dłuższy czas ich zaciągania, tym większa kwota długu do spłacenia. Rezygnacja z czynności projakościowych takich jak weryfikacja, walidacja, testowanie czy inspekcje jest bardzo kusząca, gdyż koszty przeprowadzania tych czynności mogą być bardzo duże. W punkcie 6.1.6 pokazaliśmy analizę kosztów i zysków z przeprowadzania przeglądów. Jest to przykład ilościowego podejścia do obliczania długu technologicznego. Nie zawsze jednak należy dążyć do zerowej wartości długu technologicznego za wszelką cenę. Przykładem możliwego, a nawet pożądanego długu technologicznego może być projekt tworzony w organizacji typu start-up. Z jednej strony projekty takie zwykle są innowacyjne i nie do końca wiadomo, czy odniosą komercyjny sukces. Z drugiej zaś firma, aby przetrwać, musi generować zyski ze sprzedaży. Często w tego typu przedsięwzięciach nie warto więc inwestować olbrzymich kwot w zapewnianie jakości, lecz dążyć do jak najszybszego wydania produktu na rynek, aby uprzedzić konkurencję, zweryfikować realne zapotrzebowanie na dany produkt czy usługę i zacząć zarabiać. Dopiero po osiągnięciu sukcesu można spłacić zaciągnięty dług technologiczny. Dokładne obliczenie tego, ile długu zaciągamy przez zaniechanie określonych czynności lub wykonanie ich z mniejszą starannością jest w praktyce bardzo trudne. Zwykle wymaga posiadania danych historycznych oraz umiejętności modelowania matematyczno-statystycznego.

1 Przykład ilustrujący to prawo jest pokazany na rysunku 19.2. 2 CMM jest pięciostopniowym modelem opisującym dojrzałość procesu w organizacji. Więcej na temat tego modelu i podobnych modeli Czytelnik znajdzie w rozdziałach 32 oraz 38.3.

25. Testowanie rozproszone, zakontraktowane i zewnętrzne

Wytwarzane oprogramowanie nie jest testowane wyłącznie przez zespół testerów. W procesie tym może brać udział wiele innych podmiotów, takich jak: inne zespoły w ramach organizacji; inne zespoły w ramach oddziałów organizacji; dostawcy sprzętu i oprogramowania (ang. vendors); dostawcy usług testowych; wykupione usługi testowe (tzw. Testing as a Service,TaaS). testowanie zakontraktowane (ang. insourced testing) – testowanie wykonywane przez osoby pracujące w zespole projektowym, ale nie będące zatrudnione w organizacji Sytuacja ta schematycznie jest przedstawiona na rysunku 25.1. Testowanie przez wszystkie te podmioty Black nazywa testowaniem rozproszonym [31] i proponuje następujący, prosty proces dla jego efektywnego zarządzania: 1. Wybierz podmioty, które będą brały udział w testowaniu, opierając się na szczegółowych potrzebach procesu testowego. 2. Zaplanuj rozproszone prace testowe, uwzględniając wysiłek, czas, koszty i ryzyka. 3. Zarządzaj testowaniem zewnętrznym tak, jak swoim własnym. Różnica między testowaniem rozproszonym a zewnętrznym jest taka, że to pierwsze obejmuje wszystkie zespoły zewnętrzne wobec zespołu testowego

(w szczególności mogą to być inne działy naszej organizacji), natomiast testowanie zewnętrzne odnosi się do jednostek zewnętrznych wobec firmy. Omówimy teraz krótko rolę poszczególnych podmiotów w kontekście zarządzania testowaniem, ze szczególnym uwzględnieniem korzyści oraz zagrożeń.

Rysunek 25.1. Testowanie rozproszone, zakontraktowane i zewnętrzne

25.1. Zespoły w ramach organizacji Trzy szare prostokąty na rysunku 25.1 oznaczają organizację oraz jej oddziały. Prostokąt po lewej stronie to główny oddział firmy, w którym znajduje się zespół testowy. W tej samej siedzibie firmy mogą znajdować się: analitycy biznesowi, dział wsparcia technicznego, dział wsparcia klienta itd. Każdy z tych działów może mieć jakiś udział w testowaniu. Na przykład help desk może przeprowadzać testy akceptacyjne, ponieważ będzie wspierał klientów wykorzystujących gotowe oprogramowanie. Osoby z tego działu odgrywają wtedy rolę klientów,

przeprowadzając testy alfa. Wsparcie techniczne może brać udział w testach systemowych, dotyczących architektury lub innych kwestii technicznych. W proces testowy warto włączyć docelowych użytkowników, którzy mogą testować oprogramowanie w siedzibie firmy lub poza nią, w docelowym środowisku dla tworzonego produktu (testy beta). Zaangażowanie innych działów organizacji w testowaniu ma na celu zbudowanie ich zaufania do tworzonego produktu oraz lepsze zaznajomienie się z nim. Problemem może być jednak ograniczona ilość zasobów. Często działom tym trudno wygospodarować czas przeznaczony na testowanie, które wymaga dodatkowo komunikacji z działem testowym i/lub programistami. Dlatego warto już na etapie planowania projektu uwzględnić te aktywności w harmonogramie, przydzielając odpowiedni czas i budżet. Jeśli oprogramowanie jest tworzone dla konkretnego klienta, to w umowie można zawrzeć wymóg jego dostępności przez określony czas na potrzeby przeprowadzania testów. Może to być jeden lub kilku pracowników klienta oddelegowanych do naszego działu testowego lub określenie czasu, jaki klient ma obowiązek przeznaczyć na testy np. w siedzibie naszej firmy. Klienci są również przydatni podczas testowania pilotażowych wersji projektu. Osoby z działów innych niż testowy mają często odmienne, świeże i niezależne spojrzenie na produkt, dlatego mogą wykryć inne typy błędów niż zawodowi testerzy. Dodatkowo, klienci oraz analitycy biznesowi dysponują wiedzą dziedzinową, zatem są nieocenionym źródłem informacji na temat jakości użytkowej oprogramowania.

25.2. Zespoły w ramach innych oddziałów organizacji Oddziały firmy mogą być rozsiane po całym świecie. Na rysunku 25.1 po prawej stronie na dole są pokazane dwa oddziały organizacji. Jeden stanowi biuro sprzedaży/marketingu, drugi to oddział zatrudniający testerów. Mogą oni być członkami naszego zespołu testowego lub stanowić odrębny zespół. Biura sprzedaży oraz działy marketingu mogą pomóc szczególnie w tzw. testowaniu lokalizacji, czyli dostosowaniu oprogramowania oraz dokumentacji do języka klienta docelowego. Zwykle dotyczy to tłumaczenia z jednego języka na inny, ale może obejmować również takie kwestie jak format daty, godziny, zapisu liczb rzeczywistych itp. Na przykład w USA datę zapisuje się zwykle w postaci

mm/dd/yyyy, podczas gdy w krajach europejskich dd.mm.yyyy. W jednych krajach przecinek dziesiętny ma postać przecinka, w innych kropki itd. Dział marketingu może mieć obszerną wiedzę na temat zachowań, przyzwyczajeń i kultury danej grupy klientów. Takie informacje również są przydatne przy zapewnianiu wysokiego poziomu użyteczności aplikacji. Jeśli nasze oprogramowanie (np. na urządzenia mobilne) ma mieć automatyczną opcję umieszczania informacji w sieci społecznościowej, to dział marketingu może zwrócić nam uwagę, że wypuszczając oprogramowanie na rynek rosyjski, warto uwzględnić możliwość umieszczania informacji w serwisie vKontakte, który jest tam popularniejszy od Facebooka czy Twittera. Marketing ma też zazwyczaj dokładne informacje na temat tego, jakie modele urządzeń są wykorzystywane przez klientów danego kraju, co pozwala na optymalizację bądź priorytetyzację testów, zwłaszcza opartych na technikach kombinacyjnych. Problemy z testowaniem przez dział sprzedaży lub marketingu są podobne do tych opisanych we wcześniejszym rozdziale – brak czasu i budżetu. Warto włączyć czynności testowe do obowiązków tych działów, np. w organizacyjnej strategii testowej lub polityce testów. Dodatkowy kłopot to geograficzne rozproszenie pracowników organizacji, które utrudnia komunikację. Najefektywniej porozumiewamy się z drugim człowiekiem przez rozmowę twarzą w twarz. W rozmowach telefonicznych lub mailowych tracimy część przekazu (mowa ciała, ton głosu), przez co łatwo o nieporozumienia i przekłamania w przekazie. Jeśli drugi oddział firmy leży w innej strefie czasowej, to należy uwzględnić to w grafiku prac tak, aby godziny prac obu oddziałów przynajmniej częściowo się pokrywały. Jeśli tego nie zapewnimy, to należy liczyć się z opóźnieniem w komunikacji, bo nasza wiadomość, wysłana w godzinach rannych naszej strefy czasowej może dojść w środku nocy do drugiego oddziału. Oddział ten odpowie dopiero rano, ale wtedy u nas będzie noc i odpowiedź przeczytamy następnego dnia. Tracimy w ten sposób kilka lub nawet kilkanaście cennych godzin.

25.3. Dostawcy sprzętu i oprogramowania Jeśli projekt wymaga sprzętu bądź oprogramowania dostarczanego przez zewnętrzne firmy, to powinniśmy upewnić się, że dostawca w jak największym stopniu przetestował dostarczane nam produkty. Warto szczególną uwagę

zwrócić

na

testy

integracyjne.

Często

dostarczane

komponenty

nie



przetestowane pod kątem zgodności z wersjami sprzętu lub oprogramowania wykorzystywanego przez nasz produkt. Jeśli jest to możliwe, należy nałożyć na dostawcę obowiązek przeprowadzenia takich testów. W przeciwnym razie kierownik testów musi pamiętać o tym, aby przetestować dostarczane produkty w tych obszarach, w których nie było to wykonane. Wykorzystywanie komponentów dostarczanych przez zewnętrzne firmy ma tę zaletę, że komponenty te są zazwyczaj dobrze przetestowane. W większości przypadków są to produkty typu COTS (oprogramowanie „z półki”), więc ich niezawodność powinna być wysoka. Kupując takie oprogramowanie, oszczędzamy na jego testowaniu. Wybierając dostawcę, warto sprawdzić, jak wygląda jego dojrzałość procesu testowego. Gdy dostawca ma nam dostarczyć oprogramowanie indywidualne, szyte na miarę, można nawet, np. w dokumentacji przetargowej, zastrzec sobie prawo do przeprowadzenia audytu u dostawcy i wyeliminować z postępowania przetargowego te organizacje, których dojrzałość procesu wytwórczego zostanie oceniona negatywnie przez audytora.

25.4. Dostawcy usług testowych Z zewnętrznych usług testowych warto skorzystać, jeśli: nie mamy doświadczenia w wymaganym obszarze testowania; nie mamy wystarczających zasobów do przeprowadzenia testowania; zależy nam na świeżym, niezależnym spojrzeniu na produkt tak, aby znaleźć możliwie jak najwięcej defektów i uzyskać wysoką jakość. Przy wyborze dostawcy testowania, tak jak przy wyborze dostawcy komponentów, można przeprowadzić jego audyt, aby zagwarantować wysoką jakość usług. Należy przeprowadzić analizę ekonomiczną, uwzględniającą koszt usług testowych, ich efektywność oraz koszt niskiej jakości, który możemy ponieść, jeśli nie wykonamy tych usług.

25.5. TaaS (Testing as a Service)

Testowanie jako usługa staje się coraz popularniejszą metodą na obniżenie kosztów procesu testowego. Jest ona analogiczna do usługi SaaS czy cloud computing, w której nie kupujemy na własność oprogramowania, ale jedynie prawo do jego czasowego wykorzystania. Testowanie jako usługa jest polecanym rozwiązaniem, zwłaszcza w przypadku testów wydajnościowych. Wiele organizacji nie jest w stanie samodzielnie zbudować środowiska testowego umożliwiającego przeprowadzenie testów obciążeniowych. Wykupując usługę TaaS w chmurze, możemy symulować teoretycznie dowolnie duże obciążenie np. ruchu sieciowego, zgłoszeń do systemu itp. Usługi TaaS, szczególnie TaaS w chmurze, mają wiele zalet [205]: dostęp na żądanie (24/7/365); skalowalność i elastyczność; redukcja kosztów; niewielki wysiłek w zarządzaniu; niezależność od lokalizacji lub urządzenia. Trzy podstawowe modele usług w chmurze to: SaaS (Software as a Service) – w chmurze znajduje się samo wykorzystywane oprogramowanie; IaaS (Infrastructure as a Service) – w chmurze znajduje się infrastruktura (np. serwery obliczeniowe), a oprogramowanie – po stronie klienta; PaaS (Platform as a Service) – cała platforma znajduje się w chmurze. Jeśli nie mamy odpowiedniego sprzętu dla testów obciążeniowych, to najwygodniejszym modelem jest IaaS. Jeśli testowanie wymaga specjalistycznego, drogiego oprogramowania (np. wyrafinowana analiza statyczna lub bardzo efektywne projektowanie przypadków

Rysunek 25.2. Klasyfikacja usług TaaS testowych), to odpowiednim modelem będzie SaaS. Klasyfikacja usług TaaS jest przedstawiona na rysunku 25.2. Testowanie metodą TaaS nie różni się w zasadzie niczym od zwykłego procesu testowego poza sposobem testowania oraz jego umiejscowieniem, co wynika z przyjętych rozwiązań technologicznych. TaaS umożliwia zarządzanie zarówno samą usługą, środowiskiem testowym, jak i testowaniem. W zakresie testowania na żądanie wspiera większość czynności faz projektowania, implementacji oraz monitorowania testów. Oferuje infrastrukturę do przeprowadzania testów wydajności i skalowalności. Dostarcza rozwiązania umożliwiające symulowanie zgłoszeń od różnej liczby oraz typów użytkowników a także testy związane z geograficznym rozproszeniem zasobów.

26. Zarządzanie wdrażaniem standardów przemysłowych

W inżynierii oprogramowania istnieje wiele norm obejmujących praktycznie wszystkie jej aspekty: zaczynając od opisu cyklów życia oprogramowania, przez standardy dotyczące wytwarzania, testowania, przeglądów, zapewniania jakości, na zarządzaniu defektami kończąc. Kierownik testów powinien być świadomy istnienia tych norm oraz możliwości ich wykorzystania. Dodatek B zawiera spis standardów związanych z testowaniem i jakością oprogramowania. Standardy można wykorzystywać dwojako: w sposób ścisły, tzn. zapewniać zgodność naszego procesu ze standardem lub w sposób wybiórczy, tzn. traktować normę jako zbiór rad i pomysłów, które możemy, ale niekoniecznie musimy, wykorzystać. Na przykład norma IEEE 829 może służyć jako podpowiedź co do układu i zawartości różnych dokumentów procesu testowego, a norma ISO/IEC 29119-4 dostarcza katalog technik projektowania testów, z których możemy wybrać te najbardziej dla nas przydatne. Istnieją dwie organizacje publikujące międzynarodowe standardy: ISO1 (International Standards Organization) oraz IEEE (Institute of Electrical and Electronics Engineers). ISO jest reprezentowane przez członków pochodzących z różnych krajów, w których działają lokalne organizacje współpracujące z ISO. W Polsce taką organizacją jest Polski Komitet Normalizacyjny (PKN). Z punktu widzenia kierownika testów jest ważne, aby przy stosowaniu standardów pamiętać, że są one tworzone przez grupy profesjonalistów, na podstawie ich wiedzy i doświadczenia. Nie każdy standard musi więc odpowiadać potrzebom naszej organizacji, niemniej jednak wiele z nich jest bardzo przydatnych i warto je znać, gdyż opisują bardzo wiele tzw. dobrych praktyk. Należy mieć również na uwadze, że istnienie wielu różnych standardów

oznacza, że nie wszystkie muszą być ze sobą zgodne. Jeśli stosowanie normy czy standardu jest wymogiem, to kierownik projektu powinien zapewnić zgodność procesu z tą normą.

1 Skrót „ISO” nie jest akronimem – wywodzi się od greckiego słowa ίσος (isos), oznaczającego „równy”, „taki sam”.

27. Zarządzanie incydentami

Zarządzanie incydentami jest jednym z głównych obszarów procesu testowego. Zbierane informacje dotyczące zgłoszonych oraz naprawionych defektów pozwalają kierownikowi testów na wgląd w bieżący stan projektu pod względem liczby zgłoszonych i usuniętych defektów, czasu od zgłoszenia do naprawy, gęstości defektów w poszczególnych modułach. Te dane pozwalają nie tylko na oszacowanie aktualnego poziomu jakości, lecz także na lokalizację modułów o najwyższym poziomie ryzyka czy ocenę efektywności procesu usuwania defektów. Informacje zebrane za pomocą narzędzi do zarządzania incydentami pomagają także w budowie modeli predykcyjnych dla przyszłych projektów. incydent, incydent testowy, odchylenie (ang. incident, test incident, deviation) – każde zdarzenie wymagające zbadania [29] zarządzanie incydentami (ang. incident management) – proces składający się z rozpoznania, analizy, podejmowania działań i rozwiązywania incydentów; polega na rejestracji, klasyfikacji oraz określaniu wpływu incydentów [25] W bieżących pracach projektowych zarządzanie incydentami pomaga utrzymać cały proces debagowania w ryzach. Każdy zgłoszony defekt otrzymuje swój identyfikator i priorytet. Podczas zgłoszenia przypisuje się dany defekt do osoby, która ma go naprawić. Oczywiście odbywa się to zwykle przy użyciu specjalnego narzędzia do zarządzania incydentami. Dzięki temu każdy interesariusz projektu może na bieżąco śledzić stan incydentów w projekcie. Kierownik testów musi znać cykl życia defektów oraz wiedzieć, w jaki sposób monitorować i nadzorować za jego pomocą zarówno proces testowania, jak i wytwarzania oprogramowania. Obowiązkiem kierownika testów jest również

wybór narzędzia do zarządzania incydentami, choć najczęściej dokonuje tego w porozumieniu z pozostałymi członkami projektu. Często konkretne narzędzie jest wykorzystywane w firmie od dłuższego czasu i nie ma potrzeby wymiany go na inne. Czasami w organizacji powołuje się komitet zarządzania usterkami. W skład tego ciała wchodzą różni interesariusze projektu. Rolą komitetu jest zarządzanie procesem zgłaszania usterek. Może mieć on uprawnienia dotyczące przydzielania defektów do osób zajmujących się ich naprawą, odmowy zgłoszenia defektu, priorytetyzacji defektów do naprawy itp. Częściej jednak zadania te przejmuje tzw. Rada Kontroli Zmian. komitet zarządzania usterkami, konsylium nad defektami (ang. defect management committee, defect triage committee) – zespół interesariuszy z różnych specjalności, który zarządza zgłaszanymi usterkami od zgłoszenia do końcowego rozwiązania (usunięcie usterki, odroczenia lub anulowania zgłoszenia) Zarządzanie defektami jest podzbiorem zarządzania incydentami, gdyż każdy defekt jest incydentem, ale nie każdy incydent musi być defektem. zarządzanie defektami, zarządzanie problemami (ang. defect management, problem management) – proces składający się z rozpoznania, analizy, prowadzenia działań i likwidacji usterek; polega na rejestracji usterek, ich klasyfikacji oraz określaniu wpływu defektów [25]

27.1. Cykl życia defektu Defekt jest wprowadzany do oprogramowania w wyniku pomyłki człowieka podczas tworzenia jakiegoś artefaktu procesu wytwórczego. Defekty mogą więc być wprowadzane w ciągu całego cyklu życia oprogramowania. Dlatego w każdej jego fazie należy wykorzystywać narzędzia pomocne w wykrywaniu defektów. Dla faz projektowych mogą to być różnego rodzaju formy testowania statycznego; dla faz wytwórczych – techniki testowania dynamicznego. Dokładne śledzenie faz wprowadzania i usuwania usterek pozwala na budow tzw. modeli efektywności usuwania defektów. Modele takie opiszemy w rozdziale 45. Pozwalają one ocenić wpływ poszczególnych faz cyklu życia na zwiększanie jakości produktu

i identyfikować te elementy procesu, które wymagają w tym względzie naprawy, ze względu na niską skuteczność wykrywania usterek. Wczesne wykrywanie pozwala obniżyć koszty usuwania defektów. Techniki statyczne są w tym względzie efektywniejsze od dynamicznych, gdyż znajdują bezpośrednio defekty, a nie awarie – w tym drugim przypadku należy na podstawie awarii zlokalizować miejsce defektu, który ją powoduje, co wymaga dodatkowego czasu, a więc i kosztów. Większość organizacji zarządza incydentami przez ich cykl życia. Cykl ten może wyglądać różnie, w zależności od wykorzystywanego przez firmę narzędzia. Incydent przechodzi przez sekwencję stanów, od momentu jego zgłoszenia do momentu jego rozwiązania. W każdym stanie jest wymagana określona akcja, co do każdego incydentu. Odpowiada za to osoba przypisana do tego incydentu na danym etapie. Na rysunku 27.1 przedstawiono przykładowy cykl życia incydentu, pochodzący z [60], ukazujący najbardziej typowe i najczęściej spotykane stany defektu.

Rysunek 27.1. Cykl życia incydentu Cykl życia incydentu rozpoczyna się od zgłoszenia go w systemie przez rejestrację. Może on zostać odrzucony przez innego testera lub kierownika testów z powodu błędnie napisanego raportu (np. uniemożliwiającego reprodukcję awarii). Po przepisaniu raportu incydent wraca do stanu zgłoszony. Po przejrzeniu raportu o incydencie następuje jego przejście w stan otwarty. Jeśli okaże się, że np. jest to duplikat zgłoszonego wcześniej incydentu lub że nie jest to

problem1, incydent zostaje odrzucony. Otwarty incydent może zostać zatwierdzony do naprawy i przejść do stanu przypisany, gdzie przypisuje się osobę odpowiedzialną za naprawę defektu. Może jednak nastąpić odmowa naprawy (np. z powodu braku wystarczających informacji o defekcie, chwilowym braku zasobów lub aktualnie prowadzonymi pracami nad defektami o wyższym priorytecie), co spowoduje przejście incydentu w stan odłożony, z którego – po wystąpieniu odpowiednich okoliczności – może przejść w stan ponownie otwarty, a po zatwierdzeniu do naprawy – w stan przypisany. Jeśli przypisany do określonej osoby defekt zostanie przez nią naprawiony, to przechodzi do stanu naprawiony. Wykonuje się wtedy testowanie potwierdzające. Jeśli przynajmniej jeden taki test jest niezdany, to defekt przechodzi w stan ponownie otwarty. W przeciwnym razie defekt zostaje zamknięty. Jeśli problem pojawi się ponownie, to ze stanu zamknięty defekt przechodzi do stanu ponownie otwarty. rejestracja incydentu (ang. incident logging) – dokumentowanie szczegółów zdarzenia, które wystąpiło, np. podczas testów

27.2. Atrybuty defektu i ODC 27.2.1. Atrybuty defektów Każdy defekt można scharakteryzować pewnymi atrybutami, które powinny być opisywane w raporcie o incydencie. Im więcej takich informacji w raporcie i im są one dokładniejsze, tym łatwiej zreprodukować, zlokalizować i usunąć defekt. Typowe atrybuty defektu to: miejsce zaobserwowania incydentu (np. moduł, funkcja, ekran); faza wykrycia kodowania);

incydentu

(np.

podczas

testów

jednostkowych,

wpływ (jeśli go nie naprawimy, jakie będą konsekwencje dla klienta?); dotkliwość (problem krytyczny, poważny, niewielki, zaniedbywalny).

27.2.2. ODC (Orthogonal Defect Classification) Atrybuty mogą służyć do różnego rodzaju analiz dotyczących miejsc powstawania defektów. Przydają się także podczas priorytetyzacji defektów do

naprawy. W szczególności atrybuty defektów są wykorzystywane w metodzie ODC (ang. Orthogonal Defect Classification), opublikowanej w [206]. Ostatnia wersja metody, ODC 5.2., jest opisana w [207]. Jest ona próbą połączenia zalet dwóch typowych podejść stosowanych w analizie defektów oprogramowania: analizy przyczyny źródłowej oraz modeli statystycznych, takich jak model wzrostu niezawodności. Analiza przyczyny źródłowej (ang. root cause analysis) szczegółowo przygląda się każdemu defektowi, starając się dociec jego źródłowej przyczyny. Wymaga ona dużego nakładu pracy oraz – po jej przeprowadzeniu – podjęcia wielu akcji naprawczych. Modele wzrostu niezawodności to z kolei proste metody umożliwiające śledzenie trendów dotyczących liczby defektów bądź awarii, jednak nie są one w stanie określić, jakie akcje naprawcze należy podjąć. ODC pozwala na szybkie uchwycenie semantyki każdego wykrytego defektu. Wykorzystanie atrybutów defektów umożliwia późniejszą statystyczną analizę oraz modelowanie. Analiza danych z ODC dostarcza cennych metod diagnostycznych do oceny poszczególnych faz cyklu życia oprogramowania, a także poziomu dojrzałości produktu. ODC wykorzystuje 8 atrybutów defektu. Są one zbierane podczas wykrycia defektu (3) oraz po jego naprawieniu (5): atrybuty zbierane podczas wykrycia defektu: aktywność (przegląd projektu, kodowanie, inspekcja, jednostkowe, testy funkcjonalne, testy systemowe);

testy

wyzwalacze (zgodność z projektem, logika/przepływ, kompatybilność wstecz, współbieżność, wewnętrzny dokument, zależność od języka, efekt uboczny, rzadka sytuacja, prosta ścieżka, złożona ścieżka, pokrycie testowe, odmiana testu, kolejność testów, interakcja testów, obciążenie, odtwarzanie/wyjątek, start/restart, konfiguracja sprzętu, konfiguracja oprogramowania, zablokowany test); wpływ (instalowalność, standardy, integralność/bezpieczeństwo, migracja, niezawodność, wydajność, dokumentacja, wymagania, utrzymywalność, użyteczność, dostęp, funkcjonalność); atrybuty zbierane/dostępne po naprawie defektu: cel (projekt, kod); typ defektu (przypisanie/inicjalizacja, algorytm/metoda, funkcja/klasa/obiekt, interfejs/komunikacja OO, relacja);

sprawdzanie, czas/serializacja,

kwalifikacja (brak, nieprawidłowość, nadmiarowość); wiek (defekt w niemodyfikowanej przepisany, powtórnie naprawiony);

części

programu,

nowy,

źródło (defekt znaleziony w oprogramowaniu tworzonym in-house, defekt spotkany podczas używania biblioteki, defekt znaleziony w produkcie od dostawcy, defekt ma związek z użyciem części produktu walidowanej dla innego środowiska). zablokowany przypadek testowy (ang. blocked test case) – przypadek testowy, który nie może zostać wykonany, ponieważ jego warunki wstępne nie mogą zostać osiągnięte „Ortogonalność” w nazwie metody oznacza, że powyższe atrybuty są od siebie niezależne, opisują nieredundantne informacje o defekcie. Identyfikacja wszystkich ośmiu cech defektu pozwala zgromadzić dane o semantyce danego defektu z wielu różnych perspektyw. ODC pozwala na klasyfikację defektów oraz ich matematyczną analizę. Dzięki starannie wybranym atrybutom, klasyfikacja jest generyczna, tzn. niezależna od produktu, procesu oraz tego, kiedy i gdzie defekty były znajdowane. ODC jest metodą opartą na aktywności – atrybut ten odwołuje się do tego, co robiła dana osoba, gdy wykryła określony defekt, niezależnie od tego, w którym miejscu harmonogramu produkt czy komponent się znajdował. Wyzwalacze są kompletną i wystarczającą listą czynności wykonywanych podczas procesu walidacji i weryfikacji oprogramowania. Mogą być wykorzystane jako lista kontrolna. ODC lub jej modyfikacje okazały się być pomocne w różnych specyficznych typach oprogramowania. Na przykład w [208] odmiana ODC jest wykorzystana do klasyfikacji defektów dla stron internetowych na podstawie logów z serwera. Z kolei praca [209] opisuje adaptację ODC do ciągłego odkrywania wymagań w systemach wysokiej integracji.

27.2.3. Przykładowe analizy przy użyciu ODC Mając sklasyfikowane defekty (tzn. opisane wymienionymi atrybutami), możemy wykonać wiele analiz statystycznych:

wcześniej

Analiza pojedynczego atrybutu: dla każdego atrybutu defektu możemy określić rozkład wartości tego atrybutu. Zwykle rozkład taki jest niejednostajny i identyfikuje obszary wymagające głębszych, bardziej szczegółowych badań. Dodanie do analizy wymiaru czasowego pozwala wykonać analizę trendu defektów. Analiza dwuczynnikowa: pozwala na badanie interakcji między dwoma atrybutami [210]. Analiza wyższego rzędu, np. modelowanie przy użyciu drzew, biorąca pod uwagę większą liczbę atrybutów [211]. Przykład analizy pojedynczego atrybutu jest pokazany na rysunku 27.2.

Rysunek 27.2. ODC: rozkład defektów dla atrybutu „wpływ” Jak widać, najwięcej problemów w projekcie dotyczy wydajności. Kolejna grupa obszarów wymagających zbadania i usprawnienia to: dokumentacja, wymagania, użyteczność oraz funkcjonalność. Dla 9 defektów nie mogliśmy określić wartości atrybutu, przy czym część z nich może jeszcze zostać sklasyfikowana, jeśli dostaniemy większą ilość danych na temat tych defektów. W tabeli 27.1 pokazano przykład dwuwymiarowej analizy, badającej związek między fazą znalezienia defektu a jego dotkliwością.

Tabela 27.1. ODC: przykład dwuwymiarowej analizy

Dotkliwość (severity) Faza

1 (krytyczna)

2 (duża)

3 (mała)

4 (pomijalna)

Wymag ania

6

2

0

3

Projektowanie

2

11

8

9

Kodowanie

0

36

6

11

Tes towanie

11

42

51

40

Utrzymanie

0

3

7

2

Najwięcej defektów o znaczeniu krytycznym znaleziono w fazie testowania. Być może część z nich można by wykryć wcześniej. Takich informacji może dostarczyć model usuwania defektów, w którym oznacza się nie tylko fazę usunięcia, lecz także wykrycia defektu (patrz rozdz. 45). Dla danych takich jak te z tabeli 27.1 można również wykorzystywać narzędzia statystyczne (np. test chikwadrat lub inne testy nieparametryczne), aby formalnie zweryfikować, czy procentowe rozkłady dotkliwości są takie same we wszystkich fazach.

27.3. Metryki zarządzania incydentami Najczęściej stosowane metryki dla incydentów to: czas zgłoszenia; liczba zgłoszonych defektów w danym czasie (np. w tygodniu, miesiącu); liczba zgłoszonych defektów według fazy wykrycia; liczba zgłoszonych defektów według fazy usunięcia; czas usunięcia defektu (umożliwia badanie rozkładu czasu naprawy od momentu zgłoszenia); dotkliwość; priorytet.

Czas zgłoszenia umożliwia badanie rozkładu czasów między kolejnymi zgłoszeniami oraz prezentowanie trendu liczby zgłoszonych defektów w czasie. Może służyć do

oceny

wzrostu niezawodności

oprogramowania. Liczba

zgłoszonych defektów pozwala oszacować wydajność procesu, a podział tej liczby według fazy wykrycia i usunięcia pokazuje efektywność poszczególnych faz projektowych w wykrywaniu defektów oraz ich podatność na błędy. Czas usunięcia defektu pozwala z kolei ocenić jakość i wydajność wsparcia technicznego. Na rysunku 27.3 jest podany przykład logu z systemu do śledzenia incydentów. Każdy wpis zawiera czas zgłoszenia, fazę zgłoszenia oraz czas usunięcia. Na rysunku 27.4 pokazano przykładowe zbiorcze analizy dokonane na podstawie odpowiedniej agregacji surowych danych z loga. Zakładamy, że raporty przedstawiają stan na 31 lipca 2014 roku. Raporty te mogą opisywać rozkład defektów według fazy zgłoszenia, rozkład czasu naprawy oraz liczbę zgłoszeń defektów w czasie. W naszym przykładzie wszystkie defekty oprócz jednego były naprawiane w ciągu maksymalnie 4 dni. Jeśli ustalimy te 4 dni jako dopuszczalną granicę, to musimy przeanalizować jeden defekt, którego naprawa zajęła aż 7 dni, aby odkryć powody tak długiego czasu naprawy. Mógł to być bardzo skomplikowany defekt, ale przyczyną równie dobrze mógł być urlop pracownika odpowiedzialnego za usunięcie defektu. Liczba zgłoszeń w czasie wygląda na stabilną (0–5 defektów dziennie), co oznacza, że produkt prawdopodobnie nie jest jeszcze gotowy do wydania, choć jednodniowe odstępy czasu mogą być zbyt dokładne na potrzeby tego typu analizy. Można zagregować dane po tygodniach, aby uzyskać bardziej „zgrubny” trend zgłoszeń usterek.

2014-07-14 wymag ania 2014-07-14 2014-07-14 wymag ania 2014-07-16 2014-07-15 wymag ania (oczekuje) 2014-07-16 wymag ania 2014-07-19 2014-07-16 projekt 2014-07-16 2014-07-16 projekt 2014-07-16 2014-07-16 wymag ania 2014-07-17 2014-07-17 wymag ania 2014-07-24 2014-07-17 projekt 2014-07-21

2014-07-21 kodowanie 2014-07-21 2014-07-24 kodowanie 2014-07-26 2014-07-24 projekt 2014-07-25 2014-07-24 kodowanie 2014-07-28 2014-07-25 tes towanie (oczekuje) 2014-07-25 kodowanie (oczekuje) 2014-07-28 kodowanie 2014-07-28 2014-07-28 tes towanie 2014-07-30 2014-07-28 tes towanie 2014-07-30 2014-07-29 wymag ania (oczekuje) 2014-07-30 tes towanie (oczekuje) 2014-07-30 tes towanie (oczekuje) Rysunek 27.3. Raport z narzędzia do zarządzania incydentami

Rysunek 27.4. Raporty na podstawie danych z dziennika incydentów Ostatni raport dotyczy czasu oczekiwania otwartych defektów. Mamy dwa defekty otwarte od jednego dnia, jeden oczekujący od dwóch dni, dwa oczekujące od sześciu dni oraz jeden oczekujący na naprawę przez więcej niż dziewięć dni. Trzy ostatnie defekty zdecydowanie wymagają sprawdzenia, dlaczego czas oczekiwania na ich naprawę jest aż tak długi.

27.4. Zawartość raportu o incydencie Raport o incydencie jest tworzony w momencie wykrycia defektu w analizie statycznej lub zaobserwowania awarii podczas testów dynamicznych. Od stopnia poprawności oraz szczegółowości tego raportu zależeć może efektywność usunięcia usterki. Zawartość raportu może różnić się nie tylko w poszczególnych organizacjach, lecz także w poszczególnych fazach pojedynczego projektu. Różne standardy oraz techniki, takie jak ISO 9126, ISO 25000, IEEE 829, IEEE 1044 czy ODC mogą być pomocne w określeniu rodzaju zbieranej informacji. raport o incydencie, zgłoszenie incydentu, zgłoszenie incydentu testowego, zgłoszenie odchylenia (ang. incident report, software test incident report, test incident report, deviation report) – dokument opisujący każde wymagające dalszego zbadania zdarzenie [5] Raport o incydencie może w szczególności zawierać następujące informacje [196]: imię i nazwisko osoby, która odkryła defekt/awarię; rola tej osoby (użytkownik końcowy, analityk biznesowy, programista, tester, wsparcie techniczne); typ wykonywanego testu (np. testowanie użyteczności, wydajności, testy regresji); ogólne podsumowanie zaobserwowane);

incydentu

(co

się

stało,

co

zostało

szczegółowy opis problemu; kroki do reprodukcji defektu/awarii, zawierające informację o oczekiwanych i rzeczywistych wynikach, zawierające zrzuty ekranu,

baz danych oraz logów, jeśli jest to potrzebne lub konieczne; faza wprowadzenia, wykrycia oraz usunięcia defektu, z poziomem testów (jeśli możliwe);

wraz

element systemu, w którym zaobserwowano incydent, wraz z podaniem jego wersji; dotkliwość wpływu defektu/awarii na system i/lub interesariuszy projektu (zwykle określana przez techniczny aspekt działania systemu); priorytet naprawy (zwykle określany przez biznesowy aspekt działania systemu); podsystem lub komponent zawierający defekt (na potrzeby analizy grupowania defektów); metoda, która wykryła problem (np.: przegląd, analiza statyczna, testowanie czarnoskrzynkowe, używanie programu w produkcji, używanie w środowisku użytkownika); typ defektu (wg przyjętej taksonomii, np. IEEE 1044 lub ODC); charakterystyka jakościowa, na którą wpływa defekt; opis środowiska testowego i jego konfiguracji, w którym wykryto defekt; osoba przypisana do tego defektu (o ile defekt nie jest w końcowym stanie cyklu życia); prawdopodobna przyczyna defektu/awarii; ryzyka, koszty, możliwości niepodejmowaniem naprawy. Niezależnie

od

postaci

raportu

i

o

zyski

związane z podjęciem

incydencie,

informacje

do

lub

niego

wprowadzane powinny być kompletne, spójne, dokładne, obiektywne, odpowiednie i wytworzone na czas. W szczególności w raporcie nie wolno odnosić się personalnie do innych członków zespołu („prawdopodobnie defekt znowu wprowadził Kowalski, bo on pracuje nad tym modułem”), gdyż po pierwsze raport nie służy oskarżaniu, ale informowaniu w celu szybkiego rozwiązania problemu, a po drugie, takie zachowanie bardzo szybko powoduje trudne do wygaszenia konflikty w zespole.

27.5. Komunikowanie incydentów

Testerzy podczas komunikowania incydentów muszą zachować pełen profesjonalizm. Komunikacja jest niezbędna, bo zawsze powstają jakieś problemy i zawsze należy o ich istnieniu powiadamiać osoby odpowiedzialne za ich rozwiązywanie. Komunikacja może odbywać się w formie elektronicznej, np. przy użyciu narzędzia do zarządzania incydentami, ale może być również po prostu zwykłą rozmową. Informując o problemie, tester musi zachować obiektywizm. W rozmowie nie wolno oskarżać konkretnych osób za wprowadzenie danych defektów. Tester nie może dać do zrozumienia programiście, że to jego wina. W przypadku, gdy programiści lub inni adresaci uwag testerów czują się atakowani, tester powinien im uświadomić, że jego intencją nie jest oskarżanie kogokolwiek. W końcu pierwszym i jedynym celem informowania o incydentach jest uświadomienie ich istnienia oraz potrzeba jak najszybszej naprawy. Ludzie zawsze popełniali i będą popełniać błędy. Jest to absolutnie naturalna sytuacja i dlatego właśnie w organizacji istnieją testerzy, aby wyłapywać pomyłki deweloperów czy projektantów.

1 Według znanego powiedzenia informatyków: it’s not a bug, it’s a feature (to nie błąd, to funkcjonalność).

Część V Ludzie i narzędzia

We become what we behold. We shape our tools and then our tools shape us Herbert Marshall McLuhan

Największym kapitałem firmy są pracujący w niej ludzie, a efektywność ich pracy zależy od wykorzystywanych narzędzi. W tej części książki opiszemy kwestie związane z tymi dwoma aspektami w kontekście procesu testowego. Rozdział 28 jest przeznaczony dla kierowników i menedżerów zespołu testowego. Opiszemy sposób budowania zespołu, stymulowanie rozwoju poszczególnych jego członków, a także zagadnienia dotyczące różnic między zarządzaniem a przywództwem. Istnieje wiele sytuacji, w których pracownicy – aby osiągnąć wyznaczony cel – muszą ze sobą współpracować. W rozdziale 29 przedstawimy techniki pracy grupowej, przydatne kierownikowi testów podczas organizowania różnego rodzaju spotkań i sesji. Techniki te dotyczą: wspomagania kreatywności, porządkowania i priorytetyzacji, szacowania i oceny oraz definiowania problemów, przyczyn źródłowych lub możliwości. Rozdział 30 jest poświęcony automatyzacji testowania i narzędziom wspierającym pracę testerów. Omówimy w nim proces wyboru narzędzia, klasyfikację narzędzi testowych, opis architektury automatyzacji testów oraz kwestie związane z wdrażaniem narzędzi w organizacji. Szczegółowo opiszemy najpopularniejsze techniki automatyzacji testów, metody automatycznego generowania danych testowych oraz najczęściej wykorzystywane języki i notacje do tworzenia skryptów testowych i definiowania testów. W podrozdziale 30.6 znajduje się katalog przykładowych, najczęściej stosowanych narzędzi wspomagających proces testowy, sklasyfikowanych według ich typów.

28. Ludzie i ich kompetencje – tworzenie zespołu

Kierownik testów rekrutuje, zatrudnia i zarządza zespołem ludzi mających różne umiejętności. Wymagania co do tych ostatnich mogą się zmieniać, nawet w czasie trwania jednego projektu. Dlatego, poza zatrudnianiem właściwych osób, kierownik testów musi dbać o to, by członkowie zespołu byli odpowiednio przeszkoleni, zdobywali nowe umiejętności, potrafili efektywnie współpracować oraz działali jak najefektywniej. Jest to niezwykle trudne zadanie – zarządzanie zasobami ludzkimi jest jednym z najtrudniejszych zadań, jakie stoją przed menedżerem. Aby osiągnąć na tym polu sukces, kierownik nie tylko musi znać różne techniki i metody zarządzania i pracy z ludźmi, lecz także mieć wiele umiejętności miękkich. Dzięki nim jest w stanie np. łagodzić konflikty, sprawnie komunikować się, czy też wykorzystywać ambicje i dążenia poszczególnych członków zespołu do zwiększenia efektywności pracy całej drużyny. Umiejętności miękkie to cechy związane z postrzeganiem siebie i innych, radzenie sobie z emocjami oraz umiejętne reagowanie na niesprzyjające, trudne okoliczności czy sytuacje. Oto przykłady umiejętności miękkich [212]: dążenie do samodoskonalenia – wyciąganie wniosków z każdej, szczególnie trudnej sytuacji, uczenie się na błędach, ciągłe doskonalenie swoich cech; samoświadomość – wiedza o tym, co mną kieruje, motywuje, frustruje, inspiruje; umiejętność obserwowania samego siebie w trudnych sytuacjach i rozumienie, jak postrzeganie samego siebie oraz innych kieruje moim postępowaniem; panowanie nad emocjami – zwłaszcza tymi negatywnymi; pewność siebie – konieczna do podejmowania zdecydowanych decyzji; cecha konieczna u liderów;

szybkich,

odporność na stres i umiejętność pracy pod presją czasu; regeneracja – umiejętność wycofania się po porażce rozczarowaniu, zebranie sił, podniesienie się i pójście dalej;

lub

wybaczanie i zapominanie – umiejętność nie chowania uraz do innych ludzi i do siebie; cierpliwość; postrzeganie innych – wychwytywanie niuansów w zachowaniu, mowie ciała, tonie głosu innych ludzi, aby odkryć ich prawdziwe emocje oraz dążenia; komunikacja – umiejętność efektywnego prowadzenia rozmów tak, aby komunikaty nie były zniekształcane, w rezultacie czego druga osoba może nas opacznie zrozumieć; umiejętność pracy w grupie; umiejętności interpersonalne – pomocne w zdobywaniu zaufania, znajdowaniu wspólnego języka, empatia, budowanie zdrowych i trwałych relacji z innymi; umiejętność prezentacji i przekazywania wiedzy, zdolność dostosowania poziomu abstrakcji przekazywanej informacji

do do

konkretnej osoby lub grupy osób; użyteczność (ang. facilitating skills) – zdolność do koorynacji i proszenia o opinie lub informacje zwrotne od różnorodnych ludzi tworzących grupę w celu wyłonienia najlepszego lub kompromisowego rozwiązania; umiejętność „sprzedawania” siebie, swojego zespołu oraz jego osiągnięć – aby zyskać autorytet w organizacji i zwiększyć świadomość innych osób co do działalności zespołu testerskiego; przywództwo – umiejętność radzenia sobie ze zmianami w organizacji i motywowanie ludzi do podążania za tą zmianą, umiejętność kreowania wizji oraz pomysłów inspirujących innych ludzi; mentoring/coaching – wykorzystanie swojej wiedzy, doświadczenia i umiejętności do „prowadzenia” innych ludzi, radzenia im, pomagania w ich rozwoju osobistym i zawodowym. Idealny kierownik testów powinien cechować się wszystkimi wymienionymi cechami. Niestety nie ma takich „idealnych” osób – każdy ma je w mniejszym lub większym stopniu. Ważne, aby być świadomym swoich niedostatków i kształtować w sobie te z nich, których nie mamy lub mamy w zbyt małym

stopniu. Oczywiście jest pożądane, aby każdy członek zespołu również miał przynajmniej część tych umiejętności.

28.1. Budowanie zespołu testowego Umiejętność tworzenia dobrego, zgranego zespołu jest główną cechą kierownika testów. Od jego decyzji o zatrudnieniu poszczególnych osób zależeć będzie efektywność i profesjonalizm zespołu. Zatrudnienie jednej niewłaściwej osoby może zupełnie rozbić zespół i obniżyć jego morale. Zanim przystąpi do budowania zespołu, kierownik testów musi mieć plan zatrudnienia poszczególnych osób. Plan ten musi uwzględniać budżet zespołu oraz harmonogram prac projektowych. Jeśli na przykład wiadomo, że testy automatyczne będą wykonywane dopiero pod koniec projektu, za dwa lata, to o zatrudnieniu osoby do roli inżyniera automatyzacji testów można zacząć myśleć za rok. Budowa zespołu nie musi następować od zera – może polegać jedynie na uzupełnieniu składu zespołu. Niezależnie od sposobu tworzenia zespołu, kierownik powinien: zdefiniować pozycje (role) potrzebne w zespole; zdefiniować zakres obowiązków dla każdego stanowiska; rozumieć, jakie są niezbędne wymagania i oczekiwania wobec kandydatów; być dobrym rekruterem, tzn. powinien umieć przeprowadzić rozmowę rekrutacyjną tak, aby właściwie ocenić przydatność kandydata oraz to, jak jego potencjalne zatrudnienie wpłynie na cały zespół.

28.1.1. Opis stanowiska Dla każdego stanowiska w zespole kierownik testów musi stworzyć jego opis. Opis ten powinien zawierać szczegółowy zakres obowiązków osoby zatrudnionej na tym stanowisku, umiejscowienie stanowiska w strukturze organizacyjnej, wymagany poziom doświadczenia i umiejętności. Można również podać zakres wysokości pensji1. Ogłoszenie o pracę może również zawierać spis pożądanych umiejętności, które nie są konieczne, ale mile widziane.

Tworząc ogłoszenia o pracę, menedżerowie często ulegają pokusie wpisania tam wymagań o wszystkich możliwych umiejętnościach, jakie przyjdą im do głowy2. Należy mieć na uwadze, że na takie ogłoszenie może nikt nie odpowiedzieć i stracimy tylko czas. Lepiej w opisie stanowiska ująć – jako wymagany – minimalny zestaw absolutnie koniecznych cech i umiejętności, które kandydat powinien mieć. Całą resztę można umieścić w sekcji „nice to have”, opisującej nadobowiązkowe, mile widziane umiejętności. Jeśli rekrutacja odbywa się przez wiele kanałów (np. ogłoszenia na stronie firmy, na portalach z ogłoszeniami o pracę, przez wynajęte firmy rekrutacyjne), to należy pamiętać o spójności ogłoszeń. Często zdarza się, że to samo ogłoszenie na stronie firmy ma inną treść, niż oferta zamieszczona na stronie agencji rekrutacyjnej. Ogłoszenia nie powinny zawierać błędów stylistycznych ani ortograficznych, ponieważ błędy takie bardzo źle świadczą o firmie. Opisy stanowisk powinny być wewnętrznie niesprzeczne. Autor tej książki widział kiedyś ogłoszenie dużej firmy IT, w którego treści na początku umieszczony był zapis, iż 100% czasu pracownik pracuje na miejscu, w siedzibie firmy, po czym na końcu ogłoszenia znajdowała się informacja, że około 50% czasu pracy będzie się odbywać w innych oddziałach firmy. Przy tworzeniu ogłoszeń o pracę warto korzystać z pomocy działu HR tak, aby nie popełnić żadnego z wymienionych błędów. Na rysunku 28.1 pokazano przykładowe, rzeczywiste ogłoszenie o pracę na stanowisku testera (nazwa firmy oraz stanowiska została usunięta). Jego opis jest typowy dla opisów stanowisk testerskich spotykanych w Polsce i pokazuje pewien problem związany z tymi ogłoszeniami. Większość wymagań dotyczy zwykle umiejętności programowania bądź znajomości wiedzy dziedzinowej (w tym przypadku, elektroniki i miernictwa), przy czym żadne z tych wymagań nie odnosi się do kompetencji testerskich! Jak na ironię, w powyższym ogłoszeniu jedyne takie wymaganie (znajomość zagadnień z zakresu testowania systemów i oprogramowania) umieszczone jest w sekcji „Dodatkowe atuty”, a więc opisującej umiejętności nieobowiązkowe. W rezultacie, na ogłoszenia takie odpowiadać mogą programiści, którzy niekoniecznie znają się na testowaniu lub elektronicy z podstawową wiedzą z zakresu programowania. Czy takie osoby będą wartościowymi testerami? Być może tak, choć zapewne wymagać to będzie włożenia sporej ilości czasu i wysiłku w przyuczenie takiej osoby do testerskiego fachu.

Rysunek 28.1. Przykładowe ogłoszenie o pracę dla testera Wydaje się, że pracodawcy, chcąc zatrunić testerów, wciąż bardziej poszukują programistów, których można dokształcić w zakresie zapewniania jakości niż profesjonalnych testerów oprogramowania. Prawdopodobnie wiąże się to z popularną w ostatnim czasie kwestią automatyzacji testów (= pisanie skryptów) oraz wciąż powszechnym wyobrażeniem testera jako człowieka, który „siedzi i klika”. W powyższym ogłoszeniu nie ma w ogóle wymagań dotyczących specjalistycznej wiedzy z zakresu testowania (np. znajomości konkretnych technik projektowania testów, technik analizy ryzyka, umiejętności analizy wymagań) ani tych dotyczących umiejętności miękkich (komunikacja, dociekliwość, umiejętność logicznego myślenia, praca w grupie itd.).

28.1.2. Analiza CV

Po opublikowaniu ogłoszenia do firmy napłyną zgłoszenia kandydatów, zawierające najczęściej życiorys, czasami również list motywacyjny. Pierwszym krokiem w procesie zatrudniania jest analiza CV. Dział HR może tu pełnić rolę filtra, odrzucając oferty niespełniające minimalnych wymagań lub kandydatów, którzy źle wypadli podczas rozmowy z pracownikiem działu personalnego, np. podczas sprawdzania kompetencji językowych. Wiele organizacji ma dokładne procedury postępowania w procesie rekrutacyjnym i kierownik testów powinien skonsultować się z działem HR w tej kwestii, upewniając się np. jaka dokumentacja jest wymagana zarówno podczas przeglądania CV kandydatów, jak i przeprowadzania rozmów kwalifikacyjnych. Kierownik testów powinien przeglądać CV, mając na uwadze następujące aspekty: czy kandydat ma wystarczające doświadczenie lub umiejętności (tzn. sprawdzamy tak naprawdę, czy kandydat napisał, że je ma – rzeczywista weryfikacja CV następuje podczas rozmowy kwalifikacyjnej lub przez zadanie kandydatowi jakiegoś zadania do wykonania, w którym będzie musiał użyć tych umiejętności); czy oczekiwania finansowe kandydata są zgodne z zaplanowanymi „widełkami” oraz czy odpowiadają poziomowi jego doświadczenia; czy CV jest napisane czytelnie i spójnie (np. brak „luk” w historii zatrudnienia); czy CV jest napisane bezbłędnie (błędy mogą świadczyć o niechlujstwie czy niestaranności kandydata, co jest cechą wysoce niepożądaną u testera). Sporym problemem w analizie CV jest to, że ludzie niestety bardzo często zatajają pewne fakty, „oszczędnie gospodarują prawdą” lub zwyczajnie kłamią. Kierownik testów może do pewnego stopnia weryfikować prawdziwość CV przez kontakt z osobami wymienionymi w CV jako źródło referencji (np. poprzedni pracodawca, klient, dla którego kandydat pracował). W rozmowie z taką osobą nie należy zadawać pytań, na które odpowiedzią może być tylko „tak” lub „nie”, ale pytania otwarte. Na przykład, jeśli kandydat napisał w CV, że w pracy stosował narzędzie X, osoby podanej jako źródło referencji można zapytać o to, jakich narzędzi używał kandydat.

28.1.3. Rozmowa kwalifikacyjna

Rozmowa kwalifikacyjna jest okazją do osobistego poznania kandydata, weryfikacji jego wiedzy, oceny umiejętności miękkich, poznania jego motywacji do podjęcia pracy w naszej firmie oraz uściślenia pewnych informacji, które są niedoprecyzowane w CV. Podczas rozmowy powinniśmy sprawdzić: czy kandydat ma odpowiedni stosunek do pracy (w szczególności, czy np. przybył punktualnie na umówione spotkanie)? czy kandydat jest komunikatywny? czy umie zrozumiale wyrażać swoje myśli? czy kandydat potrafi krytycznie myśleć, argumentować i uzasadniać stawiane tezy? czy kandydat wykazuje się ciekawością poznawczą? czy kandydat ma wiedzę techniczną wymaganą na danym stanowisku? czy kandydat ma zdolności organizacyjne? jak kandydat zachowuje się w sytuacji pracy pod presją czasu? czy kandydat jest dojrzały i ma umiejętności interpersonalne? czy kandydat emocjonalną?

cechuje

się

odpowiednio

wysoką

inteligencją

co o kandydacie mówi jego mowa ciała, ton głosu oraz wszystkie inne pozawerbalne składowe przekazu? Kierownik testów przed rozmową powinien ustalić plan jej przebiegu: o co pytać i w jakiej kolejności. Nie trzeba ściśle trzymać się tego planu. Można – tak jak w testowaniu eksploracyjnym – „eksplorować” cechy kandydata, rozwijając i uszczegóławiając wątki rozmowy, które kierownik uzna za ciekawe. Rozmowa powinna zacząć się od neutralnych pytań (np. czy kandydat łatwo dotarł do siedziby firmy), aby rozluźnić atmosferę. Rozmowa kwalifikacyjna jest stresującym przeżyciem dla wielu osób i często stres ten powoduje, że wypadają oni na niej o wiele gorzej niż w rzeczywistości, w „normalnych” warunkach. Zwykle rozmowa kwalifikacyjna jest najefektywniejsza, gdy kandydat ma okazję do obszerniejszych wypowiedzi. Kierownik testów powinien zadawać tzw. pytania otwarte. Na przykład, zamiast pytać „czy stosowałeś kiedyś podejście X” albo „na czym polega technika Y” lepiej poprosić kandydata, aby opisał, jakie metody i techniki testowania wykorzystywał w swojej poprzedniej pracy. Można również podać mu przykładową aplikację i poprosić o zaproponowanie najlepszej według niego strategii przetestowania tego programu. Takie pytania pozwalają

szerzej odnieść się do problemu, a kierownikowi lepiej zweryfikować umiejętności kandydata. Umiejętności techniczne kandydatów można sprawdzić, prosząc kandydata o [196]: zaprojektowanie testów na podstawie dokumentu wymagań; dokonanie przeglądu

technicznego

dokumentów

(kodu,

projektu,

wymagań); napisanie raportu z przeglądu w jasny, zrozumiały i obiektywny sposób; zastosowanie różnych technik testowania dla różnych sytuacji (np. wykorzystanie maszyny stanowej dla systemu reaktywnego czy tablicy decyzyjnej dla logiki biznesowej); przeprowadzenie czynności związanych z wykryciem incydentu (opis, raport itp.); wykorzystanie informacji konkretnego błędu;

o

klasyfikacji

wykorzystanie narzędzia do testowania pozytywnymi, jak i negatywnymi;

defektów API

do

testami,

oceny

zarówno

wykonanie zautomatyzowanych testów; napisanie prostego planu testów; zinterpretowanie liczbowych i graficznych informacji z raportów o postępie testowania. Umiejętności interpersonalne można sprawdzić, prosząc kandydata o: zaprezentowanie interesariuszom informacji o projekcie testowym, który jest opóźniony w stosunku do harmonogramu; wytłumaczenie raportu o defekcie programiście, który uważa, że defektu nie ma; opisanie, jak szkoliłby współpracownika; zaprezentowanie kierownictwu problemu nieefektywnego procesu; udział w symulowanej sytuacji konfliktowej lub stresogennej (np. związanej z presją czasu). Istnieją różne metody przeprowadzania rozmów kwalifikacyjnych. Sylabus ISTQB [213] wymienia następujące ich rodzaje:

rozmowa telefoniczna – dobry sposób na szybką, pierwszą rozmowę z kandydatem, pozwala sprawdzić zdolności komunikacyjne i językowe; często jedyna możliwość, jeśli kandydat nie może osobiście przybyć na rozmowę; wywiady grupowe i panelowe – rozmowa przeprowadzana jednocześnie przez wiele osób; jest to stresująca forma rozmowy dla kandydata, może zatem sprawdzać umiejętność działania pod wpływem stresu czy pewność siebie kandydata; pytania behawioralne – dotyczące tego, jak kandydat zachowałby się w danej sytuacji; pytania sytuacyjne – w których prosi się kandydata o podanie przykładowej sytuacji, która była jego udziałem; pytania techniczne – weryfikujące wiedzę dziedzinową kandydata; zadania logiczne (ang. puzzle solving) – sprawdzające zdolności analityczne, kombinacyjne, inteligencję oraz umiejętność szybkiego rozwiązywania problemów; szczegółowe pytania dziedzinowe – dotyczą stanowiska, na które aplikuje kandydat (np. kwestie bezpieczeństwa, automatyzacji testów); pytania dotyczące pracy w zespole – weryfikujące to, jak kandydat odnajdzie się w naszym zespole, w jaki sposób będzie współpracował z pozostałymi członkami zespołu. Z doświadczenia autora wynika, że wielu kandydatów do pracy często ma wiedzę, ale nie potrafi jej właściwie użyć i nawet nie zdaje sobie z tego sprawy. Warto więc na rozmowie kwalifikacyjnej zadawać pytania sprawdzające zdolności analityczne kandydatów. W pytaniach tych podaje się pewien scenariusz (opisując np. różne fakty dotyczące organizacji, procesu testowego, technik testowych), a następnie formułuje problem, którego rozwiązanie wymaga analizy tej sytuacji. Całe pytanie powinno być skonstruowane w ten sposób, że do pełnego rozwiązania problemu powinna wystarczyć wyłącznie wiedza podana w opisie sytuacji. Inną wartościową techniką weryfikacji kandydatów jest metoda stosowana przez szefa zespołu pewnej korporacji, w którym kiedyś pracował autor niniejszej książki. Po zakończeniu rozmowy kwalifikacyjnej szef zapraszał kandydata oraz wszystkich członków zespołu na lunch. Odbywało się to po rozmowie kwalifikacyjnej a nieformalna atmosfera sprzyjała luźnym, nietechnicznym

rozmowom, więc kandydat zazwyczaj otwierał się i zaczynał zachowywać się naturalnie i swobodnie. Dzięki temu można było lepiej go ocenić całościowo, jako potencjalnego współpracownika. Ponadto, szef tego zespołu stosował jedną prostą zasadę: kandydat zostaje zatrudniony, jeśli został zaakceptowany przez wszystkich członków zespołu (oczywiście warunkiem koniecznym było spełnienie formalnych wymogów podanych w ogłoszeniu o pracę). Ta strategia doprowadziła do stworzenia świetnego i zgranego zespołu, w którym konflikty praktycznie się nie zdarzały, panowała otwartość, gotowość niesienia pomocy oraz uczciwość wobec pozostałych osób. Praca w tym zespole była czystą przyjemnością i naprawdę świetną zabawą! Na końcu rozmowy należy zachęcić kandydata do zadania pytań kierownikowi testów. Jest to dobra okazja do dalszego poznania kandydata. Treść jego pytań może dużo mówić o jego motywacji do pracy, zainteresowaniach zawodowych oraz charakterze.

28.1.4. Asymilacja nowego członka zespołu Po zatrudnieniu nowej osoby kierownik testów powinien ją wdrożyć zarówno w procedury panujące w całej organizacji, jak i w zasady działania oraz bieżące zadania zespołu. Zasady mogą obejmować np. stosowany proces testowy, techniki projektowania testów, procedury zgłaszania defektów. Dobrą praktyką jest przeprowadzenie solidnego, wstępnego szkolenia. Szkolenie takie może być przeprowadzone przez kierownika, ale może być również współprowadzone przez innych członków zespołu, którzy zapoznają nowo przyjętą osobę z ich zakresem obowiązków. W dużych organizacjach kandydat zwykle przechodzi również szkolenie prowadzone przez dział HR, obejmujące obowiązujące w firmie procedury administracyjne. Obowiązkowe jest szkolenie BHP, stosowne do rodzaju zajmowanego stanowiska. Często nowo przyjętej osobie przydziela się tzw. mentora. Jest to jeden z członków zespołu, który nadzoruje nowego pracownika w pierwszych tygodniach jego pracy, zapoznaje go z wykorzystywanymi narzędziami, pomaga mu oraz wspiera i motywuje.

28.1.5. Zakończenie zatrudnienia Czasami przychodzi taki czas, gdy trzeba rozstać się z pracownikiem – czy to ze względu na zakończenie jego kontraktu, czy też w wyniku niespełnienia naszych

oczekiwań, co do jego efektywności bądź umiejętności. Nie jest to łatwe ani przyjemne zadanie. Kierownika testów może tu bardzo wesprzeć dział HR zwracając przede wszystkim uwagę na kwestie prawne oraz doradzając jak przeprowadzić całą procedurę. Z punktu widzenia działania zespołu testowego jest ważne, aby nie utracić umiejętności lub wiedzy, którą ma odchodzący pracownik. W tzw. okresie przejściowym (ang. transition period) należy zadbać o to, by pracownik ten przekazał wiedzę oraz produkty swojej pracy pozostałym członkom zespołu. W szczególności mogą to być: loginy i hasła dostępu; dokumenty przechowywane lokalnie; oprogramowanie zainstalowane lokalnie (np. jeśli jest tylko 1 licencja); dane testowe; skrypty testowe. Przeszkolenie innych pracowników przez odchodzącego członka zespołu minimalizuje ryzyko utraty krytycznej wiedzy czy umiejętności.

28.2. Rozwój zespołu testowego Gdy zespół jest już zbudowany, kierownik testów musi dbać o jego rozwój. Dotyczy to zarówno rozwijania umiejętności poszczególnych testerów, jak i zespołu jako całości. Kierownik testów powinien mieć odpowiednią wiedzę psychologiczną, znać potrzeby i dążenia swoich podwładnych oraz mieć wyraźną, ustaloną wizję zespołu.

28.2.1. Indywidualny rozwój poszczególnych członków zespołu Kierownik testów jest odpowiedzialny za rozwój członków zespołu. Niektóre organizacje wymagają stworzenia specjalnego planu rozwoju dla pracowników poszczególnych działów czy grup zawodowych, takich jak testerzy. Plan rozwoju może obejmować takie działania jak: formalne szkolenia; wymóg ukończenia określonych studiów/zdobycia tytułu zawodowego;

studiowanie określonej dziedziny testowania we własnym zakresie; przydzielenie testerowi nowej roli lub odpowiedzialności; certyfikacja.

Rysunek 28.2. Hierarchia potrzeb – piramida Maslowa Plan rozwoju powinien również uwzględniać różne cele i dążenia poszczególnych osób. Pomocna może być tu tzw. piramida potrzeb Maslowa przedstawiona na rysunku 28.2. Model ten pokazuje hierarchię potrzeb każdego człowieka. Teoria Maslowa mówi, że człowiek będzie dążył do zaspokojenia określonych potrzeb tylko wtedy, jeśli zostaną zaspokojone jego potrzeby bardziej podstawowe (umieszczone niżej w piramidzie)3. U każdego człowieka potrzeby te ujawniają się w różnym stopniu. Na przykład jeden członek zespołu może chcieć rozwijać się w kierunku technicznym, poznawać coraz nowocześniejsze technologie i narzędzia. Inny może dążyć do zaspokojenia potrzeb kreatywności – taka osoba ma zwykle mnóstwo innowacyjnych pomysłów. Mogą wreszcie zdarzyć się osoby chcące realizować swoją karierę w tzw. ścieżce menedżerskiej.

Kierownik projektu powinien w miarę możliwości uwzględniać wszystkie te różnorodne potrzeby i stwarzać pracownikom możliwości ich zaspokajania.

28.2.2. Wyznaczanie celów Organizacje często wymagają od kierowników zespołów, aby ci ustalali okresowe cele, jakie ich zespoły oraz poszczególni podwładni powinni osiągnąć. Cele takie powinny być zgodne z celami wysokopoziomowymi, np. zawartymi w strategii organizacji, o ile taka istnieje. Rozsądne ustalenie właściwych celów wytycza wyraźny kierunek działań, a ich bieżące monitorowanie pozwala na kontrolę stopnia ich spełnienia. Ponadto, cele umożliwiają ocenę pracownika lub całego zespołu. Cele nie powinny być narzucane odgórnie, lecz sformułowane wspólnie z członkami zespołu podczas specjalnej sesji. Spotkanie takie może trwać godzinę lub dwie, ale może też mieć formę jedno- lub kilkudniowego wyjazdu odbywającego się poza siedzibą firmy (ang. offsite), połączonego ze spotkaniem retrospektywnym czy też sesją integracyjną zespołu. Jeśli cele mają służyć późniejszej ocenie poszczególnych osób lub zespołu, to powinny być mierzalne. Należy je więc odpowiednio sformułować. Pomocna może być tutaj metoda S.M.A.R.T. Jest to akronim od pięciu angielskich słów, określających cechy definiowanych celów: Specific – cel powinien być precyzyjnie określony oraz sformułowany prosto i zrozumiale, powinien dotyczyć konkretnego, dobrze zdefiniowanego obszaru działań Measurable – cel powinien być mierzalny tak, aby można było ilościowo określić stopień jego realizacji; Attainable – cel powinien być osiągalny dla zespołu lub danej osoby; Realistic – cel powinien być realistyczny, tzn. jego sformułowanie powinno uwzględniać posiadane zasoby oraz istniejące ograniczenia projektowe; Timed – cel powinien mieć wyraźnie określone ramy czasowe, w których ma być osiągnięty. Niektórzy autorzy przypisują literom akronimu inne znaczenia, np.:

S: Significant (znaczący), Stretching (rozciągnięty), Simple (prosty), Sustainable (zrównoważony); M: Motivational (motywujący), Manageable (dający się zarządzać), Meaningful (sensowny); A: Appropriate (właściwy), Agreed (uzgodniony), Assignable (przypisany do osoby lub zespołu), Acceptable (zaakceptowany); R: Relevant (odpowiedni, trafny), Result-based (oparty na wynikach); T: Tangible (namacalny, rzeczywisty), Trackable (dający się obserwować, śledzić). metoda określania celów S.M.A.R.T. (ang. S.M.A.R.T. goal methodology) – metoda konkretnego, a nie ogólnego definiowania celów tak, aby cel był: konkretny (ang. Specific), mierzalny (ang. Measurable), osiągalny (ang. Attainable), trafny i osiągalny (ang. Relevant, Reachable) oraz terminowy (ang. Timely) Cel: „przetestować program X za pomocą technik białoskrzynkowych” jest źle postawiony. Nie jest jasne, jakie dokładnie techniki należy zastosować, w jakim czasie oraz jak zmierzyć jego osiągnięcie. Znacznie lepsza wersja tego celu może wyglądać np. tak: „osiągnąć 90% pokrycia instrukcji i decyzji oraz 85% pokrycia MC/DC dla programu X do końca grudnia 2014 roku”. Tu wiadomo konkretnie, jakie techniki mają być wykorzystane, w jaki – ilościowy! – sposób mierzone ma być pokrycie i do kiedy cel należy osiągnąć. Podczas definiowania celów kierownik testów powinien określić produkty powstałe w wyniku osiągania celów (ang. deliverables), tzn. zdefiniować, co ma być zrobione, w jaki sposób oraz w jakim czasie. Niewłaściwe określenie celów może prowadzić do niepożądanych sytuacji. Na przykład cel dotyczący liczby zaprojektowanych testów czy poziomu gęstości defektów nie jest zwykle zależny od jednego, konkretnego testera, dlatego miary takie nie powinny być podstawą do ocen indywidualnych. Kierownik testów powinien również pamiętać o bardzo ważnej zasadzie: ludzie dostosowują swoje działanie pod kątem tego, co jest mierzone, kosztem tego, co nie jest mierzone. Cele powinny więc motywować zespół do działań sensownych i pożądanych z punktu widzenia przyjętej taktyki czy strategii. Na przykład, jeśli naszym celem jest dostarczenie odpowiednio dużej liczby przypadków testowych, efektem będzie stworzenie przez testerów mnóstwa trywialnych, nic niewnoszących testów. Może to skutkować brakiem wykrytych awarii lub wykryciem mało znaczących błędów.

28.2.3. Dynamika zespołu testowego W 1965 roku Tuckman [214] zdefiniował model dynamiki zespołu, który opisuje konieczne i nieuniknione fazy związane z jego rozwojem, podejmowaniem wyzwań, rozwiązywaniem problemów, planowaniem i dostarczaniem wyników. Model jest schematycznie przedstawiony na rysunku 28.3. Podstawowy model wyróżnia cztery fazy: formowania, okresu burzy, normowania oraz działania. Zwykle następują one po sobie, jednak w wyniku reorganizacji może nastąpić „cofnięcie się” do faz wcześniejszych. Aby zespół efektywnie przeszedł przez wszystkie etapy swojego rozwoju do końcowej fazy działania, w każdym z nich kierownik powinien odgrywać nieco inną rolę wobec członków zespołu.

Rysunek 28.3. Model Tuckmana dynamiki zespołu Formowanie. Występuje, gdy nowy zespół dopiero się formuje lub przeszedł znaczącą reorganizację. W okresie tym członkowie zespołu zaczynają poznawać siebie nawzajem, swoje wartości, priorytety, cele, umiejętności, sposoby pracy. Na tym etapie kierownik testów powinien pracować z wszystkimi członkami zespołu w celu określenia jego misji i zakresu prac. Powinien też zadbać o stworzenie atmosfery życzliwości, otwartości i zaufania. Wartości te będą bowiem niezbędne dla dalszego działania. Okres burzy. Po fazie formowania zwykle następuje „okres burzy i naporu”. Członkowie zespołu zaczynają rozumieć, że w celu efektywnej pracy w grupie muszą zmienić swoje postępowanie oraz niektóre nawyki, a także dostosować się do różnych norm czy regulacji. Są często niezdecydowani: czy kłaść nacisk na realizację indywidualnych celów,

czy też pracować „na zespół”? Na tym etapie często występują konflikty, niechęć, podważanie autorytetu kierownika, próby tego, na ile można sobie pozwolić w swojej niezależności. Jest to faza „docierania się”, podobna do okresu dojrzewania w życiu normalnego człowieka. Kierownik musi być świadomy, że jest to okres przejściowy i – prędzej czy później – sytuacja się unormuje. Czasami nie trzeba nawet podejmować żadnych szczególnych kroków, choć zwykle w okresie burzy rola kierownika polega na pomaganiu zespołowi w rozwiązywaniu bieżących konfliktów. Na tym etapie to kierownik wciąż ma decydujący głos odnośnie sposobów osiągania przez zespół założonych celów. Normowanie. Okres normowania następuje, gdy członkowie zespołu „dotrą się” i wypracują efektywne metody współdziałania. Jest to czas, w którym zaczynają oni pracować zespołowo jako jedna drużyna. W fazie normowania kierownik zaczyna dzielić się odpowiedzialnością z poszczególnymi członkami zespołu i pozwala mu na samoorganizację. Scholtes [215] zauważa, że kluczowe dla tego etapu jest zbudowanie w zespole pewności i przekonania o tym, iż jego członkowie potrafią radzić sobie z występującymi u nich różnicami tak, aby żaden z nich nie czuł się pominięty lub niedoceniony. Działanie. Jest to pożądany, końcowy etap rozwoju. Zespół działa efektywnie, istnieje dobrze określony podział ról i obowiązków, wszyscy wzajemnie się uzupełniają. Rola kierownika przesuwa się z autorytatywnego dowództwa w stronę coachingu, doradztwa. Jego głównym zadaniem staje się monitorowanie działań zespołu oraz zachodzących w nim interakcji w celu rozwiązywania ewentualnych problemów wymagających interwencji. Gdy zespół wykona postawione przed nim zadanie, może przejść do następnego lub też zakończyć swoje działanie – jest to tzw. faza zakończenia (ang. adjourning), w której zespół podlega rozwiązaniu.

28.2.4. Określanie ról i odpowiedzialności Kierownik testów powinien każdemu członkowi zespołu przypisać określoną rolę i zakres odpowiedzialności. Oczywiście mogą one zmieniać się w czasie, ale ich

precyzyjne zdefiniowanie, w połączeniu z ustaleniem odpowiednich celów, jest konieczne do tego, aby umożliwić realną ocenę efektywności poszczególnych pracowników. Role i zakres odpowiedzialności mogą różnić się w zależności od firmy, jej wielkości, struktury oraz rodzaju prowadzonych w niej projektów. Przykładowe, najczęściej spotykane w ogłoszeniach o pracę stanowiska testerskie to: tester (ogólna rola, zwykle związana i wykonywaniem testów czarnoskrzynkowych);

z

projektowaniem

inżynier ds. automatyzacji testów (wymaga znajomości języków skryptowych i odpowiednich technologii); inżynier jakości (rola związana z definiowaniem, monitorowaniem i kontrolą jakości, choć w praktyce sprowadza się głównie do testowania, w tym niefunkcjonalnego); lider zespołu testerów (rola kierownicza, dotycząca zarządzaniem zespołem testerskim). Sylabus ISTQB [213] wymienia następujące role: tester czarnoskrzynkowy; tester białoskrzynkowy; inżynier ds. testów wydajnościowych; administrator danych i środowisk testowych; deweloper narzędzi; lider zespołu testowego; architekt testów; specjalista ds. automatyzacji; inżynier ds. testów bezpieczeństwa. Są to bardziej role niż stanowiska pracy. Na przykład, w ramach obowiązku testera może być zarówno przeprowadzanie testów czarno-, jak i białoskrzynkowych. Jedna osoba może odgrywać w projekcie kilka ról. Do opisu podziału ról i odpowiedzialności wygodnie jest stosować narzędzie typu matryca RACI (ang. RACI matrix). Jest to macierz, w której poszczególne wiersze odpowiadają zadaniom realizowanym w ramach zespołu, a kolumny – rolom lub poszczególnym członkom zespołu. Na przecięciu wiersza i kolumny określa się rodzaj odpowiedzialności danej osoby za określone zadanie.

Odpowiedzialność może przyjąć jedną z czterech form oznaczanych literami R, A, C, I: R – Responsible (odpowiedzialny) – osoba wykonanie pracy w celu ukończenia zadania;

odpowiedzialna

za

A – Accountable (rozliczany) – osoba odpowiedzialna za ukończenie zadania; w każdym wierszu macierzy musi być przynajmniej jedno pole „A”; C – Consulted (konsultowany) – osoba, z którą należy się konsultować podczas wykonywania zadania; jest to komunikacja dwustronna; I – Informed (informowany) – osoba, którą należy informować o wykonywanym zadaniu; jest to komunikacja jednostronna. Przykład matrycy RACI dla zespołu testowego jest pokazany na rysunku 28.4.

Kierownik Architekt Inżynier ds . tes tów tes tów automatyzacji

Techniczny Anality analityk tes tów tes tów

Wyznaczanie celów

R, A

C, I

I

S zacowanie tes tów

A, C

R

I

C, I I

Analiza ryzyka A

A

C, I

C, I

R

Projektowanie I tes tów

R, A

C, I

C, I

C, I

Tworzenie s kryptów

C

A

R

C

Wykonywanie tes tów

I

R, A

C

I

Rysunek 28.4. Matryca RACI

Jest to bardzo „wysokopoziomowa” macierz. Zadania w niej zdefiniowane są bardzo ogólne. Matryca RACI może opisywać o wiele bardziej szczegółowe zadania, takie jak „wykonanie testów regresji dla modułu XYZ”. matryca odpowiedzialności – model RACI (ang. RACI matrix) – macierz przedstawiająca udział różnych ról w wykonywaniu zadań lub produktów w projekcie lub procesie. Jest szczególnie użyteczna w wyjaśnianiu ról i odpowiedzialności

28.2.5. Umiejętności zespołu jako całości: gap analysis Powiedzieliśmy już, że kierownik testów dba o rozwój poszczególnych członków swojego zespołu. Jednak musi mieć on na uwadze również sam zespół jako taki. Zbiór umiejętności wszystkich pracowników powinien być „zrównoważony” tak, aby zespół był jak najbardziej wszechstronny w zakresie stawianych przed nim zadań. Wygodnym narzędziem, jakie kierownik projektu może wykorzystać do analizy i planowania w tym zakresie jest tzw. inwentarz umiejętności. Może on mieć formę zwykłego arkusza kalkulacyjnego. Przykładowy inwentarz, wzorowany na arkuszu z książki [173] jest pokazany na rysunku 28.5. W kolejnych wierszach są wymienione umiejętności, pogrupowane w cztery kategorie: ogólne, związane z testowaniem, dotyczące wiedzy dziedzinowej oraz techniczne. W pierwszych dwóch kolumnach opisano minimalne wymagania dla dwóch typów ról: analityka oraz technicznego analityka testów. Litera W oznacza, że dana umiejętność jest wymagana, P – że jest pożądana. Kolejne cztery kolumny dotyczą poszczególnych

Rysunek 28.5. Inwentarz umiejętności i gap-analysis

członków zespołu: kierownika, analityka i dwóch technicznych analityków. W ostatnich dwóch kolumnach znajdują się dwie proste statystyki dla każdej umiejętności: minimalny poziom oraz średni poziom w zespole. Inwentarz jest wygodnym narzędziem, które pozwala spojrzeć na zespół „z lotu ptaka” i ocenić, kto i w jakim obszarze powinien się rozwijać tak, aby zespół jako całość był możliwie wszechstronny. Na przykład: TTA Katarzyna Iksińska nie spełnia wymaganego poziomu umiejętności formalnej komunikacji pisemnej (wymagany poziom to 2 wobec posiadanego poziomu 1). Kierownik może np. poprosić innego pracownika, np. TTA Pawła Nowaka, aby pomógł Katarzynie Iksińskiej podczas pisania kilku formalnych dokumentów. Dzięki temu powinna ona zwiększyć swoje umiejętności w tym zakresie. Grażyna Malinowska ma bardzo wiele umiejętności na dosyć wysokim poziomie, jest więc cennym członkiem zespołu. Jej odejście byłoby bardzo dużą stratą. Być może warto rozważyć jej awans na stanowisko lidera zespołu testowego. Słabą stroną zespołu jest brak doświadczenia w metodykach zwinnych. Kierownik powinien zorganizować dla zespołu szkolenie w tym zakresie.

28.2.6. Indywidualne osobowości i role w zespole Każdy członek zespołu, oprócz tzw. umiejętności twardych, ma swoją unikalną osobowość, która wpływa na jego funkcjonowanie w grupie. Rozpoznanie indywidualnych cech osobowościowych pracowników może pomóc kierownikowi testów w zarządzaniu zespołem, w szczególności w zakresie motywowania oraz zarządzania konfliktami. Jedną z metod określenia typów osobowości jest tzw. wskaźnik osobowości Myers–Briggs (ang. Myers–Briggs Type Indicator, MBTI) [216]. Na rysunku 28.6 przedstawiono model MBTI: cztery kategorie osobowości, z których każda zawiera dwa przeciwstawne typy.

Rysunek 28.6. Wskaźnik osobowości Myers–Briggs Każdy człowiek ma wszystkie 8 cech, ale w różnym stopniu. Zwykle w każdej z czterech kategorii jeden z typów jest przeważający. Opisuje się je czterema literami oznaczającymi dominujące typy: E lub I, S lub N, T lub F, J lub P. Istnieje więc 16 kombinacji określających 16 odmiennych typów osobowości. Każdy z nich jest „normalny” – nie ma lepszych ani gorszych typów, przy czym osoba o określonym wskaźniku osobowości w pewnych sytuacjach będzie działać lepiej, a w innych gorzej. test osobowości Myers–Briggs (ang. Myers–Briggs Type Indicator, MBTI) – wskaźnik preferencji psychologicznych reprezentujący różne typy osobowości i style komunikacji Osoby techniczne, takie jak inżynierowie oprogramowania, najczęściej cechują się typem ISTJ – introwertyk, zmysły, analiza, osąd (w populacji USA jest 11,6% osób o tym typie, podczas gdy w populacji informatyków aż 25%; kolejne często spotykane typy ludzi branży IT to INTJ (16%) oraz ENTP (9%)) [217]. Są to najczęściej osoby introwertyczne, niekierujące się intuicją, lecz zmysłami, obserwacjami i faktami. Mają wysoko rozwinięte zdolności analityczne kosztem umiejętności odczuwania i empatii. Są osobami wolącymi wydawać zdecydowane sądy, mają własne poglądy i nie są zbyt elastyczne jeśli chodzi o ich zmianę czy rewizję. Z kolei, według Evans [218], właściciele biznesowi produktu (ang. business owners) cechują się najczęściej typem ESTJ – ekstrawertyzm, zmysły, analiza, osąd. Jak widać, typy inżynierów (INTJ oraz ENTP) i właścicieli biznesowych produktu są zgodne jedynie w 50%. To może tłumaczyć problemy, jakich doświadczają zespoły projektowe w kontaktach z klientami.

Poznanie wskaźników osobowości Myers–Briggs wszystkich członków zespołu niesie ze sobą wiele zalet. Na przykład, w kontekście wprowadzanych zmian kierownik testów: może lepiej zrozumieć, jak proponowane zmiany będą zaadaptowane przez zespół; może przewidzieć, czy zmiany będą przyjęte pozytywnie (P: percepcja), czy też powstanie opór (I: introwertyzm); może dokładniej określić, czy ludzie będą eksperymentowania i akceptacji pewnych

przygotowani do porażek podczas

wprowadzania zmian (E: ekstrawertyzm, N: intuicja), czy raczej będą woleli poczekać do uzyskania „idealnego” rozwiązania (T: analiza, J: osąd); może przewidzieć sposób komunikacji między poszczególnymi członkami zespołu: ludzie o podobnych typach osobowości (np. ISTP i INTP) będą zwykle lepiej ze sobą współpracować niż ludzie o odmiennych typach (np. ESTJ i INFP) [219]. Istnieje więcej metod i technik poznawania, opisywania i klasyfikowania charakterów oraz typów osobowości. Belbin [220] proponuje schemat pozwalający odkryć słabe i silne strony osobowości, opisując dziewięć typów ról oraz ich wady i zalety. Evans [218] proponuje technikę określania interesariuszy projektu. Wagner [221] jest autorem analizy transakcyjnej, która pozwala badać sposób interakcji między ludźmi, a także dostarcza metody umożliwiające zrozumienie werbalnych komunikatów (transakcji). analiza transakcyjna (ang. transactional analysis) – analiza interakcji między ludźmi z uwzględnieniem ich stanów ego (stanów umysłu); transakcja jest zdefiniowana jako bodziec wraz z odpowiedzią; transakcje zachodzą między ludźmi oraz między stanami ego (osobowościami) znajdującymi się w umysłach tych ludzi Wagner wyróżnia sześć „wewnętrznych ja” (które nazywa stanami ego) pokazanych na rysunku 28.7, które ma każdy człowiek. Z punktu widzenia komunikacji trzy z nich (na rysunku po lewej stronie) są efektywne i pomagają w porozumiewaniu się, a trzy pozostałe (po prawej stronie) są nieefektywne i utrudniają komunikację.

Rysunek 28.7. Model analizy transakcyjnej Wagnera Kierownik testów może przeprowadzić analizę transakcyjną, aby zrozumieć, jak członkowie zespołu komunikują się ze sobą i jak tę komunikację polepszyć. W metodzie tej dąży się do uzyskania interakcji typu „dorosły-do-dorosłego”. Kierownik, jako jedna z komunikujących się stron, powinien przyjąć postawę „dorosłego”: być rozsądnym, rozumiejącym, słuchającym i skupionym na drugiej osobie. Nie może osądzać („w tym miejscu popełniłeś błąd!”), obwiniać („przez ciebie mamy opóźnienia”) ani być sarkastyczny. Przyjęcie postawy „surowego rodzica” może tylko pogorszyć sprawę, bo zwykle skutkuje przyjęciem przez rozmówcę postawy „wojowniczego dziecka” lub „posłusznego dziecka”, przez co komunikacja będzie nieefektywna. Zasadniczą umiejętnością, jaką powinien mieć kierownik, jest rozpoznanie „wewnętrznego ja” osób, z którymi rozmawia. Komunikacja z osobą o typie „naturalnego dziecka” może wymagać okresowego sprowadzania dyskusji na „właściwe tory”. „Wojownicze dziecko” może w ogóle być do kierownika nastawione negatywnie tylko z tego powodu, że to nie ono jest kierownikiem. Ta postawa, wraz z „posłusznym dzieckiem” występują zwykle na początku konwersacji. Kierownik powinien rozpoznać wewnętrzne ego interlokutora i tak

przeprowadzić dyskusję, aby wydobyć z niego postawę „dorosłego” lub przynajmniej „naturalnego dziecka” bądź „opiekuńczego rodzica”.

28.2.7. Rozwój umiejętności i szkolenia Jedną z najlepszych metod rozwoju zespołu testowego jest zapewnienie jego członkom możliwości ciągłego szkolenia się i podnoszenia swoich umiejętności. Szkolenia mogą być nieformalne (np. jeśli trening jest przeprowadzany przez jednego z członków zespołu, który dzieli się wiedzą z innymi) lub formalne (np. jeśli szkolenie jest przeprowadzane przez zewnętrzną firmę, która dostarcza również materiały szkoleniowe). Niektóre organizacje wykorzystują platformy do e-learningu, oferując szeroką gamę kursów z różnych dziedzin. Kierownik testów powinien działać optymalnie, bo budżet na szkolenia prawie nigdy nie jest wystarczający. Dlatego zawsze warto najpierw sięgnąć do zasobów wewnętrznych (dzielenie się wiedzą w ramach zespołu), co ma tę dodatkową zaletę, że minimalizuje ryzyko utraty wiedzy lub doświadczenia w przypadku odejścia danej osoby z zespołu. Można wykorzystać również zasoby samej organizacji. W dużych firmach istnieją specjalne zespoły zajmujące się szkoleniem. Często korporacje takie zatrudniają trenerów specjalizujących się w określonych obszarach, np. zarządzaniem projektami w metodykach zwinnych. Warto wykorzystać wiedzę i doświadczenie takich osób. Można na przykład poprosić trenera Scrum o przeprowadzenie dla testerów szkolenia z tej metodologii lub o wsparcie we wdrożeniu jej w naszym zespole.

Rysunek 28.8. Style uczenia się według Honeya i Mumforda Inną metodą rozwoju jest zachęcanie członków zespołu do budowania relacji z osobami spoza działu testowego. Będzie to skutkować nie tylko zbudowaniem pozytywnych relacji z innymi pracownikami, lecz także może być szansą na poszerzenie wiedzy testerów w zakresie nieznanych im dotąd obszarów (np. projektowanie systemów, pozyskiwanie wymagań, wiedza o konkretnym języku programowania czy wykorzystywanej w firmie technologii itp.).

Kierownik testów, planując aktywności związane z rozwojem swoich pracowników, powinien wziąć pod uwagę różne style uczenia się. Honey i Mumford [222] proponują klasyfikację identyfikującą cztery style uczenia się, opisując zalety i wady każdego z nich. Model ten jest przedstawiony na rysunku 28.8. Dla poszczególnych osób, o odmiennych stylach uczenia się, można przygotować odmienny typ szkolenia. Na przykład dla naśladowcy dobre będą szkolenia wykorzystujące metody podawcze, zawierające instrukcje mówiące, co i w jakiej kolejności należy wykonywać. Z kolei dla teoretyka wartościowe będą szkolenia wymagające kreatywnego myślenia. Aktywiści dobrze będą się uczyć w grupie, wykonując wspólne ćwiczenia i zadania. Aby szkolenie było efektywne dla pragmatyka, musi zawierać „mięso” – nie powinno być przeładowane teorią, lecz dostarczać praktycznych, konkretnych przykładów.

28.2.8. Mentoring Pracownikom zespołu w indywidualnym rozwijaniu się pomóc mogą tzw. mentorzy. Są to zwykle starsi stażem, bardziej doświadczeni pracownicy. Mentoring, czyli doradztwo, jest relacją pozwalającą na odkrywanie swojego potencjału przez wykorzystanie informacji zwrotnej od mentora, który doradza, podpowiada i stymuluje do rozwoju. W pewnym zakresie mentorami mogą być również pracownicy innych działów. Na przykład techniczny analityk testów może rozwijać swoje umiejętności testowania białoskrzynkowego przez pracę z deweloperem, na przykład wykorzystując model programowania w parach.

28.2.9. Okresowa ocena członków zespołu Wiele organizacji wymaga przeprowadzania okresowej oceny pracowników. Zwykle odbywa się ona raz na rok, rzadziej co pół roku lub co dwa lata. Od oceny może zależeć decyzja o awansie, nagrodzie lub podwyżce. Jej okresowość pozwala również na śledzenie postępów w rozwoju pracownika. Kierownik testów, przygotowując się do przeprowadzenia ewaluacji, powinien zebrać dane dotyczące danego pracownika, obejmujące okres oceny (np.: raporty, przypadki testowe, pomysły na usprawnienie pracy, czyli tzw. wnioski racjonalizatorskie); może również zasięgnąć opinii innych osób, np. spoza działu

testowego, z którymi pracował tester, aby dowiedzieć się, jak oceniają tę współpracę. Kierownik może posłużyć się ustandaryzowanym kwestionariuszem lub po prostu zebrać luźne opinie. Niektóre firmy zobowiązują pracowników do dokonania samooceny. Informacje te są włączane wtedy do danych stanowiących podstawę ewaluacji. Kierownik testów powinien dostarczyć każdemu ocenianemu pracownikowi informację zwrotną, zawierającą w szczególności: odniesienie do samooceny pracownika; wyniki pracownika uzyskane w procesie ewaluacji; sugestie, co pracownik może poprawić, na czym się skupić, jakie umiejętności rozwijać; merytoryczne uzasadnienie oceny. Uzasadnienie może również zawierać pochwały, uwagi krytyczne oraz opinie lub fragmenty opinii innych osób o danym pracowniku. Szczególnie ważne jest umiejętne docenienie pracownika tak, by widział, że doceniamy jego wysiłek i pracę. Jeśli zespół ustalał cele metodą S.M.A.R.T. należy także przedstawić ilościowo stopień osiągniętych przez pracownika celów indywidualnych.

28.3. Przywództwo 28.3.1. Zarządzanie a przywództwo Istnieje zasadnicza różnica między zarządzaniem a przywództwem. Nie każdy menedżer jest dobrym przywódcą i na odwrót – nie każdy przywódca może być sprawnym menedżerem. Zarządzanie to radzenie sobie ze złożonością. Duże projekty mają skomplikowaną strukturę oraz mnóstwo zależności i ograniczeń, nad którymi ktoś musi panować. Projekt musi być zaplanowany, należy oszacować jego wielkość i koszt, zdefiniować harmonogram, przydzielić zasoby, a następnie kontrolować postępy, synchronizować działania, kierować i nadzorować. W trakcie prac zawsze pojawia się wiele problemów natury organizacyjnej, z którymi trzeba sobie radzić. Te wszystkie działania i czynności stanowią właśnie o złożoności problemu.

Przywództwo to radzenie sobie ze zmianą. Przywództwo wymaga umiejętności wywierania społecznego wpływu na ludzi. Każda zmiana – sposobu pracy, narzędzia pracy, procesu, projektu itd. – jest przyjmowana z nieufnością i oporem przez ludzi, których dotyczy. Rolą przywódcy jest przekonanie ich, że zmiana ta przyniesie pozytywne skutki oraz motywowanie do przeprowadzenia tej zmiany.

28.3.2. Model zmiany według Satir Aby przeprowadzić w zespole zmianę, ludzie muszą być do niej odpowiednio przygotowani. Należy więc dostarczyć im zasoby, narzędzia, wiedzę, umiejętności i motywację, które będą potrzebne, aby zaczęli wykonywać swoje zadania w inny sposób. Przy wprowadzaniu zmian, zwłaszcza dużych, obejmujących całą organizację lub dużą jej część, zawsze trzeba pamiętać o jej związku z produktywnością. Opisuje to model Virginii Satir [223] (tzw. model zmiany) przedstawiony na rysunku 28.9.

Rysunek 28.9. Model zmiany według V. Satir Na początku produktywność oscyluje na pewnym określonym poziomie. Wprowadzenie zmiany powoduje nastąpienie chaosu, który obniża produktywność. Okres ten jest jednak przejściowy i należy być na niego

przygotowanym. Po pewnym czasie, gdy niektórzy ludzie przekonają samych siebie do zmiany (bo np. zobaczą, że pracuje im się lepiej, wygodniej, wydajniej itd.) w naturalny sposób będą wpływać na innych, stymulując ich do zmiany swoich przyzwyczajeń. Jest to faza integracji i praktyki, w której obserwuje się powolny wzrost produktywności, który w końcu ustabilizuje się na wyższym, niż początkowy poziomie – o ile oczywiście zmiana będzie sensowna. zarządzanie zmianą (ang. change management) – 1) ustrukturalizowane podejście do przechodzenia jednostek, zespołów i organizacji z bieżącego do przyszłego, pożądanego stanu; 2) kontrolowany sposób wprowadzania zmiany lub proponowanej zmiany do produktu lub usługi; patrz także: zarządzanie konfiguracją Wielu menedżerów nie rozumie, że zmiany praktycznie zawsze wiążą się z chwilowym spadkiem wydajności pracy. Spadek ten jest zupełnie naturalny, ponieważ zespół musi mieć czas, aby dostosować się do nowej rzeczywistości. Bardzo często kierownicy, zamiast dać zespołowi czas na zmianę nawyków i przyzwyczajeń popełniają błąd, podejmując w środku fazy „chaosu” decyzję o zaniechaniu wprowadzenia zmiany. Lider wdrażający zmianę musi potrafić przekonać kierownictwo, że niższa efektywność jest zjawiskiem przejściowym. Tylko wtedy uda się w pełni wdrożyć zmianę, z wszystkimi jej korzyściami.

28.3.3. Cechy charakteru lidera i zasady przywództwa Dokument MCRP 6-11B W/CH 1 Dowództwa Piechoty Morskiej USA [224] definiuje cechy, które powinien mieć lider. Choć dotyczy on przywództwa w kontekście dowodzenia w armii, są to cechy na tyle uniwersalne, że można je zaadaptować w zasadzie do każdego typu lidera. Pokazane są one na rysunku 28.10.

Rysunek 28.10. Cechy charakteru lidera W tym samym dokumencie znajdziemy następujące zasady przywództwa, które również można zaadaptować dla roli kierownika testów: znaj siebie i ciągle dąż do samorozwoju; bądź biegły w technice i w taktyce; rozwiń poczucie współodpowiedzialności wśród podwładnych; podejmuj czytelne decyzje i podejmuj je na czas; dawaj przykład; znaj swoich żołnierzy (w naszym przypadku: testerów) i dbaj o ich dobro; informuj swoich żołnierzy; bierz odpowiedzialność za swoje działania; upewnij się, że zadania są rozumiane, nadzorowane i kończone; trenuj żołnierzy tak, aby stanowili drużynę; znaj granice swojego dowództwa.

28.3.4. Informowanie i komunikacja

Zadaniem kierownika testów jest upewnienie się, że zespół testowy dobrze rozumie swoją rolę w organizacji oraz że każdy tester zna swoją funkcję w zespole. Aby osiągnąć ten cel, kierownik musi dzielić się z zespołem wszelką wiedzą na temat projektów, w których zespół uczestniczy. Wiedza ta pochodzić może np. ze spotkań kierownictwa wyższego szczebla, na których są omawiane bądź podejmowane decyzje o charakterze strategicznym. Wymiana informacji musi być jednak dwustronna i nie może się sprowadzać wyłącznie do informowania zespołu. Kierownik może wykorzystać informacje przekazywane mu przez zespół w trakcie rozmów z kierownictwem projektu lub zarządem. Przykładowe sposoby wymiany informacji to: spotkania „jeden na jeden”; poczta elektroniczna i wewnętrzne fora dyskusyjne; okresowe spotkania całego zespołu; raporty (np. weekly status reports); komunikacja nieformalna.

28.3.5. Lojalność, zaufanie i odpowiedzialność Lojalność i zaufanie są podstawą dobrego przywództwa i warunkiem koniecznym efektywnego działania zespołu. Kierownik testów musi być wobec swoich podwładnych otwarty i uczciwy. Powinien dążyć do stworzenia atmosfery, w której żaden członek zespołu nie będzie bał się wygłosić krytycznej opinii, czy popełnić błędu. Sam kierownik również powinien umieć przyznawać się do błędów. Dobry lider nie tylko wydaje polecenia, ale również uczy się od swoich podwładnych. Musi także umieć brać odpowiedzialność za działania swoje i swojego zespołu. Kierownik-lider jest adwokatem swoich ludzi, a każdy ich sukces odpowiednio nagłaśnia, tak, aby inni pracownicy byli świadomi profesjonalizmu oraz roli, jaką odgrywa zespół testerski w organizacji. W ten sposób lider buduje autorytet i zaufanie do zespołu w oczach innych działów i kierownictwa.

28.3.6. Budowanie zespołu

Poza czysto zawodowymi kwestiami, kierownik testów może wykorzystywać inne metody umacniające zespół jako całość. Oto kilka przykładów takich aktywności: wspólny obiad/lunch; impreza z okazji zakończenia projektu (np.: wspólne wyjście na gokarty, kręgle lub po prostu na piwo); stworzenie newslettera zespołu; organizacja seminariów (np. w porze obiadowej), na których poszczególni członkowie zespołu dzielą się swoją wiedzą na temat nowych narzędzi, aplikacji czy technologii, które można wykorzystać w codziennej pracy; celebrowanie osobistych, ważnych wydarzeń w życiu poszczególnych członków zespołu, np. urodzin, ślubów czy narodzin dziecka. Tego typu aktywności umacniają więzi między poszczególnymi osobami. Zespół staje się bardziej zgrany, a ludzie po prostu zwyczajnie lubią ze sobą przebywać, co jest kluczowe dla efektywności prac.

28.4. Poziomy niezależności zespołu testowego Każda organizacja ma swoje metody organizacji zespołów, w tym zespołów testowych. W małych firmach, szczególnie w tych stosujących metodyki zwinne często nie ma formalnie wydzielonego zespołu testowego. W organizacjach większych sprawa może być bardziej skomplikowana, ponieważ sposób organizacji wpływa na poziom niezależności, a to bezpośrednio przekłada się na efektywność pracy oraz dostarczaną jakość. Kierownik testów musi zdawać sobie sprawę z konsekwencji przyjęcia przez firmę odpowiedniego poziomu niezależności i dostosować do niego metody zarządzania. Sylabus ISTQB Poziom Zaawansowany – Kierownik Testów [196] wyróżnia następujące poziomy niezależności, uszeregowane według jej stopnia: brak niezależnych testerów – sytuacja częsta w małych organizacjach; kod jest testowany przez dewelopera, który – ze względu na brak lub ograniczenie stosowania narzędzi do zarządzania incydentami – może szybko poprawić defekt; gdy brak niezależnych testerów, zwykle nie

wykorzystuje się narzędzi pomocnych w zbieraniu danych o defektach, czasie naprawy itd., co może utrudnić kontrolę projektu oraz predykcję jakości; testowanie wykonuje programista inny niż autor kodu – występuje mała niezależność między deweloperem a testerem; istnieje ryzyko, że deweloper w roli testera nie wykryje określonego typu defektów, programista zwykle dostarcza „pozytywne” przypadki testowe; testowanie wykonuje tester będący częścią zespołu deweloperskiego – lepsze skupienie się na testowaniu pod kątem spełnienia wymagań; ze względu na to, że tester jest członkiem zespołu wytwórczego, może mieć inne zadania poza czysto testerskimi; istnieje ryzyko, że testowanie będzie miało niższy status niż wytwarzanie oprogramowania; testowanie wykonywane przez zespół testerski, nie związany z zespołem deweloperskim – wyniki testów są obiektywnie raportowane interesariuszom, jest kładziony większy nacisk na jakość, rozwój zawodowy nastawiony jest na polepszanie umiejętności testerskich, testowanie postrzegane jest jako ścieżka kariery zawodowej, występuje zwykle osobna rola dla zarządzania zespołem testerskim, zespół testerski ma większy autorytet funkcjonujący w ramach zespołu deweloperskiego;

niż testerzy

specjaliści z zewnątrz wykonujący określone typy testów – w takim podejściu mamy zapewnione profesjonalne, eksperckie podejście do testowania określonych obszarów, którego nie jesteśmy w stanie samodzielnie przeprowadzić ze względu na brak wiedzy, czasu, środków lub odpowiedniego sprzętu; spojrzenie eksperta na całość tworzonego oprogramowania jest ograniczone, skupi się on wyłącznie na specyficznych testach, a więc na wycinku systemu; testowanie wykonywane przez organizację zewnętrzną – ten model daje maksymalną niezależność zespołu testerskiego; wymaga jednak dobrej komunikacji, zwłaszcza w zakresie wymagań oraz w przypadku, gdy w proces testowania jest zaangażowany klient (wtedy komunikacja występuje pomiędzy trzema podmiotami); przed skorzystaniem z usług firmy zewnętrznej należy upewnić się, co do jej profesjonalizmu i jakości wykonywanych przez nią prac; można przeprowadzać okresowe audyty tej organizacji.

W dużych projektach można wykorzystywać podejścia mieszane. Na przykład zespół projektujący GUI może pracować w metodyce zwinnej i nie mieć wydzielonych ról testerskich, zespół tworzący back-end może korzystać z usług wewnętrznego zespołu testerów, a dla testów bezpieczeństwa i wydajności firma może korzystać z usług zewnętrznej organizacji. Kierownik testów musi uwzględnić wszystkie te formy organizacji zespołów testerskich zarówno podczas planowania (harmonogram, budżet), szacowania ryzyka, jak i zapewnienia skutecznej komunikacji oraz monitorowania postępów. niezależność testowania (ang. independence of testing) – rozdzielenie odpowiedzialności, które sprzyja zapewnieniu obiektywności testowania [225] Podczas wyboru poziomu niezależności jest konieczne dokonywanie pewnych kompromisów. Większa niezależność może powodować izolację zespołu testerskiego oraz niższy transfer wiedzy. Niski poziom niezależności zwiększa szansę lepszego transferu wiedzy, ale może powodować powstanie sprzecznych celów (np. jakość vs. harmonogram/budżet) [196].

28.5. Komunikacja Kierownik testów, ze względu na pełnioną funkcję musi mieć bardzo dobrze rozwinięte umiejętności komunikacji za pomocą różnych środków przekazu. W codziennej pracy uczestniczy on aktywnie w wielu spotkaniach i przeprowadza dużą ilość rozmów z wieloma podmiotami. Na rysunku 28.11 przedstawiono zadania kierownika testów w zakresie

schematycznie przykładowe komunikowania.

Rysunek 28.11. Komunikacja kierownika testów z innymi podmiotami Komunikacja musi odbywać się w sposób obiektywny i profesjonalny. Dobry kierownik cechuje się wyczuciem, kulturą oraz talentem dyplomatycznym. W przypadku tworzenia prezentacji ich poziom szczegółowości należy dostosować do oczekiwań odbiorcy. Kierownik testów powinien sprawnie posługiwać się wszystkimi formami komunikacji, takimi jak: komunikacja werbalna; komunikacja pisemna (formalna i nieformalna); spotkania (formalne i nieformalne); raportowanie (formalne i nieformalne).

1 Podawanie „widełek” pensji jest standardem w krajach takich jak Wielka Brytania. W Polsce praktycznie żadna firma nie praktykuje tego zwyczaju. 2 Na przykład tak, jak w słynnych już parodiach ogłoszeń typu „przyjmę do pracy osobę młodą, tuż po studiach, z 20-letnim doświadczeniem”. 3 Ta teza nie zawsze jest zgodna z rzeczywistością i w gruncie rzeczy zależy w dużej mierze od okoliczności. Na przykład podczas II wojny światowej w okupowanej przez Niemców Polsce wystawiano tajne przedstawienia teatralne oraz grano koncerty, a więc realizowano potrzeby samorealizacji mimo braku zapewnienia bardziej podstawowych potrzeb bezpieczeństwa.

29. Techniki pracy grupowej

W rozdziale tym opiszemy kilka podstawowych technik pracy grupowej. Metody te pozwalają zwiększyć produktywność spotkań, w których bierze udział większa liczba osób. Kierownik testów może wykorzystać techniki pracy grupowej do wspomagania kreatywności, porządkowania i priorytetyzacji pomysłów, szacowania czy przeprowadzania analiz. Kierownik powinien wiedzieć, które metody się sprawdzą, w jakich sytuacjach. Efektywność tych technik zależy bowiem nie tylko od rodzaju problemu, jaki mamy do rozwiązania, ale także od składu grupy oraz cech osobowościowych poszczególnych osób biorących udział w spotkaniu. Istnieją różne typy spotkań. W szczególności można wyróżnić: spotkanie informujące (briefing); spotkanie dotyczące planowania lub przeglądu; spotkanie mające na celu budowę zaufania lub tzw. team-building; spotkanie poświęcone podjęciu decyzji; spotkanie

poświęcone

tworzeniu

nowych

pomysłów,

podejść

i rozwiązań; spotkanie poświęcone planowaniu strategicznemu; spotkanie poświęcone rozwiązywaniu kryzysowych;

problemów

bądź

sytuacji

spotkanie mające na celu uzyskanie zaangażowania uczestników w określony projekt; spotkanie poświęcone celebrowaniu uroczystości (np. zakończenia projektu).

Każdy typ spotkania wymaga zebrania odpowiednich ludzi oraz innego podejścia w jego przeprowadzaniu. Rodzaj spotkania, w połączeniu z jego tematyką rzutuje na wybór określonych technik pracy grupowej. Jedno spotkanie może dotyczyć kilku spośród rzeczy wymienionych na powyższej liście. Na przykład, pierwszym punktem spotkania może być dokonanie briefingu, kolejnym – przegląd raportu o defektach, a ostatnim – sesja poświęcona tworzeniu nowych pomysłów dotyczących tego jak lepiej zapobiegać defektom. W każdej z tych części kierownik testów, który prowadzi spotkanie, może wykorzystywać odmienne techniki pracy zespołowej.

29.1. Proces podejmowania decyzji Większość spotkań ma na celu podjęcie jakiejś decyzji. Na rysunku 29.1 pokazano generyczny proces związany z tym zagadnieniem. Załóżmy, że naszym problemem jest niska efektywność usuwania defektów w fazie testowania. Możemy przeprowadzić sesję burzy mózgów lub użyć innej techniki, aby określić potencjalne rozwiązania tego problemu, na przykład: przeprowadzanie formalnych inspekcji, szkolenie w zakresie inżynierii wymagań, wykorzystanie zewnętrznych testerów, wykorzystanie formalnych technik projektowania testów. Następnie każde z tych rozwiązań jest oceniane pod kątem sensowności, kosztu i ewentualnych zysków, np. za pomocą technik szacowania. Priorytetyzując rozwiązania, możemy wybrać jedno lub kilka najefektywniejszych, a następnie stworzyć szczegółowy plan ich implementacji w organizacji. Na końcu powinniśmy określić, w jaki sposób możemy skontrolować, czy wprowadzone zmiany/rozwiązania przyniosły oczekiwane efekty. W naszym przykładzie najprościej wykorzystać używaną już przez nas metrykę efektywności usuwania defektów w fazie (warto również mierzyć koszt stosowania określonych rozwiązań).

Rysunek 29.1. Generyczny proces podejmowania decyzji

29.2. Techniki wspomagania kreatywności Techniki te pozwalają w krótkim czasie wygenerować wiele pomysłów. Aby wzmóc kreatywność zespołu, umożliwiają one jego członkom „puszczenie wodzów fantazji”. Dlatego często kolejnym krokiem jest redukcja liczby wymyślonych idei oraz zawężenie ich do pomysłów sensownych i możliwych do wdrożenia.

29.2.1. Burza mózgów Aby przeprowadzić sesję burzy mózgów, wszyscy uczestnicy spotkania muszą dobrze rozumieć cel spotkania. Kierownik bądź moderator przed spotkaniem powinien wysłać uczestnikom informację opisującą dokładnie, co będzie przedmiotem sesji. Na przykład: „celem burzy mózgów będzie wymyślenie możliwych ryzyk dla nowego projektu XYZ”. Na spotkaniu każdy uczestnik może zgłaszać swoje pomysły, które są zapisywane przez moderatora lub skrybę na kartce, w komputerze bądź na ścianie. Nie należy tą czynnością obciążać uczestników, ponieważ powinni oni być maksymalnie skupieni na powierzonym im zadaniu. Zapisywanie pomysłów w widocznym dla wszystkich miejscu oraz zupełna dowolność w wymyślaniu nawet najbardziej absurdalnych pomysłów zwiększa kreatywność innych uczestników sesji. Zasadą burzy mózgów jest to, że nie wolno krytykować cudzych pomysłów. Jedyna dopuszczalna czynność to generacja idei. Sesja burzy mózgów jest przeprowadzana do wyczerpania pomysłów lub do momentu zakończenia czasu na nią przeznaczonego. Na zakończenie uczestnicy mogą z powrotem wejść w swoje role analityczne oraz przedyskutować i spojrzeć krytycznie na wygenerowane przez siebie idee.

29.2.2. NGT (Nominal Group Technique) NGT jest techniką podobną do burzy mózgów, jednak bardziej zorganizowaną. Stanowi dobre rozwiązanie w sytuacji, gdy: w grupie istnieją osoby dominujące dyskusję; w grupie są nowi członkowie; temat dyskusji jest kontrowersyjny; członkowie zespołu nie mogą dojść do porozumienia. Metoda składa się z 7 kroków [77]: 1. Grupie jest przedstawiany cel spotkania oraz reguły NGT (jeśli nie są znane). 2. Grupie jest przedstawiany problem do rozwiązania. 3. Każdy członek zespołu wymyśla indywidualnie i w ciszy listę pomysłów, które w jego ocenie mogą przyczynić się do rozwiązania problemu. Pomysły mogą być zapisywane na małych karteczkach tak, aby jeden pomysł znajdował się na jednej kartce. Dzięki przeprowadzeniu tego etapu w ciszy, członkowie zespołu nie wpływają na siebie (jest to w pewnym sensie odwrotność metody burzy mógzów). 4. Pomysły przedstawiane są cyklicznie przez uczestników sesji, tzn. każdy uczestnik przedstawia jeden pomysł. Po przejściu przez taki cykl każdy uczestnik przedstawia kolejny pomysł itd., aż do wyczerpania pomysłów. Sekwencyjne przedstawianie idei pozwala na zaangażowanie wszystkich uczestników spotkania, co również skutkuje tym, że żadna osoba nie zdominuje sesji. Tak jak w burzy mózgów, podczas przedstawiania pomysłów nie powinno się ich dyskutować ani krytykować, ale mogą być one wykorzystane do stymulacji kreatywności i wymyślania nowych idei. Jeśli ktoś wpadł na ten sam pomysł, co my i przedstawił go pierwszy, to powinniśmy wykreślić go z naszej listy. 5. Po prezentacji wszystkich pomysłów moderator otwiera dyskusję, podczas której można również zadawać pytania oraz uszczegóławiać zgłoszone idee. 6. Uczestnicy powracają do swoich analitycznych ról. Pomysły są grupowane, upraszczane, usuwane lub łączone ze sobą.

7. Idee powstałe w kroku 6. są priorytetyzowane, np. przy wykorzystaniu techniki wielokrotnego głosowania (patrz p. 29.4.1).

29.2.3. Metoda analogii Metoda ta pozwala wymyślać idee przez stosowanie analogii. Na przykład: „gdyby nasz zespół testowy był organizmem biologicznym, a defekty – wirusami w tym organizmie, to problem ich usuwania można by rozwiązać w taki a taki sposób”. Zastosowanie analogii może zwiększyć u uczestników sesji kreatywność.

29.2.4. JAD (Joint Application Development) JAD

to

metoda

tworzenia

oprogramowania

przy

współudziale

klienta.

Wykorzystuje tzw. warsztaty współprojektowania (ang. JAD workshops), w których uczestniczą zarówno projektanci, jak i użytkownicy bądź klienci. Metoda została zdefiniowana w IBM w celu przyspieszenia procesu tworzenia aplikacji. Sesje JAD można w szczególności wykorzystać w procesie pozyskiwania wymagań (ang. requirements elicitation). Przez zebranie w jednym miejscu wszystkich interesariuszy, sesja JAD umożliwia szybką komunikację oraz równie szybkie rozwiązywanie powstałych w trakcie sesji problemów. Sesje JAD skupiają się na koncepcji współpracy oraz budowania zespołu, co wzmacnia u wszystkich uczestników poczucie współodpowiedzialności za dostarczany produkt.

29.3. Techniki porządkowania i priorytetyzacji 29.3.1. Diagram podobieństwa (affinity diagram) Diagram podobieństwa (ang. affinity diagram) stosuje się do porządkowania pomysłów powstałych np. podczas burzy mózgów czy Nominal Group Technique. Składa się z 3 kroków: 1. Wypisanie każdego pomysłu na osobnej kartce i umieszczenie ich na tablicy lub na podłodze tak, by każdy mógł do nich mieć swobodny dostęp. 2. Praca grupowa (w ciszy), polegająca na przemieszczaniu kartek, organizowaniu ich w grupy oraz usuwaniu dublujących się pomysłów. Podczas tego kroku można również dodawać nowe pomysły, np. jeśli

w wyniku zgrupowania kilku pomysłów w jedną kategorię okaże się, że ewidentnie czegoś w niej brakuje. Jeśli jeden pomysł pasuje do kilku kategorii, to można stworzyć jego kopie i umieścić je w odpowiednich grupach. 3. Gdy przemieszczanie kartek z kroku 2. się skończy, grupa przerywa milczenie i przeprowadza dyskusję nad pogrupowanymi ideami. Każdą grupę można opisać krótką, hasłową etykietą [226]. Na rysunku 29.2 jest pokazany przykładowy diagram podobieństwa. Grupa pracowała nad problemem: „jak można ulepszyć nasz proces testowy?”. Po lewej stronie znajdują się wszystkie wygenerowane przez grupę pomysły. Po prawej pomysły te są zorganizowane w 5 kategorii: „szkolenia i rozwój”, „benchmarking”, „stosowanie technik V&V”, „automatyzacja” oraz „klient”. Usunięto jedną z kartek dotyczących testów regresji (dublowanie się pomysłów), a kartkę „automatyzacja testów”, która opisuje dosyć ogólną technikę, zamieniono na „automatyzacja generowania testów”. Nastąpiło to w wyniku utworzenia grupy „automatyzacja” – uczestnicy zauważyli, że pojęcie to może odnosić się nie tylko do wykonywania, lecz także do projektowania przypadków testowych.

Rysunek 29.2. Diagram podobieństwa – przykład

29.3.2. Macierz i graf priorytetyzacji Macierz priorytetyzacji służy do szeregowania pomysłów według przyjętego kryterium. Przydatność techniki widać dobrze w sytuacji, gdy decyzja musi być podjęta na podstawie większej liczby kryteriów. W wierszach macierzy znajdują się pomysły (możliwe rozwiązania), w kolumnach – kryteria oceny. Każdemu kryterium można przypisać wagę. Najczęściej wagi sumują się do 1. Każdy pomysł oceniany jest pod kątem każdego kryterium według przyjętej skali ocen (np. od 1 do 5, gdzie 1 = zupełnie nieważne/nieistotne/bardzo trudne/bardzo drogie itp., 5 = bardzo ważne/bardzo łatwe/bardzo tanie itp.). Następnie dla każdego możliwego rozwiązania oblicza się sumę ważoną, dodając do siebie oceny kryteriów przemnożone przez odpowiednie wagi. Tak uzyskany wynik określa miejsce danego rozwiązania na liście priorytetowej.

Tabela 29.1. Macierz priorytetyzacji – przykład

Kryteria i wagi

Efek Łatwość Czasochłonność Kosztochłonność znajd Rozwiązanie implementacji (0,3) (0,1) defe (0,2) (0,4) TDD / JUnit

4

3

5

2

Ins pekcje modułów

5

1

2

3

MBT + narzędzie XYZ

2

5

1

4

W tabeli 29.1 przedstawiono przykładową macierz priorytetyzacji dla przeprowadzania testów jednostkowych. Ocenie podlegały 3

problemu

rozwiązania: wykorzystanie techniki TDD i darmowego programu JUnit, inspekcja modułów oraz testowanie oparte na modelu, z użyciem komercyjnego narzędzia XYZ. Brano pod uwagę 4 kryteria: łatwość implementacji danego rozwiązania, czasochłonność testowania, koszt przeprowadzenia testów oraz efektywność znajdowania defektów, z wagami odpowiednio 0,2, 0,3, 0,1 i 0,4. Implementacja jest najtrudniejsza w przypadku użycia komercyjnego narzędzia dla testowania opartego na modelu. Z kolei inspekcje zajmują najwięcej czasu. Zakup narzędzia komercyjnego jest najdroższy, ale za to pozwala ono najefektywniej znajdować defekty. Po podsumowaniu priorytet rozwiązań jest następujący: na pierwszym miejscu uplasowało się podejście oparte na modelu (3,6 punktów), następnie stosowanie TDD + JUnit (3 punkty), a na końcu – inspekcje modułów (2,7 punktów).

Rysunek 29.3. Graf priorytetyzacji – przykład W przypadku, gdy istnieją tylko dwa kryteria oceny, wyniki można przedstawić graficznie w postaci dwuwymiarowego grafu priorytetyzacji, tak jak na rysunku 29.3. Współrzędne każdego punktu reprezentującego pojedyncze rozwiązanie bądź pomysł odpowiadają wartościom obu kryteriów. Obszary grafu wyznaczone przerywanymi liniami pomagają w priorytetyzacji. Na przykład punkty 4 i 5,

leżące w prawym górnym rogu wykresu, mają najwyższy priorytet, punkty 3 i 6 – średni, punkt 1 – niski, a punkt 2 – zaniedbywalny.

29.3.3. Mapa myśli Mapa myśli (ang. mind map) to metoda organizacji idei, ułatwiająca naukę i zapamiętywanie przez użycie graficznej struktury wykorzystującej różne kształty i kolory [227]. Mapa myśli może być również produktem wynikowym innej techniki, na przykład burzy mózgów. Na rysunku 29.4 jest pokazana przykładowa mapa myśli dotycząca zarządzania ryzykiem w procesie testowym. Mapa ta została wygenerowana za pomocą darmowego https://bubbl.us/.

narzędzia

internetowego

dostępnego

pod

adresem

mapa myśli (ang. mind map) – diagram używany do przedstawienia słów, idei, zadań lub innych elementów związanych i układających się wokół centralnego słowa lub idei; wykorzystywany do generowania, wizualizacji, organizowania i klasyfikowania idei oraz jako środek wspomagający poznawanie, zapamiętywanie, organizację, rozwiązywanie problemów i podejmowanie decyzji

Rysunek 29.4. Mapa myśli – przykład

29.4. Techniki szacowania i oceny 29.4.1. Wielokrotne głosowanie Technika wielokrotnego głosowania (ang. multivoting) jest przydatna w sytuacji, gdy wiele osób ma dokonać oceny wielu rozwiązań. Każdy z uczestników otrzymuje określoną pulę punktów do dyspozycji, które może przydzielić według swojego uznania do poszczególnych rozwiązań, np. przydzielić całą pulę punktów jednemu rozwiązaniu lub rozdzielić punkty między kilka wariantów. Po dokonaniu oceny wyniki są sumowane i jest przeprowadzana dyskusja. Przykład zastosowania techniki wielokrotnego głosowania jest pokazany na rysunku 29.5. Cztery pomysły, oznaczone literami A, B, C i D, są oceniane przez trzy osoby. Każda z nich ma do dyspozycji trzy głosy. Osoba pierwsza oceniła wariant A na 1 punkt, a wariant C – na dwa punkty. Osoba druga całą pulę przeznaczyła na wariant A, natomiast osoba trzecia rozdzieliła swoje głosy między warianty B, C i D. Po podsumowaniu wariant A wygrał, uzyskując 4 punkty. W trakcie dyskusji warto również rozważyć wariant C, który uplasował się tuż za A, otrzymując tylko o jeden punkt mniej.

Rysunek 29.5. Wielokrotne głosowanie – przykład Odmianą techniki wielokrotnego głosowania jest tzw. metoda rangowania. Każdy z uczestników jest proszony o wybór i spriorytetyzowanie n najlepszych według niego rozwiązań, np. przez przypisanie im liczb od n (priorytet najwyższy) do 1 (priorytet najniższy). Po dokonaniu wyboru przez wszystkie osoby moderator podlicza punkty i przedstawia uczestnikom ostateczną listę z rozwiązaniami uszeregowanymi według sumy zdobytych punktów.

29.4.2. Metoda delficka i Wideband Delphi

Metoda delficka to iteracyjna metoda estymacji, w której bierze udział grupa ekspertów. Ma ona wiele zastosowań biznesowych, ale z punktu widzenia testowania przydaje się głównie w szacowaniu poziomu ryzyka oraz w estymacji testów (kosztochłonność, czasochłonność, pracochłonność, liczba przypadków testowych, liczba pozostałych defektów itp.). Metodę delficką stosuje się, gdy nie istnieje ugruntowana teoria lub dobrze zbadany model pozwalający na dokonanie wystarczająco precyzyjnej predykcji, ale w zamian mamy do dyspozycji ekspertów w danej dziedzinie [228]. Główna idea tej techniki opiera się na dwóch zasadach. Pierwsza to tzw. mądrość tłumu [229]. Mówi ona, że uśredniony wynik szacunków dużej liczby osób zwykle jest bliski rzeczywistej wartości. Druga zasada stanowi, że estymacje dokonywane przez dobrze ustrukturyzowaną grupę osób (ekspertów dziedzinowych) są dokładniejsze, niż te dokonywane przez losowo wybranych ludzi. W literaturze istnieje wiele odmian metody delfickiej. Tu opiszemy tylko jedną z możliwości. Proces estymacji składa się z następujących kroków: 1. Wybór ekspertów. 2. Przedstawienie ekspertom problemu do szacowania. 3. Zebranie estymat od ekspertów. Każdy dokonuje szacowania niezależnie od pozostałych uczestników. 4. Przedstawienie ekspertom zbiorczych wyników. 5. Powtórzenie kroków 3.–4. (przynajmniej raz). Przez powtarzanie kroków 3. i 4 liczymy na to, że wariancja (zróżnicowanie) ocen ekspertów zmniejszy się do akceptowalnego poziomu. Każdy ekspert, po dokonaniu szacowania, otrzymuje wyniki wszystkich pozostałych ekspertów i na tej podstawie może zweryfikować swoją estymację. Na rysunku 29.6 zilustrowano przykładowy przebieg sesji metody delfickiej.

Rysunek 29.6. Metoda delficka – przykład W sesji brało udział 6 ekspertów. Mieli oni za zadanie oszacować pracochłonność testowania pewnego modułu (w osobotygodniach). Każdy znak X oznacza estymację jednego eksperta. W pierwszej rundzie rozrzut wyników oscylował między 5 a 42 tygodni. W rundzie drugiej każdy ekspert otrzymał zbiorcze wyniki z rundy pierwszej i na tej podstawie mógł zmodyfikować swoje szacunki. Jak widać, w każdej kolejnej rundzie rozrzut przewidywanych wartości zmniejszał się, aby w rundzie czwartej przyjąć postać przedziału od 31 do 42. Ten interwał jest już na tyle mały, że moderator zdecydował o zakończeniu sesji. Ostateczny wynik to 36,5 – środek przedziału, choć można również wziąć inną miarę statystyczną, np. średnią lub medianę z wyników ostatniej rundy (mediana jest lepszym rozwiązaniem, ponieważ w odróżnieniu od średniej nie jest podatna na wartości skrajne). Zasadą metody delfickiej jest anonimowość ekspertów. Żaden z nich nie powinien wiedzieć, kim są pozostali oceniający. Dzięki temu unika się uprzedzeń wśród ekspertów. Estymacje wpisuje się na specjalnych arkuszach (można również wykorzystać do tego celu różne programy komputerowe) z możliwością dodania komentarzy uzasadniających szacowanie, a także odnoszących się do

ocen pozostałych ekspertów z poprzedniej rundy. Moderator, rozsyłając wyniki danej rundy do wszystkich uczestników może filtrować komentarze, usuwając z nich informacje nieistotne lub mogące ujawnić tożsamość eksperta. Ten rodzaj komunikacji (przez moderatora) jest kolejną podstawową zasadą metody, zwaną ustrukturyzowanym przepływem informacji. Trzecia fundamentalna zasada to występowanie informacji zwrotnej – eksperci po dokonaniu szacunków otrzymują wyniki wszystkich pozostałych uczestników. Metoda Wideband Delphi, szerokopasmowa technika delficka, różni się od swojej poprzedniczki większą interakcją oraz komunikacją między ekspertami. Proces przeprowadzania estymacji techniką Wideband Delphi jest pokazany na rysunku 29.7.

Rysunek 29.7. Proces metody Wideband Delphi Uważa się, że osiąganie konsensusu za pomocą spotkań i dyskusji, tak jak to ma miejsce w metodzie Wideband Delphi, daje lepsze szacunki niż estymacje indywidualne, bez interakcji ekspertów. Więcej informacji na temat metody delfickiej Czytelnik znajdzie np. w książkach [230] i [231]. metoda Wideband Delphi, szerokopasmowa technika delficka (ang. Wideband Delphi) – bazująca na wiedzy eksperckiej technika estymacji pracochłonności,

opierająca

się na

zbiorowej wiedzy

członków zespołu, polegająca

na

dokładnym szacowaniu

29.4.3. Poker planistyczny (planning poker) Poker planistyczny to technika osiągania konsensusu służąca do estymacji (głównie pracochłonności). Najczęściej kojarzy się z metodykami zwinnymi wytwarzania oprogramowania, gdzie zwykle używa się jej do szacowania złożoności historyjek użytkownika. W pokerze planistycznym uczestnicy siedzą przy jednym stole, tak jak w zwykłym pokerze. Każdy z nich dysponuje specjalną talią kart, z której wybiera jedną kartę i kładzie ją przed sobą, wartością do dołu, tak, aby nikt inny jej nie widział. Pozwala to uniknąć tzw. kognitywnego biasu (tzw. anchoring) polegającego na tym, że ludzie mogą podjąć inną decyzję, ulegając wpływowi informacji z zewnątrz. poker planistyczny (ang. planning poker) – technika szacowania oparta na kompromisie, zwykle używana do oszacowania nakładu pracy lub relatywnej wielkości historyjek użytkownika, wykorzystywana na ogół w zwinnym tworzeniu oprogramowania; jest modyfikacją metody Wideband Delphi, wykorzystującą karty z wartościami reprezentującymi jednostki, w których zespół dokonuje szacowania Na znak moderatora wybrane karty są odsłaniane i następuje dyskusja dotycząca wybranych estymat. Talia kart do pokera planistycznego składa się zwykle z wartości pokazanych na rysunku 29.8. Wartości 1, 2, 3, 5, 8 i 13 to początkowe wartości ciągu

Rysunek 29.8. Poker planistyczny w formie aplikacji na smartfona

Fibonacciego, w którym każda kolejna wartość jest sumą dwóch poprzednich. Tak zdefiniowana skala umożliwia lepsze szacowanie przez porównanie złożoności danego problemu do złożoności innych, znanych problemów (np.: problem X ma złożoność podobną do sumy złożoności problemów Y i Z). Wartości 20, 40 i 100 nie pochodzą z ciągu Fibonacciego. Są one przeznaczone do opisywania bardzo dużych złożoności, gdzie odwoływanie się do innych, prostszych problemów w celu dokonania estymacji nie jest już pomocne. Wartości 0 i 1/2 oznaczają, że dany problem jest już tak naprawdę rozwiązany lub wymaga minimalnego nakładu pracy. Wystawienie karty z symbolem nieskończoności oznacza, że dany uczestnik uważa wykonanie zadania za nieosiągalne. Karta z pytajnikiem jest używana, gdy uczestnik zupełnie nie ma pojęcia, jak dokonać szacowania. Karta z kawą oznacza, że uczestnik jest już zmęczony, chce przerwać sesję i odpocząć (np. pijąc kawę). Karty pokazane na rysunku 29.8 pochodzą z aplikacji do pokera planistycznego na smartfona autorstwa firmy Power Symbol Technology LLC. Zamiast kart można bowiem wykorzystywać w tej grze telefony komórkowe.

29.4.4. Model Saaty’ego (Analytic Hierarchy Process) Metoda AHP (Analytic Hierarchy Process) pomaga w podejmowaniu wielokryterialnych decyzji. Według Saaty’ego [232] najbardziej kreatywnym zadaniem podczas tego procesu jest wybór czynników istotnych dla dokonania wyboru. W metodzie AHP czynniki te, gdy są już wybrane, zostają ułożone w hierarchiczną strukturę złożoną z celu, kryteriów, podkryteriów oraz alternatyw (wyborów). Każdy wybór zależy od zestawu kryteriów. Dla każdego wyboru kryteria są porównywane parami tak, aby określić preferencje decydenta. Używa się przy tym następującej skali liczbowej: 9 punktów, gdy element A jest ekstremalnie preferowany względem B; 7 punktów, gdy element A jest silnie preferowany względem B; 5 punktów, gdy element A jest preferowany względem B; 3 punkty, gdy element A jest słabo preferowany względem B; 1 punkt, gdy element A jest równoważny z B, co do preferencji. Dopuszcza się także wartości pośrednie oraz odwrotne (np. 1/9, gdy B jest ekstremalnie preferowany względem A). Na podstawie określonych ocen na

każdym poziomie hierarchii tworzone są tzw. macierze preferencji, w których na przekątnej zawsze są jedynki oraz zachodzi własność a ij = 1/a ji, gdzie a ij to element macierzy w i-tym wierszu i j-tej kolumnie. Ranking końcowy powstaje na

podstawie obliczenia dla każdej alternatywy wartości tzw. zagregowanej funkcji użyteczności. Jest to suma bezwzględnych wag alternatywy oraz wag kryteriów. Wagi każdej macierzy oblicza się przez wyznaczenie ich znormalizowanych, głównych wektorów własnych1, nazywanych w AHP wektorami priorytetów. Przykład. Rozważmy

następujący

cel: wybór techniki

analizy

ryzyka.

Definiujemy trzy możliwe alternatywy: A1) analiza oparta na istniejących listach kontrolnych; A2) analiza oparta na metodzie PRisMa; A3) analiza przy użyciu FMEA. Rozważamy cztery kryteria: K1) łatwość stosowania metody; K2) dokładność w identyfikacji ryzyk; K3) czas poświęcony na analizę; K4) pracochłonność metody. Struktura tego problemu decyzyjnego w ujęciu AHP jest przedstawiona na rysunku 29.9.

Rysunek 29.9. AHP – struktura przykładowego problemu decyzyjnego

Mamy dwa poziomy hierarchii, dla których obliczamy macierze preferencji: poziom kryteriów i poziom alternatyw. Macierz dla poziomu kryteriów może wyglądać następująco:

K1

K2

K3

K4

K1

1

1/5

1/2

1

K2

5

1

1/3

2

K3

2

3

1

3

K4

1

1/2

1/3

1

Na przykład dokładność w identyfikacji ryzyk (K2) jest silnie preferowana względem łatwości stosowania metody, dlatego w pozycji (K2, K1) macierzy występuje liczba 5 (silna preferencja), a w polu (K1, K2) do niego symetrycznym – odwrotność tej wartości, czyli 1/5. Główny wektor własny tej macierzy to (0,214, 0,526, 0,792, 0,220), co po znormalizowaniu daje wektor priorytetów vk = (0,122, 0,3,

0,452, 0,126). Następnie, dokonujemy porównania parami elementów na najniższym poziomie (czyli alternatyw) ze względu na cztery zdefiniowane kryteria. Będziemy więc mieć 4 macierze 3 × 3. K1 – łatwość stosowania metody:

K2 – dokładność identyfikacji ryzyk:

K3 – czasochłonność:

K4 – pracochłonność:

Mając obliczone macierze, możemy obliczyć lokalne i globalne priorytety dla każdego możliwego wariantu (alternatywy rozwiązania), mnożąc wartości wektorów priorytetów odpowiadające poszczególnym rozwiązaniom przez wagi wektora priorytetów dla kryteriów i sumuje je

K1 (0,122) K2 (0,3) K3 (0,452) K4 (0,126) Wynik A1: lis ty kontrolne

0,740

0,109

0,752

0,637

0,543

A2: PRis Ma

0,093

0,309

0,070

0,105

0,149

A3: FMEA

0,167

0,582

0,178

0,258

0,308

Analiza wykazała, że biorąc pod uwagę 4 kryteria K1–K4 oraz nasze preferencje co do kryteriów oraz alternatyw, wybór A1 – listy kontrolne – jest najlepszy.

29.5. Definiowanie problemu, przyczyny źródłowej lub możliwości 29.5.1. Analiza pola sił (force field analysis) W Analizie pola sił zespół identyfikuje czynniki, które sprzyjają osiągnięciu założonego celu, oraz te, które nas od celu oddalają. Na rysunku 29.10 jest kompletna analiza dla procesu implementacji zmiany,

przedstawiona

zaczerpnięta z [77].

Rysunek 29.10. Analiza pola sił dla problemu zmiany w organizacji Mając przeprowadzoną analizę, możemy zaplanować czynności, które będą wzmacniały siły wspierające osiągnięcie celu oraz czynności zapobiegające lub łagodzące oddziaływanie sił „negatywnych”, oddalających nas od niego.

29.5.2. Immersja Technika immersji polega na przeprowadzeniu sesji w środowisku silnie stymulującym kreatywność. Zespół może dzielić się na małe grupy, ale równie dobrze poszczególne osoby mogą pracować samodzielnie. Pomieszczenie, w którym przeprowadza się sesję, powinno być wyposażone w sprzęt

i przedmioty wspomagające myślenie i kreatywność (książki, plakaty, tablice, arkusze papieru, przybory do rysowania, a nawet odpowiednie zabawki). Grupa przez określony czas przebywa w takim pomieszczeniu, tworząc przy użyciu wyżej wspomnianych narzędzi pomysły i idee. Poszczególne osoby lub grupy w ramach zespołu dostarczają raporty z wykonanego zadania. Następuje dyskusja, w której zespół osiąga konsensus w sprawie kierunku dalszych działań, po czym jest proszony o powtórzenie zadania, mając na uwadze obrany kierunek. Konsensus w sprawie definicji problemu może być osiągnięty tylko po całkowitym „zanurzeniu się” członków zespołu w myślenie o problemie i spojrzeniu nań z wielu perspektyw.

29.5.3. Diagram rybiej ości (diagram Ishikawy) Diagram rybiej ości (ang. fishbone diagram), zwany również diagramem Ishikawy2 lub diagramem przyczynowo-skutkowym, jest jednym z narzędzi analizy jakości, ale opisujemy go w tym rozdziale, gdyż zwykle jest tworzony przez grupę osób. Proces jego tworzenia wykorzystuje najczęściej techniki wspomagające kreatywność, np. burzę mózgów. Diagram ten obrazuje zależności przyczynowo-skutkowe i dzięki temu może być użyty do analizy przyczyny źródłowej (ang. root cause analysis), co pozwala na usuwanie rzeczywistych przyczyn, a nie tylko skutków (objawów) występujacych problemów. diagram Ishikawy, diagram przyczynowo-skutkowy, diagram rybiej ości (ang. Ishikawa diagram, cause-effect diagram, fishbone diagram) – model służący do organizacji i graficznego przedstawienia związków między różnymi możliwymi przyczynami problemu; możliwe przyczyny rzeczywistego lub potencjalnego defektu lub awarii są porządkowane w kategorie i podkategorie poziomej struktury drzewiastej z (potencjalnym) defektem lub awarią jako korzeniem drzewa [202] Nazwa „diagram rybiej ości” bierze się z jego kształtu, przypominającego szkielet ryby. Problem do rozwiązania umieszcza się w głowie „ryby”. Do centralnej osi szkieletu dochodzą główne ości, reprezentujące czynniki bezpośrednio wpływające na problem. Z kolei do nich mogą dochodzić inne, mniejsze, opisujące powody występowania tych czynników itd. Budując w ten

sposób diagram, otrzymujemy szczegółowy opis podstawowych przyczyn, które składają się na wystąpienie analizowanego problemu.

Rysunek 29.11. Diagram rybiej ości (Ishikawy) – przykład Przykładowy diagram rybiej ości jest pokazany na rysunku 29.11. Dotyczy on problemu wysokiej gęstości defektów w kodzie. W wyniku przeprowadzonej sesji burzy mózgów ustalono, że głównymi powodami tego stanu rzeczy są: złe zarządzanie, kłopoty ze środowiskiem testowym, stosowanie nieadekwatnych metod oraz problemy z zasobami ludzkimi; na kłopoty ze środowiskiem wpływa brak narzędzi do analizy kodu oraz niewygodny system do zarządzania incydentami itd. Każda ość diagramu jest analizowana pod kątem jej przyczyn. Przyczyny te są dodawane, dopóki nie będziemy w stanie znaleźć bezpośrednich powodów ich występowania. Gdy nam się to uda, oznacza to, że zidentyfikowaliśmy przyczynę źródłową głównego problemu. Likwidując ją, usprawniamy cały proces, ponieważ nie usuwamy objawów, lecz rzeczywiste powody problemu.

1 Wektor własny macierzy kwadratowej M to niezerowy wektor v taki, że Mv = λv dla pewnej wartości liczbowej λ, zwanej wartością własną. Główny wektor własny to wektor własny dla największej wartości własnej. Wektor znormalizowany to wektor podzielony przez sumę swoich wartości, np. dla wektora (1, 2, 3) po normalizacji otrzymamy

. Suma wartości elementów

znormalizowanego wektora wynosi 1. 2 Nazwa techniki pochodzi od nazwiska jej twórcy, dra Kaoru Ishikawy.

30. Testowanie wspierane narzędziami

Narzędzia mogą być użyte w wielu obszarach procesu testowego. Istnieją dwa główne typy narzędzi: wspomagające pracę ludzi oraz służące do automatyzacji. Wspomaganie może dotyczyć: wspierania zarządzania procesem testowym; zarządzania incydentami, konfiguracją, wersjonowaniem; wspierania w projektowaniu testów. Natomiast narzędzia służące do automatyzacji można wykorzystać do: wykonywania testów przez uruchamianie skryptów testowych; wykonywania testów niemożliwych do wykonania ręcznego (np. testy wydajności); automatycznego projektowania testów; pomiaru, analizy oraz raportowania metryk (np. dotyczących pokrycia, liczby znalezionych defektów, efektywności usuwania defektów); automatycznej generacji danych testowych; porównywania wyników rzeczywistych z oczekiwanymi; przeprowadzania analizy statycznej i dynamicznej itd. narzędzie testowe (ang. testing tool) – oprogramowanie wspomagające jedną lub kilka czynności testowych, takich jak planowanie, zarządzanie, specyfikacja, budowanie początkowych plików i danych, wykonanie i analiza testów [117]

Od mniej więcej początku XXI wieku w branży testerskiej coraz częściej mówi się o automatyzacji testów. „Automatyzacja” stała się słowem-kluczem. Większość ogłoszeń o pracę dla testerów wymaga znajomości jakichś narzędzi lub języków skryptowych, a wielu pracodawców poszukuje wręcz ludzi na stanowiska typu „specjalista ds. automatyzacji testów”. Każdy, kto chce być dobrym testerem, musi zatem znać przynajmniej podstawowe koncepcje związane z użyciem narzędzi w procesie testowym. Jest to dziedzina bardzo szeroka, ale i zwodnicza – wielu menedżerów sądzi, że automatyzacja procesu testowego rozwiąże wszystkie problemy z testowaniem: obniży koszty, pozwoli zredukować liczbę pracowników, zwiększy efektywność wykrywania awarii czy usterek. Niestety, bardzo często po wdrożeniu okazuje się, że: koszt utrzymania narzędzia jest większy, niż myślano; narzędzie to – jak na ironię – obsługiwać musi człowiek, więc nie bardzo jest kogo zwolnić; wręcz przeciwnie – czasami trzeba zatrudnić nową osobę do obsługi narzędzia; pojawiają się problemy z wdrożeniem czy obsługą opóźniające prace zespołu; efektywność znajdowania defektów nie jest taka, jak by sobie kierownictwo życzyło. Oczywiście nie jest tak, że wdrożenie narzędzi musi wiązać się z samymi kłopotami. Sekret polega na tym, że musi być ono przemyślane i poprzedzone dokładnymi analizami dotyczącymi opłacalności oraz efektywności (tzw. business case for automation), a często także wdrożeniem pilotażowym. Należy również zawczasu zadbać o odpowiednie szkolenia związane z obsługą narzędzia lub zatrudnienie odpowiednich specjalistów. Zwykle korzyści z wykorzystania narzędzi w organizacji są widoczne dopiero w perspektywie długookresowej. Ponadto, nie każdy proces związany z testowaniem poddaje się automatyzacji. Nierzadko okazuje się, że pewne czynności opłaca się nadal przeprowadzać ręcznie, wcale ich nie automatyzując. Podrozdział 30.1 stanowi wprowadzenie w zagadnienie wykorzystania narzędzi w organizacji testowej. Mówiąc o automatyzacji, zwykle ma się na myśli automatyzację wykonywania testów, jednak nie jest to jedyna część procesu testowego, którą można w ten sposób usprawnić. W podrozdziale 30.2 opiszemy różne poziomy automatyzacji. Podrozdział 30.6 zawiera klasyfikację narzędzi

testowych wraz z konkretnymi aplikacjami dostępnymi na rynku, zarówno komercyjnymi, jak i open-source. W podrozdziale 30.7 omówimy kwestie dotyczące wdrażania narzędzi w organizacji.

30.1. Podstawowe zagadnienia związane z użyciem narzędzi Aby osiągnąć maksymalną korzyść w związku z użyciem narzędzi, nie można ograniczać się wyłącznie do wyboru narzędzia i jego wdrożenia. Potrzebne jest również ciągłe zarządzanie, obejmujące – oprócz wymienionych wcześniej aktywności – planowanie, wdrożenie pilotażowe, nadzór nad wykorzystaniem czy utrzymanie. Narzędzia pozwalają generować i wykonywać o wiele więcej przypadków testowych niż podczas wykonywania ręcznego. Dlatego narzędzia, skrypty oraz dane testowe powinny być objęte zarządzaniem konfiguracją. Podstawową kwestią jest przygotowanie odpowiedniego środowiska testowego oraz właściwej architektury systemu dla automatyzacji testów. Rzeczywiste wyniki testów muszą być porównywane z wynikami oczekiwanymi – dobrze jest zautomatyzować również i tę czynność, choć zwykle jest to bardzo problematyczne. Pojawia się tu tzw. problem wyroczni i, o ile nie wykorzystujemy testowania opartego na modelu (które oprócz stworzenia samego testu może podać również oczekiwaną odpowiedź) ani nie mamy dostępu do komparatora, jest wymagana manualna praca testera polegająca na określaniu oczekiwanych wyników. Przed wdrożeniem jakiegokolwiek narzędzia należy przeprowadzić analizę biznesową opłacalności tego kroku (tzw. business case). Analiza powinna obejmować trzy zasadnicze aspekty: koszty, które poniesiemy w związku z wdrożeniem i używaniem narzędzia; ryzyka związane z automatyzacją testów za pomocą narzędzia; korzyści, jakie uzyskamy w związku z wykorzystaniem narzędzia.

Rysunek 30.1. Automatyzacja vs testowanie ręczne – koszty

30.1.1. Wybór odpowiedniego narzędzia Podczas

wybierania

odpowiedniego

narzędzia

należy

wziąć

pod

uwagę

następujące aspekty: dojrzałość organizacji – w firmie, która nie ma zdefiniowanych procesów, jasnego podziału ról, kompetencji i odpowiedzialności, program wdrożeniowy z dużym prawdopodobieństwem zakończy się porażką; wybrane narzędzie może okazać się nieodpowiednie, jego wdrożenie niemożliwe lub czasochłonne, a koszt jego utrzymania o wiele wyższy niż planowany (o ile w ogóle takie plany były tworzone); właściwa ocena – aby wybrać narzędzie najlepiej dopasowane do naszych potrzeb, powinniśmy zdefiniować jasne, czytelne i łatwo weryfikowalne kryteria oceny oraz zastosować je do wszystkich rozważanych narzędzi; dowód słuszności pomysłu (ang. proof-of-concept) – polega na wykonaniu pilotażowego projektu z wykorzystaniem określonego narzędzia, aby wykazać jego rzeczywistą przydatność w naszym środowisku produkcyjnym; projekt pilotażowy umożliwia ponadto

zapoznanie się z narzędziem oraz ustalenie standardów jego użycia dla całej organizacji, jeśli zdecydujemy się na wdrożenie; ocena dostawcy – ewaluacja dostawcy/producenta narzędzia jest tak samo ważna jak ocena samego narzędzia; należy ocenić poziom dojrzałości organizacji dostarczającej lub produkującej narzędzie, w szczególności pod kątem jakości tworzonych produktów oraz jakości wsparcia technicznego po ewentualnym zakupie; identyfikacja potrzeb szkoleniowych oraz doradczych – jakie szkolenia i dla kogo będą niezbędne w związku z wdrożeniem danego narzędzia? czy organizacja będzie potrzebowała doradztwa w zakresie wdrożenia bądź używania narzędzia?; oszacowanie ROI – czyli obliczenie stosunku potencjalnych zysków do potencjalnych kosztów związanych z zakupem danego narzędzia. Analizując wybór odpowiedniego narzędzia, należy również rozważyć możliwość stworzenia go samodzielnie lub zlecić jego wykonanie na potrzeby naszej organizacji. Zaletą tego rozwiązania jest idealne dostosowanie narzędzia do specyfiki organizacji. Wadą – często wysoki koszt oraz czas jego tworzenia. własne narzędzie (ang. custom tool) – narzędzie software’owe wytworzone specjalnie dla grupy użytkowników lub klientów

30.1.2. Koszty wdrożenia narzędzia Analizując koszty, należy mieć na uwadze, że zwykle zysk z zainwestowania w narzędzie zwróci się dopiero po dłuższym czasie. Często koszty automatyzacji w pierwszych tygodniach czy miesiącach przewyższają koszty testowania ręcznego. Schematycznie pokazano to na rysunku 30.1. Nieudane wdrożenie może skutkować znacznym wzrostem kosztów, a w skrajnych przypadkach – nawet przekroczeniem kosztów testowania ręcznego. Koszty możemy podzielić na dwie główne kategorie: koszty początkowe, czyli te, które ponosimy tylko raz, oraz koszty powracające, związane z ciągłym utrzymaniem narzędzia. Przykładowe koszty wpadające w obie te kategorie są pokazane na rysunku 30.2. Niektóre koszty można starać się minimalizować. Na przykład redukcję kosztów szkoleń dla nowych członków zespołu można uzyskać, wymagając od

kandydatów do pracy znajomości danego narzędzia. Minimalizacja kosztów może również zwiększać ryzyko związane z używaniem narzędzi. Na przykład niewykupienie wsparcia technicznego może mieć poważne konsekwencje, jeśli narzędzie ulegnie poważnej awarii i nikt w zespole nie ma pojęcia, jak ją naprawić. Bardzo istotny jest koszt utrzymania skryptów testowych. Ujawnia się on w momencie, gdy niezbędna jest ich modyfikacja, ponieważ zajmuje to czas testerów bądź deweloperów. Dlatego podczas tworzenia całej architektury dla systemu automatyzacji testów warto zaprojektować go tak, aby skrypty musiały być modyfikowane tak rzadko, jak to tylko możliwe.

Rysunek 30.2. Koszty związane z wdrożeniem narzędzia

30.1.3. Ryzyka związane z wdrożeniem narzędzia Ryzyka związane z używaniem narzędzi możemy podzielić na trzy kategorie: ryzyka związane i szacunkami;

z

błędnymi

ryzyka związane z samym i wykonywaniem testów;

lub

niedokładnymi

procesem

testowym,

analizami tworzeniem

ryzyka zewnętrzne, związane z dostawcą narzędzia. Przykłady tych ryzyk, zaczerpnięte z [13] oraz [173] są pokazane na rysunku 30.3. Wszystkie tego typu ryzyka podlegają zarządzaniu, a więc w szczególności, po ich identyfikacji oraz analizie należy je łagodzić. Na przykład: nierealistyczne oczekiwania są typowe w organizacjach niemających wcześniej doświadczenia w podobnych projektach dotyczących automatyzacji. Ludzie oczekują, że samo wdrożenie narzędzia w magiczny sposób rozwiąże wszystkie dotychczasowe problemy. Dlatego przed wdrożeniem należy upewnić się, że pracownicy dobrze rozumieją rolę narzędzia w procesie oraz zakres jego działania. Zbytnie poleganie na narzędziu objawia się najczęściej tym, że ludzie starają się automatyzować wszystkie możliwe testy, również te nieautomatyzowalne. W analizie przedwdrożeniowej należy bardzo wyraźnie zaznaczyć, które testy będą, a które nie będą podlegać automatyzacji.

Rysunek 30.3. Ryzyka związane z wykorzystaniem narzędzi w testowaniu

30.1.4. Korzyści z wdrożenia narzędzia Doszliśmy wreszcie do najprzyjemniejszej części analizy – w końcu po to ponosimy koszty i przyjmujemy na siebie ryzyko, żeby w rezultacie osiągnąć korzyści z użycia narzędzia. Sylabus ISTQB [13] wymienia następujące potencjalne korzyści z wykorzystania narzędzi: redukcja powtarzającej się pracy (np. wykonywanie testów regresji, powtórne wprowadzanie tych samych danych testowych, sprawdzanie

zgodności ze standardami kodowania, montowanie i rozmontowanie środowiska testowego); zwiększenie spójności i powtarzalności (np. testy są wykonywane przez narzędzie w tej samej kolejności, ryzyko popełnienia błędu jest o wiele mniejsze niż w przypadku wykonania ręcznego); obiektywna ocena rezultatów (np. miary poprawności wyników);

pokrycia, weryfikacja

łatwiejszy dostęp do danych o testach i testowaniu (statystyki i raporty dotyczące wykonania, postępu, wydajności, efektywności itp.). Jeszcze jedną korzyścią z automatyzacji jest możliwość wykonywania testów, których do tej pory nie byliśmy w stanie wykonać (np. testy wydajności). Aby zmaksymalizować korzyści, mądre zespoły testowe inwestują duże nakłady czasu i pieniędzy w dobre zaprojektowanie automatycznych przypadków testowych, danych testowych, frameworków oraz innych artefaktów wspomagających automatyzację lub przez nią wymaganych. Zysk z tych działań polega na osiągnięciu bardzo niskich kosztów utrzymania testaliów i całego środowiska testowego. Na przykład, uważne projektowanie skryptów/testów dla GUI tak, aby logika testu była odseparowana od danych wejściowych oraz elementów samego GUI daje – w przypadku zmiany tego GUI lub układu jego elementów – zerowy lub bliski zeru koszt utrzymania testu. Złe, nieuważne projektowanie testów niskiej jakości, bezpośrednio zależnych od konkretnych danych i elementów GUI skutkuje w takich samych okolicznościach stratami spowodowanymi wysokim nakładem czasu pracy potrzebnego na modyfikację skryptów.

30.1.5. Strategie wdrażania automatyzacji Wspomnieliśmy już wcześniej, że automatyzacja testowania powinna być przeprowadzona mądrze. Oto kilka dobrych praktyk związanych z automatyzacją i wdrażaniem narzędzi: automatyzuj, mając na uwadze perspektywę długookresową (tzn. myśl strategicznie, a nie taktycznie); od samego początku dbaj o dobrą utrzymywalność narzędzia oraz testaliów (skryptów, danych testowych itp.);

automatyzuj tylko to, co da się zautomatyzować (tzn. te testy, których wykonanie może się odbyć bez jakiejkolwiek ingerencji człowieka); automatyzuj testy i czynności, które są podatne na błędy, gdy wykonywane przez ludzi (np. testy silnie oparte na składni danych lub skomplikowanej logice); nie automatyzuj testów, o których z góry wiadomo, że będą wykonane najwyżej kilka razy – takie testy szybciej wykona się ręcznie; szukaj najprostszych i najefektywniejszych metod automatyzacji (tzw. quick-win).

30.1.6. Integracja i wymiana informacji między narzędziami W organizacji może istnieć wiele różnych narzędzi wspierających proces testowy: narzędzie do zarządzania wymaganiami, do zarządzania testowaniem, do wykonywania testów regresji, do analizy pokrycia, do zarządzania incydentami, do raportowania itd. Ważne jest, aby narzędzia te potrafiły ze sobą współpracować. Jeśli decydujemy się na zakup nowego narzędzia, to powinniśmy zadbać o to, aby potrafiło komunikować się z pozostałymi. Na przykład, narzędzie do wykonywania testów powinno mieć możliwość ustanowienia obustronnego śledzenia między testami a wymaganiami. Te ostatnie powinno być w stanie uzyskać od narzędzia zarządzającego wymaganiami. Czasami zachodzi potrzeba, aby taką współpracę zapewnić samodzielnie, jeśli nie jest wspierana przez oryginalne aplikacje. Wtedy należy jednak rozważyć opłacalność projektu integracji narzędzi, gdyż może się okazać, że integrowanie narzędzi własnymi środkami może być zbyt kosztowne. Obecnie na rynku dostępnych jest coraz więcej zestawów (suit) programów od jednego producenta, które potrafią ze sobą dobrze współpracować. Jeśli tworzymy nasze środowisko testowe od zera, to warto zainwestować w tego typu rozwiązania. Jeśli jednak środowisko istnieje od dawna i nagle pojawia się potrzeba dodania nowego, pojedynczego narzędzia, to sprawa się nieco komplikuje. W takim przypadku zawsze należy rozważyć zakup tego spośród dostępnych narzędzi, które umożliwia najlepszą integrację z pozostałymi aplikacjami wykorzystywanymi w procesie testowym.

30.1.7. Klasyfikacja narzędzi testowych

Narzędzia testowe można sklasyfikować według różnych kryteriów [79]: według poziomu akceptacyjne);

testów

(jednostkowe,

integracyjne,

systemowe,

według rodzaju awarii, które rozpoznają; według podejścia testowania;

do

testowania

lub

wykorzystywanej techniki

według celów (pomiary, sterowniki, logowanie, porównywanie); według dziedziny (symulacja ruchu, sieci, protokoły, transakcje, ekrany TV, automatyka pojazdów itd.); według wspieranego obszaru testowania (wprowadzanie danych, środowisko, konfiguracja itp.); według sposobu wdrożenia (oprogramowanie z półki, platformy do adaptacji, wtyczki itp.); według sposobu komercyjne itp.).

licencjonowania

(open-source,

na

licencji

GPL,

Z praktycznego punktu widzenia najbardziej użyteczna jest jednak klasyfikacja ze względu na kategorię narzędzia. Oto typowe kategorie: narzędzia do modelowania (ang. modeling tools) – pozwalają na modelowanie testowanego systemu oraz walidację tego modelu [60]; część z nich umożliwia również tworzenie przypadków i/lub procedur testowych na podstawie gotowego modelu; narzędzia do projektowania testów (ang. test design tools) – wspierają projektowanie testu generując wejścia ze specyfikacji (przechowywanej np. w repozytorium systemu typu CASE jak narzędzie zarządzania wymaganiami), z wyspecyfikowanych warunków testu przechowywanych w samym narzędziu lub z kodu; pomagają decydować, jakie testy powinny być stworzone i wykonane; można do nich zaliczyć narzędzia do generacji danych testowych; narzędzia do wykonywania testów (ang. test execution tools) – głównie skrypty testowe (ang. test scripts), symulatory, emulatory, uprzęże testowe, narzędzia nagrywająco-odtwarzające (zwane też narzędziami rejestrująco-odtwarzającymi lub narzędziami typu capture & replay) oraz środowiska pozwalające na automatyczne uruchamianie

i wykonywanie testów; można tu wyróżnić podkategorię narzędzi umożliwiających wykonywanie testów GUI, a także do przeprowadzania testów bezpieczeństwa, obciążeniowych i wydajnościowych; narzędzia do zarządzania testowaniem – zaliczają się do nich narzędzia do zarządzania konfiguracją (ang. configuration management tools), wymaganiami (ang. requirements management tools), incydentami (ang. incident management tools), defektami (ang. defect management tools), testami (ang. test management tools) czy wersjami (ang. version control tools); narzędzia te pozwalają śledzić powiązania między artefaktami testowymi, statusy i zmiany, zbierać metryki, generować raporty itp.; w ogólności wspierają czynności zarządcze występujące w procesie testowym; narzędzia mierzące pokrycie (ang. coverage tools) – dostarczają obiektywne miary tego, które elementy strukturalne testowanego systemu zostały sprawdzone przez zestaw testów; narzędzia do posiewu usterek (ang. fault seeding tools) – narzędzie do celowego wprowadzania usterek w modelu lub systemie; przydatne np. w testowaniu i analizie mutacyjnej; narzędzia monitorujące, monitory (ang. monitoring tools, monitors) – narzędzia programistyczne lub sprzętowe, działające równolegle z testowanym modułem lub systemem, nadzorujące, rejestrujące i analizujące jego zachowanie; uprzęże testowe dla testów jednostkowych (ang. unit test harnesses) – wspomagają testy modułowe przez symulację środowiska, w którym działa testowany komponent, w szczególności dostarczając sterowniki i zaślepki testowe; komparatory (ang. comparators) – pozwalające na rzeczywistych i oczekiwanych wyników testów;

porównanie

narzędzia do przygotowywania danych testowych; symulatory i emulatory; narzędzia wspierające przeglądy i inspekcje (ang. review tools) – zapewniają wsparcie w procesie przeglądu; typowe cechy tych narzędzi to: planowanie przeglądu, wspomaganie śledzenia, wspomaganie

komunikacji oraz współpracy przy przeglądach, a także prowadzenie repozytorium do gromadzenia i raportowania metryk; narzędzia do analizy statycznej i dynamicznej; narzędzia do testowania zabezpieczeń (ang. security testing tools) – wspierają testowanie charakterystyk zabezpieczeń i podatności na ataki; narzędzia

do

testów

wydajnościowych,

obciążeniowych

i przeciążeniowych (ang. performance testing tools, load testing tools, stress testing tools) – narzędzia wspierające testy wydajnościowe; potrafią symulować narastające obciążenie, np. liczbę aktualnie pracujących użytkowników lub liczbę żądań/transakcji w określonym czasie; narzędzia do pomiaru metryk kodu – w szczególności mierzące stopień pokrycia kodu testami; narzędzia do kontroli lub oceny poprawności/jakości danych; narzędzia do debugowania1 (ang. debugging tools) – używane przez programistów do odtwarzania awarii, śledzenia stanu programu i odszukiwania usterek. Debugery umożliwiają programistom wykonanie programów krok po kroku, zatrzymywanie wykonania programu na dowolnej instrukcji oraz sprawdzanie i ustawianie wartości zmiennych; narzędzia do testowania hiperłączy (ang. hyperlink test tools) – używane do sprawdzania, czy na stronie internetowej występują wadliwe (działające nieprawidłowo) hiperłącza; narzędzia do zabezpieczeń (ang. security tools) – narzędzia wspierające zabezpieczenia operacyjne.

30.2. Automatyzacja testów 30.2.1. Czynniki sukcesu udanej automatyzacji Aby projekt automatyzacji testów miał duże szanse powodzenia, muszą wystąpić następujące krytyczne czynniki sukcesu [233]:

dobra architektura automatyzacji testów – narzędzia wykorzystywane do automatyzacji są oprogramowaniem i dlatego powinno się do nich stosować

te same kryteria

dotyczące jakości

(w

szczególności

wymagania niefunkcjonalne), co w przypadku wytwarzanego oprogramowania; dobra architektura oznacza w szczególności wysoką pielęgnowalność, łatwą rozszerzalność, wydajność i łatwość nauki; SUT (testowany system, ang. System Under Test) powinien być zaprojektowany pod kątem testowalności; np. w przypadku testowania GUI dane oraz logika interfejsu powinny być dobrze odseparowane od graficznej prezentacji interfejsu; w przypadku testowania API więcej klas, modułów czy interfejsów powinno być publicznie dostępnych w celu umożliwienia ich przetestowania lub ich użycia w testowaniu; SUT powinien umożliwiać obserwowalność oraz kontrolowalność jego działania; automatycznemu testowaniu powinny najpierw podlegać testowalne części SUT, ponieważ kluczowym czynnikiem sukcesu jest tutaj łatwość implementacji skryptów testowych; taka priorytetyzacja umożliwia ponadto przeprowadzenie dowodu słuszności koncepcji (ang. proof-ofconcept) i osiągnięcie szybkiego zysku (ang. quick win); ustanowienie strategii automatyzacji testów, która będzie uwzględniać kwestie dotyczące pielęgnowalności, usprawniania i spójności; do różnych części SUT być może trzeba będzie zastosować inne podejście automatyzacji; testując GUI, dobrze jest równocześnie testować sam API i sprawdzać spójność wyników obu testów; stworzenie łatwego w użyciu i utrzymaniu, dobrze udokumentowanego frameworka dla automatyzacji; musi on zapewniać możliwość raportowania o jakości SUT, wykonywać i logować wykonanie testów, umożliwiać szybkie rozwiązywanie problemów z niezdanymi testami (które może być powodem samego testu, ale również skryptu testowego czy środowiska), dobrze współpracować z środowiskiem testowym (często jest ono dedykowane dla automatyzacji). krytyczny czynnik sukcesu (ang. critical success factor) – element niezbędny w organizacji lub projekcie do wypełnienia jej/jego misji; krytyczne czynniki

sukcesu to te czynniki lub aktywności, które są niezbędne do zapewnienia powodzenia podejmowanych działań Rozmiar

kodu

tworzonego

na

potrzeby

automatyzacji

jest

podobny,

a nierzadko nawet większy od rozmiaru kodu samego testowanego systemu. Dlatego jest niezbędne objęcie go odpowiednim procesem zarządzania konfiguracją i wersjami. Do samego kodu testowego należy stosować te same zasady, co do kodu testowanego, tzn. powinien być on czytelny, utrzymywalny oraz pisany zgodnie z przyjętymi standardami kodowania. Kod testowy nie powinien być podatny na zmiany w kodzie testowanym (np. techniczna zmiana kodu API nie powinna wpływać na konieczność zmian w kodzie testującym to API).

30.2.2. Metryki dla automatyzacji testów Automatyzacja jest częścią procesu usprawniania testowania i jako taka powinna podlegać monitorowaniu i kontroli. Na każdym etapie tego projektu powinniśmy móc stwierdzić, jaki jest jego postęp w stosunku do planów oraz czy automatyzacja daje nam oczekiwane zyski (ewentualnie kiedy spodziewamy się, że zyski przewyższą koszty automatyzacji). Aby zrealizować to zadanie, możemy wykorzystać następujące metryki [233]: pracochłonność tworzenia testów automatycznych; pracochłonność analizowania incydentów w testach automatycznych; koszt utrzymania testów automatycznych; czas wykonywania testów automatycznych; liczba zautomatyzowanych przypadków testowych (lub stosunek testów zautomatyzowanych do manualnych); liczba testów zdanych i niezdanych; liczba testów z wynikami pozytywnymi;

fałszywie negatywnymi

i

fałszywie

pokrycie kodu testami automatycznymi; gęstość defektów w samym kodzie testowym. Metryki te mogą być użyte razem z metrykami dla testów manualnych, aby określić:

liczbę godzin zaoszczędzonych na testowaniu manualnym; redukcja czasu potrzebnego na wykonanie testów regresji; liczbę zyskanych dodatkowych cyklów wykonania testów; liczba/procent wykonanych dodatkowych testów; procent testów zautomatyzowanych w stosunku do wszystkich testów w suicie testowej; wzrost pokrycia kodu w wyniku użycia automatyzacji.

30.3. Generyczna architektura automatyzacji testów W każdym projekcie automatyzacji testów pojawiają się podobne zadania i czynności, które inżynier testów musi wykonać. Na rysunku 30.4 zebrano te czynności w postaci modelu generycznej architektury automatyzacji testów [233], w który można wpasować w zasadzie każdy projekt automatyzujący proces testowy. Model składa się z warstw, komponentów (białe prostokąty) oraz interfejsów (strzałki). Każdy projekt architektury systemu automatyzacji konkretyzuje w odpowiedni sposób te abstrakcyjne elementy. Model ten umożliwia ustrukturyzowane, systematyczne podejście do procesu automatyzacji. Zaleca się, aby architektura systemu automatyzacji testowania była zgodna z następującymi zasadami ułatwiającymi i utrzymanie rozwiązania automatyzacji testów:

projekt,

rozwój

pojedyncza odpowiedzialność (ang. single responsibility) – każdy komponent rozwiązania powinien odpowiadać za pojedynczy, wydzielony obszar; odpowiedzialność ta powinna być całkowicie zawarta (ang. encapsulated) w tym komponencie; rozszerzalność – każdy komponent powinien móc być rozszerzany, ale nie modyfikowany, tzn. musi być zapewniona zgodność funkcjonalności wstecz; zastępowalność – każdy komponent powinno dać się wymienić na inny bez wpływu na zachowanie całego systemu; oczywiście oczekiwane zachowanie i funkcjonalność komponentów zastępujących musi być co najmniej taka sama jak komponentów zastępowanych; zasada rozdzielności (ang. component segregation principle) – lepiej mieć więcej szczegółowych komponentów niż jeden duży, wielofunkcyjny

komponent; ułatwia to zastępowalność i utrzymywalność; abstrakcyjność zależności – komponenty systemu automatyzacji testowania powinny zależeć od abstrakcji, nie od konkretnych obiektów, tzn. nie powinny zależeć od specyficznych automatycznych scenariuszy testowych. System automatyzacji wdrażany jest zwykle za pomocą określonych aplikacji, technologii i języków programowania. Model z rysunku 30.4 jest niezależny od konkretnych

Rysunek 30.4. Model generycznej architektury automatyzacji testów

dostawców narzędzi. Przykłady takich narzędzi opiszemy w podrozdziale 30.6. Dobrą praktyką podczas wdrażania systemu jest oparcie się na uznanych normach międzynarodowych lub standardach dziedzinowych (patrz Dodatek B). Inżynier automatyzacji powinien mieć wiedzę oprogramowania, w szczególności dotyczącą

z zakresu inżynierii modeli cyklu życia

oprogramowania. Ponadto, powinien znać standardy kodowania i dokumentacji oraz inne praktyki istniejące w wykorzystywanych przez niego technologii, np.: MISRA dla C/C++, czyli standard opisujący sposób tworzenia czystego kodu, wspierający jego bezpieczeństwo, przenaszalność oraz niezawodność; standardy kodowania JSF dla C++ [234]; reguły AUTOSAR (Automotive Open System Architecture). Powróćmy do generycznego modelu. Składa się on z czterech warstw, odpowiadających czterem obszarom, które można automatyzować. Obszary te opiszemy w dalszych podrozdziałach.

30.3.1. Warstwa generowania testów Warstwa generowania testów dotyczy projektowania przypadków testowych i dostarcza narzędzi do ich tworzenia. Mogą one wspierać testera w ręcznym projektowaniu przypadków, a także automatycznie je generować w przypadku wykorzystywania modelu systemu i/lub środowiska. Narzędzia wspomagające generowanie mogą ponadto pomagać w tworzeniu, dostarczaniu lub przechwytywaniu danych testowych. Dane mogą być generowane w sposób syntetyczny, wyciągane z baz danych za pomocą odpowiednich zapytań, scrape’owane ze stron internetowych lub logów itp. Komponenty tej warstwy są używane do edytowania i przeglądania suit testowych. Pozwalają również na definiowanie powiązań między przypadkami testowymi a innymi artefaktami procesu wytwórczego, takimi jak: wymagania, ryzyka czy cele testowania. Służą do dokumentowania projektu testów. W przypadku testowania opartego na modelu, narzędzia potrafią odnieść wygenerowane testy do poszczególnych elementów modelu (np. krawędzi w CFG, du-par w grafie przepływu danych), co pozwala na obliczenie pokrycia.

30.3.2. Warstwa definiowania testów Warstwa definiowania testów zawiera narzędzia wspierające [233]: specyfikację przypadków testowych (wysokiego i niskiego poziomu); definiowanie danych testowych dla niskopoziomowych przypadków testowych; specyfikowanie procedur testowych dla zbioru przypadków testowych; definiowanie skryptów testowych wykonujących przypadki testowe; dostęp do bibliotek testowych (np. w podejściu opartym na słowach kluczowych). Komponenty tej warstwy wykorzystywane są do dokonywania operacji na danych testowych (podział, uwzględnianie więzów, parametryzacja), specyfikowania kolejności wykonywania testów oraz do dokumentowania danych testowych i/lub przypadków testowych. Istnieją również narzędzia pozwalające automatycznie generować sam kod skryptów testowych.

30.3.3. Warstwa wykonania testów Warstwa wykonania testów zawiera narzędzia służące do automatycznego uruchamiania przypadków testowych, wykonywania ich oraz logowania przebiegu wykonania i ich rezultatów. W podejściu opartym na modelu czasami jest również możliwe zastosowanie narzędzia-wyroczni, które potrafi automatycznie zweryfikować poprawność wyniku testu. Komponenty tej warstwy służą do: tworzenia i rozmontowywania testowanego systemu w celu wykonania testów (przykładem takich narzędzi mogą być linkery w kompilatorach czy też aplikacje do ciągłej integracji); tworzenia i rozmontowywania suit testowych, przypadków testowych zawierających dane testowe;

czyli

zbioru

konfiguracji i parametryzacji testów; interpretowania danych oraz przypadków testowych i przekształcanie ich w wykonywalne skrypty (typowym przykładem jest np. testowanie oparte na słowach kluczowych);

instrumentacji systemu testowego oraz testowanego programu w celu logowania wykonania testów oraz umożliwienia zebrania metryk pokrycia; analizowania odpowiedzi testowanego systemu w celu podjęcia decyzji o wykonaniu testów zależnych w jakiś sposób od tej odpowiedzi; porównywania odpowiedzi testowanego systemu z odpowiedziami oczekiwanymi.

30.3.4. Warstwa adaptacji testów Warstwa adaptacji testów zawiera narzędzia wspierające [233]: kontrolę uprzęży testowych; interakcję z testowanym systemem; monitorowanie testowanego systemu; symulację lub emulację środowiska testowanego systemu (np. aplikacje mobilne zamiast bezpośrednio na telefonach komórkowych mogą być testowane na komputerach stacjonarnych wyposażonych w emulatory systemów zainstalowanych na tych urządzeniach). W warstwie adaptacji odbywa się komunikacja między niezależną od technologii definicją testów a konkretną technologią wykorzystywaną przez testowany system oraz środowisko testowe. Jest ona również odpowiedzialna za rozproszenie wykonania testów na wielu urządzeniach lub z użyciem wielu interfejsów testowych, jeśli proces testowy zakłada takie podejście.

30.3.5. Zarządzanie konfiguracją Jeśli popatrzeć na system automatyzacji testów pod kątem jego wdrożenia, to nie różni się on od zwykłego projektu deweloperskiego. W trakcie jego tworzenia pojawi się zatem wiele wersji różnych jego komponentów, które powinny podlegać zarządzaniu konfiguracji i wersjonowaniu. Zarządzanie konfiguracją systemu automatyzacji może obejmować [233]: modele testów; definicje/specyfikacje testów zawierające dane, przypadki testowe i biblioteki;

skrypty testowe; silniki do wykonywania i komponenty;

testów

oraz

pomocnicze

narzędzia

symulatory i emulatory testowanego systemu; wyniki i raporty testów. Zarządzanie konfiguracją spoczywa na Radzie Kontroli Zmian. Rada Kontroli Zmian, komitet kontroli zmiany (ang. Configuration Control Board, change control board) – grupa ludzi odpowiedzialna za ocenę oraz udzielenie lub nieudzielenie zgody na wykonanie zmian w elementach konfiguracji oraz za zapewnienie wykonania zaaprobowanych zmian [7] Jedną z czynności dokonywanych w ramach zarządzania konfiguracją jest identyfikacja konfiguracji oprogramowania. Polega ona na dokonaniu hierarchicznego podziału produktu na elementy konfiguracji oraz związane z nimi komponenty i jednostki służące do wyprodukowania tych elementów. Identyfikacja konfiguracji jest prerekwizytem dla innych czynności zarządzania konfiguracją [235]. identyfikacja konfiguracji (ang. configuration identification) – element zarządzania konfiguracją składający się z wyboru elementów konfiguracji, z których ma się składać system oraz zapis ich funkcjonalnych i fizycznych właściwości w dokumentacji technicznej [7]

30.4. Automatyczna generacja danych testowych Szczególnym typem automatyzacji testów jest generowanie danych testowych. Istnieje wiele metod tej generacji, np.: generacja metodą losową (z rozkładem równomiernym); generacja z rozkładu prawdopodobieństwa innego niż równomierny (zmienną losową2 może być wartość danej, rodzaj danej, ale też np. rozmiar pliku stanowiącego wejście do testu; rozkład często oparty jest na profilu operacyjnym);

generacja z modelu (np. z łańcucha Markowa, specyfikacji napisanej w notacji Z lub B; w przypadku danych tekstowych można wykorzystać np. wyrażenia regularne); generacja na podstawie symbolicznego wykonania kodu (możliwość taką oferują programy, takie jak KLEE [236], które potrafią dobrać dane wejściowe tak, aby wymusić określoną ścieżkę przepływu sterowania); generacja metodyczna, wykorzystująca strategie kombinacyjne, takie jak testowanie par czy testowanie kombinatoryczne (jeśli dane wejściowe zawierają co najmniej 2 elementy); jeśli parametr wejściowy ma skończoną liczbę wartości, to można wygenerować wszystkie możliwe dane testowe; generacja na podstawie danych zewnętrznych (np. dane testowe mogą być ściągnięte ze strony www lub otrzymane w wyniku odpytania bazy danych). generator testu, narzędzie do przygotowywania danych testowych (ang. test generator, test data preparation tool) – rodzaj narzędzia testowego, które pozwala na wybranie danych z istniejącej bazy danych lub ich stworzenie, wygenerowanie, przetworzenie i edycję dla użycia w testowaniu Podczas generacji danych można wykorzystywać różne strategie zapewniające odpowiednie kryteria pokrycia. Oto prosty przykład. Załóżmy, że testujemy pole formularza przeznaczone na wpisanie adresu e-mailowego. Załóżmy ponadto, że adres e-mailowy jest opisany następującym wyrażeniem regularnym3: a {1–10}@a {2–10} (.a +){0–5}.aa gdzie a oznacza znak alfanumeryczny (literę, cyfrę, pauzę lub podkreślenie dolne), liczby w nawiasach klamrowych w indeksie górnym oznaczają liczbę możliwych wystąpień danego obiektu (np. a {1–10} oznacza łańcuch złożony z co najmniej jednego i co najwyżej dziesięciu znaków alfanumerycznych), nawiasy służą do grupowania, a znak małpy oraz kropka są zwykłymi elementami adresu mailowego. Adres [email protected] pasuje do tego wzorca: przed małpą znajduje się ciąg 8 znaków (mieści się w przedziale od 1 do 10), po małpie przed kropką jest łańcuch 6-znakowy (mieści się w przedziale od 2 do 10), część wzorca ujęta w nawias występuje 0 razy, a końcówka „.pl” adresu pasuje do wzorca „.aa”. Adres

[email protected] do wzorca nie pasuje, ponieważ końcówka „.abc” nie pasuje do wzorca „.aa” (po ostatniej kropce muszą wystąpić dokładnie dwa znaki). Mając wzorzec, możemy generować dane testowe np. według następujących reguł: 1) wygeneruj ciąg, w którym wszystkie elementy mogące wystąpić 0 razy nie występują; 2) wygeneruj ciągi, w których każdy element mogący wystąpić od x do y razy występuje: dokładnie x – 1 razy (dana niepoprawna), x razy, y razy oraz y + 1 razy (dana niepoprawna); 3) wygeneruj ciągi, w których z każdego zakresu powtórzeń losowana jest ustalona liczba tych powtórzeń (taki ciąg będzie reprezentował „typową” daną poprawną”); 4) wygeneruj ciągi, w których ustalone we wzorcu elementy są zamieniane na inne lub usuwane. Metoda ta podobna jest do testowania opartego na składni, opisanego w podrozdziale 8.14. Używając powyższych reguł, dla naszego przykładu moglibyśmy wygenerować następujący przykładowy zbiór danych testowych reprezentujących poprawne i niepoprawne adresy e-mailowe (w nawiasach nr reguły, do której się odnoszą, wykrzyknik oznacza daną niepoprawną): (1) [email protected] (2!) @abc.de (2) [email protected] (2) [email protected] (2!) [email protected] (2!) [email protected] (3) [email protected] (3) [email protected] (4!) ab+cde.fg (4!) ab@cde+fg (4!) [email protected] (4!) [email protected]

Oczywiście skrypt realizujący taką generację wyprodukowałby dane w sposób systematyczny. Można wzbogacić go o różnego rodzaju strategie generacji, np. wymusić możliwie małą liczbę przypadków testowych, każdą regułę do każdego elementu stosować dokładnie w jednym przypadku testowym. Tego typu decyzje będą wpływać na czas generacji danych oraz „moc” tego zbioru danych dla wykrywania awarii w systemie. Więcej informacji na temat danych testowych, zarządzania danymi oraz konkretnych przykładów danych pochodzących z różnych obszarów biznesowych Czytelnik znajdzie w książce [237].

30.5. Metody i techniki automatyzacji testów Przedstawimy teraz czynności wykonywane podczas tworzenia architektury systemu automatyzacji. Wykonanie lub nie wykonanie tych czynności zależy od typu projektu automatyzacji, złożoności testowanego systemu, przyjętej strategii testowej, dostępnych zasobów oraz umiejętności. Punkty 30.5.1 oraz 30.5.2 są oparte na sylabusie [233].

30.5.1. Kroki procesu projektowania architektury Zebranie wymagań dla zdefiniowania właściwej architektury automatyzacji. Wymagania te powinny określać: które czynności lub fazy procesu testowego mają podlegać automatyzacji (zarządzanie, specyfikacja, generowanie, wykonywanie, raportowanie itp.); które poziomy testowe mają być wspierane przez automatyzację (testy jednostkowe, integracyjne, systemowe); które typy testów mają być automatyzowane (testy funkcjonalne, obciążeniowe itp.); które role w zespole mają być wspierane przez automatyzację (osoba wykonująca testy, projektująca testy, inżynier jakości, kierownik testów itp.); który z tworzonych produktów, systemów lub która linia produktowa ma być wspierana przez automatyzację (w celu zdefiniowania zakresu i horyzontu czasowego dla systemu automatyzacji testów);

które technologie wykorzystywane przez testowany system mają być wspierane lub z którymi z nich system automatyzacji musi być kompatybilny.

Rysunek 30.5. Proces projektowania architektury automatyzacji testów

Porównanie różnych podejść projektowania architektury. Inżynier ds. automatyzacji powinien rozważyć wady i zalety różnych podejść podczas projektowania

warstw

architektury

systemu

automatyzacji.

Przykładowe

kwestie, nad którymi należy się zastanowić są pokazane na rysunku 30.5. Zostały one pogrupowane według warstw modelu, których dotyczą. Wybór określonego podejścia do generacji testów rzutuje na wybór strategii generacji, np. w podejściu opartym na zachowaniu zazwyczaj wykorzystywanymi strategiami będą modele stanowe lub grafowe. Wybór narzędzia do wykonywania testów wpływa zarówno na podejście do interpretacji/kompilacji procedur testowych, jak i na wybór konkretnej technologii do tej interpretacji/kompilacji. Identyfikacja miejsc, gdzie abstrakcja przyniesie korzyści. Abstrakcja pozwala na uniezależnienie się od kwestii technologicznych. Dzięki niej ta sama suita testowa może być używana w różnych środowiskach testowych i w różnych technologiach. Abstrakcja umożliwia większą przenaszalność, a co za tym idzie – ponowne użycie tych samych testów w różnych projektach. Innymi jej zaletami są: zwiększenie utrzymywalności oraz zdolności adaptacji testów do nowych lub ewoluujących technologii testowanego systemu. Abstrakcja pozwala osobom nietechnicznym na łatwiejszy dostęp i wykorzystanie architektury systemu automatyzacji, ponieważ suity testowe mogą być udokumentowane (np. w postaci graficznej) oraz opisane na wyższym poziomie ogólności, zapewniając tym samym większą czytelność i zrozumiałość. Inżynier ds. automatyzacji powinien, wspólnie z innymi interesariuszami (kierownik testów, testerzy, klient, inżynierzy ds. jakości itp.) określić, jakimi poziomami abstrakcji operować na odpowiednich obszarach systemu automatyzacji testów. Na przykład: które interfejsy warstwy wykonania lub adaptacji powinny zostać udostępnione na zewnątrz, formalnie zdefiniowane i utrzymywane w stabilnej postaci przez cały czas wykorzystywania architektury automatyzacji? Im większa abstrakcja, tym wyższa elastyczność architektury systemu automatyzacji. Niestety, wiąże się to również z wyższymi kosztami początkowymi, związanymi z koniecznością wykorzystania bardziej skomplikowanych mechanizmów automatyzacji, dłuższym czasem wdrożenia, większymi wymaganiami dotyczącymi znajomości odpowiednich narzędzi czy modeli itp.

Kierownik testów (bądź kierownik ds. automatyzacji testów) powinien przeprowadzić analizę opłacalności (ROI) w celu znalezienia odpowiedniego poziomu abstrakcji, uwzględniając wszystkie zależności i kompromisy. Inżynier ds. automatyzacji pomaga w tych analizach, dostarczając ocenę rozwiązań technicznych, porównując różne podejścia itp. Zrozumienie technologii testowanego systemu oraz jego powiązań ze strukturą automatyzacji testowania. W centrum każdego automatycznego wykonywania testów znajduje się kwestia dostępu do interfejsów testowych testowanego systemu. Dostęp ten może być uzyskiwany na różnych poziomach: poziom oprogramowania (np. testowany system i oprogramowanie testowe są ze sobą powiązane); poziom API (ang. Application Programming Interface), czyli interfejsu programowania aplikacji (np. system automatyzacji testów wywołuje funkcje, operacje lub metody dostarczone przez API testowanego systemu); poziom protokołu komunikacyjnego (np. system automatyzacji testów wchodzi w interakcję z testowanym systemem przez protokół http, tcp itp.); poziom usług (np. system automatyzacji testów wchodzi w interakcję z testowanym systemem przez webserwisy, serwisy REST). Ponadto, inżynier ds. automatyzacji powinien określić model obliczeniowy dla architektury automatyzacji testów, który będzie wykorzystany w komunikacji między nim a testowanym systemem, w sytuacji, gdy oba te systemy będą rozdzielone przez API, protokoły lub serwisy. Model taki może być oparty m.in. na następujących paradygmatach: paradygmat oparty na zdarzeniach; paradygmat klient–serwer; paradygmat peer-to-peer. Paradygmat ten często zależy od architektury testowanego systemu i może mieć wpływ na architekturę systemu automatyzacji testów. Zrozumienie środowiska testowanego systemu. Testowany system może być osobną aplikacją, ale może też współpracować z wieloma innymi programami, sprzętem, usługami czy serwisami. System automatyzacji testów może

symulować lub emulować elementy środowiska, w jakim działa testowany system. Czas i złożoność implementacji architektury testaliów. Kierownik projektu automatyzacji, we współpracy z inżynierem ds. automatyzacji, powinien oszacować wysiłek związany z przeprowadzeniem projektu automatyzacji. Przykładowe metody estymacji to: metoda analogii (np. przeprowadzana na podstawie analizy podobnych projektów); wykorzystanie WBS (ang. Work Breakdown Structure); estymacja parametryczna, np. przy użyciu modelu Putnama lub modelu COCOMO II; estymacja rozmiaru architektury, np. przy użyciu Analizy punktów funkcyjnych lub Analizy punktów pracy zaplanowanej (ang. Story Points); metoda estymacji grupowej, np. metoda delficka lub technika pokera planistycznego. Łatwość użycia danej implementacji architektury. Oprócz funkcjonalności systemu automatyzacji testowania, jej kompatybilności z testowanym systemem, długookresowej stabilności, wysiłku oraz analizy opłacalności, inżynier ds. automatyzacji powinien rozważyć kwestie użyteczności systemu automatyzacji. Oto kilka przykładów: projekt architektury zorientowany na testowanie; łatwość użycia systemu automatyzacji; wsparcie systemu automatyzacji dla innych ról w projekcie, np. inżynierów jakości czy kierowników projektu; efektywność organizacji, nawigacji, wyszukiwania przy użyciu systemu automatyzacji; użyteczność i czytelność dokumentacji, podręczników i wbudowanej pomocy; raportowanie przez system automatyzacji i dotyczące samego systemu.

30.5.2. Podejścia do automatyzacji przypadków testowych

Przypadki testowe przekładają się na sekwencje akcji wykonywanych na testowanym systemie. Sekwencje te mogą być udokumentowane w procedurze testowej i/lub być zaimplementowane jako skrypt testowy. Zautomatyzowane przypadki testowe powinny również definiować lub pobierać z zewnętrznego źródła dane testowe, będące „treścią” interakcji z testowanym systemem. Wreszcie, przypadki testowe powinny sprawdzać, czy wynik rzeczywisty jest zgodny z wynikiem oczekiwanym. Istnieją różne sposoby tworzenia sekwencji akcji: 1) inżynier ds. automatyzacji implementuje przypadki testowe bezpośrednio jako skrypty testowe; ta opcja jest najmniej polecana, ze względu na brak abstrakcji; 2) inżynier ds. automatyzacji tłumaczy procedury testowe na automatyczne skrypty testowe; w tej wersji istnieje abstrakcja, ale brak jest automatyzacji generowania skryptów testowych; 3) narzędzie tłumaczy procedury testowe na automatyczne skrypty testowe; to podejście łączy w sobie abstrakcję z automatyczną generacją skryptu; 4) narzędzie generuje automatyczne procedury testowe i/lub dane testowe z modeli; w stosunku do podejścia 3. tu mamy dodatkowo automatyczną generację procedur testowych. Należy mieć na uwadze, że nie zawsze opcja 4. jest najlepsza, a 1. najgorsza – wybór podejścia zależy od wielu wymienionych wcześniej czynników. Czasami opłaca się implementować bezpośrednio przypadki testowe jako skrypty; czasami automatyzacja procedur testowych może być niemożliwa; czasami dane testowe mogą/powinny być generowane wyłączne w sposób automatyczny, co eliminuje podejście 1. itd. Dalej jest podana relacja między powszechnie stosowanymi podejściami do automatyzacji przypadków testowych a sposobami tworzenia sekwencji akcji: podejścia „nagraj i odtwórz” (ang. capture & replay) oraz skryptów linearnych mogą być użyte w opcji 1.; podejścia: zorganizowanych skryptów, oparte na danych, oparte na słowach kluczowych mogą być użyte w opcji 2. lub 3.; testowanie oparte na modelu (włącznie z podejściem opartym na procesie) może być użyte w opcji 4.

Opiszemy teraz dokładniej podejścia do automatyzacji przypadków testowych, omawiając jednocześnie ich wady i zalety oraz pokazując przykładowe ich zastosowanie.

30.5.3. Podejście nagraj i odtwórz Opis

podejścia.

W

podejściu

nagraj

i

odtwórz

narzędzie

służy

do

przechwytywania interakcji z testowanym systemem, które tworzą sekwencję akcji zdefiniowaną przez procedurę testową. Wejścia oraz czynności wykonywane przez użytkownika są przechwytywane i nagrywane; wyjścia również mogą być nagrywane w celu późniejszego ich sprawdzenia z odpowiedziami oczekiwanymi. Sprawdzanie następuje podczas odtwarzania nagranych akcji i może odbywać się w różny sposób: metoda manualna: tester obserwuje odpowiedź systemu i sprawdza, czy występują jakieś anomalie; metoda zupełna: wszystkie wyjścia przechwycone podczas nagrywania muszą być zreprodukowane podczas odtwarzania sekwencji akcji; metoda dokładna: wszystkie przechwycone wyjścia muszą być zreprodukowane dokładnie w taki sam sposób, jak podczas nagrywania (np. zachowując ich kolejność, czas odpowiedzi); metoda punktów kontrolnych: podczas odtwarzania są sprawdzane tylko wybrane wyjścia, w niektórych miejscach, dla określonych wartości wejściowych. Zalety. Metoda nagraj i odtwórz może być stosowana na poziomie GUI lub API. Jest łatwa do konfiguracji i użycia. Nie wymaga znajomości języków skryptowych przez testera, dlatego testy mogą być wykonywane np. przez eksperta biznesowego, a niekoniecznie przez testera. Wady. Skrypty powstałe w wyniku nagrywania są trudne w utrzymaniu – przechwycone wykonanie testowanego systemu silnie zależy od jego wersji i konfiguracji, dla której przeprowadzono nagrywanie. Na przykład, jeśli odbywało się to na poziomie graficznego interfejsu użytkownika, zmiany w układzie GUI mogą wpływać na skrypty testowe, nawet jeśli dotyczą chociażby zmiany współrzędnych danego elementu. Problemy mogą pojawić się, jeśli np. elementy typu radio button zostaną zamienione na listę do wyboru lub na

elementy typu check-box (patrz rys. 30.6). Kolejna wada: jeśli po kliknięciu jakiegoś elementu GUI system potrzebuje czasu na odpowiedź, to gdy podczas odtwarzania system ten będzie bardziej obciążony niż podczas nagrywania, czas ten może się wydłużyć. W rezultacie, narzędzie odtwarzające będzie chciało wybrać element, który nie jest jeszcze dostępny.

Rysunek 30.6. Różne sposoby reprezentacji elementów GUI: a) check-box; b) radio button; c) lista rozwijana Przykład. Pokażemy prosty przykład automatyzacji testu za pomocą metody „nagraj i odtwórz”. Wykorzystamy do tego celu narzędzie Selenium. Przetestujemy poprawność obliczania wskaźnika BMI dla formularza znajdującego się na stronie http://bmi-online.pl/. Forumlarz jest pokazany na rysunku 30.7.

Rysunek 30.7. Formularz ze strony www obliczającej indeks BMI

Rysunek 30.8. Widok okna Selenium po nagraniu przypadku testowego Uruchamiamy wtyczkę Selenium w przeglądarce. Klikamy w niej czerwony guzik nagrywania (oznaczony czarną strzałką na rys. 30.8) i wpisujemy w pola formularza wartości: 82 dla wagi oraz 178 dla wzrostu, po czym klikamy przycisk „Oblicz BMI”. Akcje te zostały nagrane i są widoczne w zakładce Table narzędzia Selenium (patrz rys. 30.8).

Chcemy jeszcze dodać w przypadku testowym sprawdzanie poprawnej odpowiedzi. Powinna ona wynosić 25,88. Jest to tekst, który pojawia się na stronie po naciśnięciu przycisku „Oblicz BMI”. W Selenium z rozwijanej listy Command wybieramy VerifyTextPresent i w pole Value wpisujemy 25,88. Krok ten będzie sprawdzał, czy na stronie rzeczywiście pojawił się tekst „25,88”. Po nagraniu przypadku testowego możemy go zapisać lub wyeksportować jako skrypt (w p. 30.5.4, gdzie są omówione skrypty linearne, na listingu 30.3 pokazano skrypt w języku Python dla naszego przypadku testowego). Aby odtworzyć skrypt, w Selenium klikamy na ikonę zielonego trójkąta (Play current test case) i otrzymujemy wynik pokazany na rysunku 30.9. Wartości wagi i wzrostu zostały automatycznie wpisane w formularz, przycisk „Oblicz BMI” naciśnięty, a na końcu zweryfikowano, czy w wyniku tych działań na stronie www pojawił się tekst „25,88”. Wszystkie kroki tego przypaku testowego są w Selenium zaznaczone kolorem zielonym, co oznacza ich pomyślne zaliczenie.

Rysunek 30.9. Selenium – odtworzenie nagranych akcji przypadku testowego W kontekście testowania stron www warto wspomnieć o przydatnym wzorcu projektowym, jakim jest Page Object Pattern, czyli wzorzec obiektu strony. Stosowanie go w narzędziach takich jak Selenium jest zalecane jako jedna

z dobrych praktyk. Wzorzec ten reprezentuje ekrany aplikacji internetowej jako ciąg obiektów i enkapsuluje cechy (ang. features) reprezentowane przez strony www. Obiekt strony jest niczym innym, jak klasą służącą jako interfejs do testowanej strony. Dzięki niemu możemy w wygodny sposób modelować interfejs użytkownika w testach. Oto prosty przykład, zaczerpnięty ze strony http://assertselenium.com/automation-design-practices/page-object-pattern/. listingu 30.1 jest pokazany Page Object modelujący stronę wyszukiwania Google.

public class GoogleSearchPage { protected WebDriver driver; private WebElement q; private WebElement btn; public GoogleSearchPage(WebDriver driver) { this.driver = driver; } public void open(String url) { driver.get(url); } public void close() { driver.quit(); } public String getTitle() { return driver.getTitle(); } public void searchFor(String searchTerm) { q.sendKeys(searchTerm); btn.click(); } public void typeSearchTerm(String searchTerm) { q.sendKeys(searchTerm); } public void clickOnSearch() {

Na

btn.click(); } } Listing 30.1. Page Object dla strony www wyszukiwania Google W szczególności dostarcza on takich metod jak searchFor (wyszukiwanie przez Google) oraz typeSearchTerm (pozwalającą na wpisanie tekstu w pole tekstowe wyszukiwania na stronie głównej Google). Przykładowy test wykorzystujący ten Page Object pokazany jest na listingu 30.2.

public class SearchOnGoogle { private GoogleSearchPage page; @Before public void openTheBrowser() { page = PageFactory.initElements( new FirefoxDriver(),GoogleSearchPage.class); page.open("http://www.google.com"); } @After public void closeTheBrowser() { page.close(); } @Test public void tytulWyszukanejStronyMaZawieracNapisSelenium() { page.searchFor("selenium"); assertThat(page.getTitle(), containsString("selenium") ); } }

Listing 30.2. Test z wykorzystaniem Page Object Pattern Instrukcje tuż pod annotacją @Before opisują montowanie środowiska testowego: uruchomienie przeglądarki oraz otworzenie strony www.google.com. Instrukcje pod annotacją @Test zawierają właściwą procedurę testową: instrukcja page.searchFor(„selenium”) powoduje wpisanie do pola tekstowego wyszukiwarki frazy „selenium” i kliknięcie guzika „Szukaj”. Następnie jest sprawdzane, czy tytuł strony, na której się znaleźliśmy, zawiera rzeczywiście to słowo. Instrukcje pod annotacją @After służą do rozmontowania konfiguracji testowej, tzn. zamykają przeglądarkę. To, co jest tu istotne, to sposób odwoływania się do poszczególnych elementów strony – ta funkcjonalność jest opakowana w metody klasy GoogleSearchPage, która służy jako interfejs do komunikacji ze stroną wyszukiwarki Google. Tester tworzy testy, odwołując się do tych metod, a nie bezpośrednio do elementów strony www.

30.5.4. Skrypty linearne Opis podejścia. W skrypcie linearnym przypadek testowy jest implementowany bezpośrednio jako skrypt testowy. Na przykład, dla przypadku testowego z poprzedniego przykładu skryptem takim mógłby być program z listingu 30.3. Skrypt linearny może być tworzony ręcznie lub automatycznie (np. za pomocą narzędzia takiego jak Selenium). Zwykle dla każdej procedury testowej powstaje jeden duży skrypt testowy. Dobrą praktyką jest dodawanie do tych skryptów komentarzy tłumaczących, jakie czynności w danym kroku są wykonywane. Często rozbudowuje się je o dodatkowe instrukcje weryfikujące różnego rodzaju wyjścia i odpowiedzi systemu. W skryptach można wykorzystywać ustandaryzowane metody odwoływania się do elementów testowanego systemu, np. Document Object Model [238] dla stron www czy XPath dla dokumentów XML [239]. Skrypt może zostać odtworzony przez narzędzie, przez co zostaną powtórzone dokładnie te same czynności, które zostały zakodowane w skrypcie. Zalety. Metoda nie wymaga żmudnych i kosztownych przygotowań przed rozpoczęciem automatyzacji. Umiejętności programistyczne nie są konieczne, gdy skrypt jest tworzony automatycznie przez narzędzie. Wady. Koszt automatyzacji procedur testowych jest liniowy ze względu na ich liczbę, tzn. skrypt automatyzacji setnej procedury zajmie podobną ilość czasu, co w przypadku procedury pierwszej – nie ma tu widocznego zysku na

pracochłonności. Fragmenty kodów skryptów wykonujących podobne czynności będą podobne lub wręcz identyczne – nie wykorzystujemy tu bibliotek testowych z gotowymi fragmentami kodu. Skrypty linearne, podobnie jak w przypadku metody „nagraj i odtwórz”, są trudne i kosztowne w utrzymaniu. Zmiana we fragmencie kodu obecnym w wielu skryptach powoduje konieczność modyfikacji wszystkich tych skryptów. Skrypty są pisane za pomocą języka programowania, dlatego mogą być trudno zrozumiałe dla nie-programistów. Przykład. W listingu 30.3 pokazano napisany (a właściwie wygenerowany automatycznie) w Pythonie skrypt linearny dla przypadku testowego sprawdzającego poprawność obliczania wskaźnika BMI na podstawie wagi i wzrostu przez stronę http://bmi-online.pl/.

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import Select from selenium.common.exceptions import NoSuchElementException import unittest, time, re class SprawdzanieBMITypoweDane(unittest.TestCase): def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) self.base_url = "http://bmi-online.pl/" self.verificationErrors = [] self.accept_next_alert = True def test_sprawdzanie_b_m_i_typowe_dane(self): driver = self.driver driver.get(self.base_url + "/") driver.find_element_by_name("waga").clear() driver.find_element_by_name("waga").send_keys("82") driver.find_element_by_name("wzrost").clear() driver.find_element_by_name("wzrost").send_keys("178")

driver.find_element_by_css_selector("input[type=\"submit\"]").clic # Warning: verifyTextPresent may require manual changes

try: self.assertRegexpMatches(driver.find_element_by_css_selector("BODY r"^[\s\S]*$") except AssertionError as e: self.verificationErrors.append(str(e)) def is_element_present(self, how, what): try: self.driver.find_element(by=how, value=what) except NoSuchElementException, e: return False return True def is_alert_present(self): try: self.driver.switch_to_alert() except NoAlertPresentException, e: return False return True def close_alert_and_get_its_text(self): try: alert = self.driver.switch_to_alert() alert_text = alert.text if self.accept_next_alert: alert.accept() else: alert.dismiss() return alert_text finally: self.accept_next_alert = True def tearDown(self): self.driver.quit() self.assertEqual([], self.verificationErrors) if __name__ == "__main__":

unittest.main() Listing 30.3. Skrypt w Pythonie dla nagranego przypadku testowego

30.5.5. Skrypty zorganizowane Opis podejścia. Skrypty zorganizowane, w przeciwieństwie do skryptów linearnych, wykorzystują biblioteki skryptów, które można wykorzystywać wielokrotnie na takiej samej zasadzie, jak biblioteki czy pakiety w językach programowania. Metodę skryptów zorganizowanych można porównać do paradygmatu programowania proceduralnego. Skrypty wchodzące w skład bibliotek zawierają sekwencje czynności wymaganych przez wiele różnych testów. Przykładem może być np. operacja logowania do systemu, od której powinien rozpoczynać się każdy scenariusz testowy. Zalety. Niewątpliwą zaletą podejścia zorganizowanego jest znacząca redukcja kosztów utrzymania w porównaniu z opisanymi wcześniej podejściami, a także zmniejszenie kosztów automatyzacji nowych testów (ponieważ mogą one wykorzystywać wcześniej napisane skrypty). Jeżeli testowanie jest oparte na API, to jest możliwe rozpoczęcie automatyzacji jeszcze przed wydaniem testowanego systemu. W takim przypadku jedyną wymaganą rzeczą jest gotowa definicja API w postaci zbioru funkcji/procedur wraz z opisem ich parametrów. Wady. Za wadę można uznać zwiększone początkowe koszty tworzenia reużywalnych skryptów. Jeśli jednak projekt automatyzacji będzie przeprowadzony poprawnie, ta wstępna praca zwróci się tym szybciej, im częściej skrypty z bibliotek będą ponownie używane w innych testach. Tworzenie skryptów zorganizowanych wymaga umiejętności programistycznych, ponieważ samo nagrywanie czynności tutaj nie wystarczy. Biblioteki skryptów muszą być dobrze zarządzane. Powinny być poddane kontroli zmian oraz kontroli wersji, co wiąże się ze wzrostem nakładów na czynności o charakterze organizacyjnym. Dobre zarządzanie oznacza również dokładną dokumentację skryptów tak, aby mogły być w łatwy sposób wyszukiwane oraz wykorzystywane przez testerów. Zaleca się użycie ujednoliconej konwencji nazewnictwa skryptów. Nazwy powinny oddawać treść/znaczenie poszczególnych skryptów.

Przykład. W listingu 30.4 jest przedstawiony pseudokod przykładowego skryptu zorganizowanego dla systemu ELROJ z Dodatku A.

library CommonTestTasks { function Login(String username, String password) { Connection C := new Connection() Service svc := new Service() Session s := new Session() C := getConnection("www.xyz.abc") svc := C.Service("loginPage") svc.setUsername(username) svc.setPassword(password) s := login(svc) assertEquals(s.Status,1) assertEquals(s.GetUsername(),username) assertEquals(s.GetPassword(),password) return s } function Logout(Session s) { if (s.Status == 1) { assertEquals(s.CloseSession(),1) return 1 } else { return NOT_LOGGED_IN } } } function TestujDublujaceLinie() { Session s = Login("john","doe") s.timetableFacade.RemoveTimetable() timetableFacade tf := ReadTimetable("110.")

s.timetableFacade.AddLine(110) s.timetableFacade.AddLine(110) assert(s.timetableFacade.Equals(tf)) Logout(s) } ... function TestujUsuniecieLinii() { Session s = Login("john","doe") s.timetableFacade.RemoveTimetable() timetableFacade tf := ReadTime("") s.timetableFacade.AddLine(110) s.timetableFacade.RemoveLine(110) assert(s.timetableFacade.Equals(tf)) Logout(s) } Listing 30.4. Pseudokod zorganizowanego skryptu Część oznaczona słowem kluczowym library jest osobnym plikiem – biblioteką skryptów testowych Login oraz Logout zawierającym testy sprawdzające funkcjonalność logowania oraz wylogowania. Funkcje TestujDublujacaLinie oraz TestujUsuniecieLinii, a także wszystkie inne funkcje m.in., symbolicznie oznaczone wielokropkiem, są skryptami zorganizowanymi. Każdy z nich zawiera na początku instrukcję logowania (za pomocą użytkownika „john” i hasła „doe”), która wykorzystuje biblioteczny test funkcjonalności logowania. Analogicznie, każdy z nich na samym końcu wykorzystuje biblioteczny test funkcjonalności wylogowania, który jednocześnie wylogowuje użytkownika z systemu. Dzięki zdefiniowaniu skryptów logowania i wylogowania w pozostałych skryptach nie trzeba powtarzać tych samych instrukcji – wystarczy jedna linia kodu wywołująca funkcję logowania i jedna wywołująca funkcję wylogowania. Funkcja ReadTimetable pozwala na ręczne zdefiniowanie rozkładu jazdy. Instrukcje te potrzebne są nam do określenia oczekiwanych, wynikowych rozkładów powstałych na skutek wykonania testów TestujDublujaceLinie oraz

TestujUsuniecieLinii. Zauważmy, że dane testowe we wszystkich skryptach są wpisane na sztywno.

30.5.6. Testowanie oparte na danych Opis podejścia. Testowanie oparte na danych (ang. data-driven testing), zwane również testowaniem sterowanym danymi jest rozszerzeniem techniki skryptów zorganizowanych. Główna różnica między tymi podejściami to sposób traktowania i wykorzystywania danych testowych. W testowaniu sterowanym danymi skrypty testowe oraz dane testowe są rozdzielone, tzn. dane są wyłączone ze skryptów i umieszczone w osobnych plikach bądź bazach danych. Skrypt testowy podczas wykonania odczytuje dane testowe z tych zewnętrznych źródeł. Dzięki temu jeden skrypt testowy umożliwia wykonanie wielu testów. Takie skrypty testowe nazywa się skryptami kontrolnymi. Jeden skrypt kontrolny najczęściej wykonuje jeden typ testu. Dlatego najczęściej tworzy się wiele skryptów kontrolnych. Zwiększanie liczby danych testowych dla danego skryptu kontrolnego nie przekłada się zwykle na zwiększenie pokrycia strukturalnego. testowanie oparte na danych, testowanie sterowane danymi (ang. data-driven testing) – technika automatyzacji testów polegająca na umieszczeniu danych testowych i oczekiwanych wyników w tabeli lub arkuszu kalkulacyjnym tak, aby jeden skrypt testowy mógł wykonać wszystkie testy z tabeli; jest często używane jako uzupełnienie narzędzi wykonywania testów, takich jak narzędzia rejestrująco-odtwarzające [24] Zalety. Koszt dodania nowego testu jest praktycznie pomijalny – jedyne, co trzeba zrobić, to dodać do pliku lub bazy z danymi wiersz lub rekord zawierający nowe dane testowe. Technika testowania opartego na danych jest wykorzystywana do automatyzacji wielu wariantów użytecznego testu, co pozwala w szybki i wygodny sposób na głębsze, bardziej szczegółowe przetestowanie określonego obszaru funkcjonalności testowanego systemu. Tworzenie testów nie wymaga umiejętności programistycznych. Analityk testów może automatyzować testy przez wybór/stworzenie jednego lub wielu plików z danymi testowymi. Nie jest zależny od technicznego analityka testów czy programisty tak bardzo, jak w poprzednio opisanych podejściach.

Wady. Pliki/bazy z danymi testowymi wymagają odpowiedniego utrzymania i właściwego zarządzania, jednak nie stanowi to zbyt wielkiej wady, jeśli jest przeprowadzane zgodnie ze sztuką. Większą wadą jest ograniczona zwykle możliwość przeprowadzania testów negatywnych. Przykład. Rozważmy skrypt testowy (w postaci pseudokodu) z listingu 30.5. Implementuje on przypadek testowy dla systemu ELROJ z Dodatku A polegający na wczytaniu rozkładu jazdy, aktualnej godziny oraz liczby wierszy ekranu i sprawdzeniu poprawności wyświetlanej na ekranie treści.

1 function TestujZawartoscEkranu(file f) { 2 String timetable, currentTime 3 file TestDataFile := open(f, type="readOnly") 4 while (not eof(TestDataFile)) do { 5 testCaseData := readLine(TestDataFile) 6 (id, lines, ttable, currentTime, noOfLines, expectedOutput):= 7 split(testCaseData, ";") 8 timetableFacade tf = new timetableFacade() 9 10

tf.BulkLoad(lines, ttable, currentTime) String actRes = tf.getDisplayString(currentTime,

noOfLines) 11 if (assertEquals(actRes, expectedOutput)==1) 12 13 14

write "TEST ID " + id + ": ZDANY" else { write "TEST ID " + id + ": NIEZDANY"

15 write "Wynik oczekiwany : " + expectedOutput 16 write "Wynik rzeczywisty: " + actRes 17 } 18 } 19 } Listing 30.5. Skrypt dla testowania opartego na danych

Skrypt w linii 3. otwiera plik z danymi testowymi. W każdym przebiegu pętli 4.–18. jest czytana jedna linia pliku (linia 5.), zawierająca dane testowe dla jednego przypadku testowego. Linia taka składa się z następujących, oddzielonych średnikami elementów (linie 6.–7.): id, zbiór linii autobusowych, rozkład jazdy, bieżący czas, liczba linii ekranu, oczekiwane wyjście. Elementy te są przypisywane do odpowiednich zmiennych. W liniach 8.–9. jest tworzony rozkład jazdy za pomocą funkcji BulkLoad. Funkcja ta nie występuje w oryginalnej dokumentacji systemu – została stworzona na potrzeby testowania sterowanego danymi i stanowi element uprzęży testowej. W linii 10. jest wywoływana testowana funkcja getDisplayString, a do zmiennej actRes jest przypisywany wynik przez nią zwrócony. W linii 11. znajduje się asercja porównująca wynik oczekiwany z aktualnym. Jeśli są zgodne, program wypisze na wyjście komunikat mówiący, że test o numerze id został zaliczony. W przeciwnym wypadku, wypisane zostaną oba wyjścia: aktualne i porównania.

oczekiwane, w celu umożliwienia

ich ręcznego

Poniżej jest pokazany przykładowy plik z danymi. Do pliku tego analityk testów może dodawać nowe testy według uznania, bez znajomości języka programowania, w jakim został napisany skrypt TestujZawartoscEkranu.

1; 110 111; "110 | 05 06 11 34 35 || 111 | 08 30 35 39"; 31; 2; 110 3 111 4 2; 129 130 141; "129 | 00 || 130 | 00 || 141 | 00 10"; 58; 4; 129 2 130 2 141 2 141 12 3; 118; "118 | 10 30 50 || [komunikat]"; 30; 5; 118 0 118 20 118 40 118 60 komunikat Omówmy pierwszą linię programu. Definiuje ona przypadek testowy o numerze 1. Rozkład zawiera minuty przyjazdów dwóch linii: 110 i 111. Linia 110 przyjeżdża w minutach 5, 6, 11, 34 i 35 każdej godziny, a linia 111 – w minutach 8, 30, 35 i 39. Aktualny czas to 31 minuta. Autobus linii 110 przyjedzie zatem za 3 minuty, a autobus linii 111 – za cztery. Ekran ma dwie linijki, dlatego oczekiwanym wyjściem zwracanym przez funkcję jest łańcuch „110 3 111 4” reprezentujący ekran przedstawiony na rysunku 30.10.

LINIA

Czas przyjazdu

110

3 min

111

4 min

Rysunek 30.10. Oczekiwana informacja na ekranie systemu ELROJ

w

Należy zauważyć, że skrypt z listingu 30.5 nie jest napisany zbyt dobrze – szczególności nie zawiera on mechanizmów zabezpieczających przed

wczytaniem niepoprawnych danych (np. gdy w jakiejś linii będzie brakowało jednego z niezbędnych elementów lub będzie miała niewłaściwą strukturę). Sam skrypt kontrolny powinien zostać poddany procesowi testowemu lub inspekcji. Można np. zapytać, co się stanie, jeśli w rozkładzie (trzeci parametr) wystąpi linia, której nie ma w zbiorze linii (drugi parametr)? Być może lepszym pomysłem byłoby usunięcie w ogóle drugiego parametru i odczytywanie zbioru linii bezpośrednio z rozkładu. Skrypty o niskiej jakości mogą wpływać negatywnie na proces testowy, opóźniając go. Podczas tworzenia skryptów należy dbać o ich jakość tak samo, jak o jakość kodu produkcyjnego.

30.5.7. Testowanie oparte na słowach kluczowych Opis podejścia. Testowanie oparte na słowach kluczowych jest rozszerzeniem techniki testowania opartego na danych. Po pierwsze, istnieje tylko jeden skrypt kontrolny. Po drugie, pliki zewnętrzne nie zawierają czystych danych testowych, ale definicje testów. Definicje te są tworzone przy użyciu przyjaznych użytkownikowi słów kluczowych (stąd nazwa techniki). Każde słowo kluczowe powinno być związane z jakąś znaczącą funkcją biznesową. Najczęściej słowa te dotyczą wysokopoziomowych interakcji z systemem (np.: zarejestruj się w systemie, złóż zamówienie). Każdemu słowu kluczowemu odpowiada specjalny skrypt testowy „oprogramowujący” je, tzn. zawierający zestaw instrukcji niezbędnych do przeprowadzenia danej akcji. Sekwencje słów kluczowych, wraz z odpowiadającymi im danymi destowymi są wykorzystywane do budowania przypadków testowych. Dane testowe zwykle przekazywane są jako parametry odpowiednich słów kluczowych. Można wyróżnić specjalne słowa kluczowe odpowiadające za czynności weryfikacji lub też czynności te można włączyć do słów kluczowych opisujących funkcje biznesowe. Tworzenie przypadków z sekwencji słów kluczowych przypomina konstruowanie budowli z klocków. Tester nie musi znać kodu źródłowego

odpowiadającego poszczególnym słowom, ale sam język słów kluczowych jest wysokopoziomowy i zrozumiały dla testera. Dzięki takiemu podejściu analityk testów może tworzyć samodzielnie przypadki testowe, nie mając umiejętności programistycznych. Skrypt kontrolny wczytuje stworzoną przez analityka testów sekwencję słów kluczowych wraz z ich parametrami zawierającymi dane testowe, a następnie wykonuje fragmenty kodu odpowiadające poszczególnym słowom. Testowaniu opartemu na słowach kluczowych jest poświęcona piąta część normy ISO 29119 – Software Testing Standard. Zalety. Po stworzeniu skryptu kontrolnego oraz skryptów oprogramowujących poszczególne słowa kluczowe, koszt dodawania nowych testów jest minimalny. Wymagane jest jedynie dostarczenie sekwencji słów i ich parametrów. Skrypt kontrolny automatycznie przekształci je w procedurę testową. Analityk testów może specyfikować automatyczne testy, opisując je słowami kluczowymi i danymi testowymi. Dzięki temu jest niezależny od technicznego analityka testów czy programisty. Ma również dużą swobodę w zakresie tworzenia testów. Nie jest ograniczony funkcjonalnością poszczególnych skryptów testowych tak, jak w podejściu opartym na danych. Może wykazać się o wiele większą kreatywnością, składając testy ze słów kluczowych tak, jak konstruuje się wspomnianą wcześniej budowlę z klocków. Tworzenie i czytanie skryptów tworzonych przez testerów jest łatwe i zrozumiałe, ponieważ wykorzystuje się w nich słowa kluczowe oznaczające konkretne akcje w systemie. Przypadki testowe są łatwe w utrzymaniu, gdyż odwołują się do abstrakcyjnych nazw – logika biznesowa ukryta jest w skryptach odpowiadających poszczególnym słowom kluczowym. Dlatego słowa kluczowe oferują testerowi abstrakcję od skomplikowanych mechanizmów wykorzystywanych w interfejsach testowanego systemu. Wady. Implementowanie słów kluczowych jest zadaniem żmudnym i czasochłonnym, zwłaszcza gdy wykorzystuje się narzędzie niemające wsparcia dla tej techniki pisania skryptów. W przypadku małych systemów koszt stosowania podejścia opartego na słowach kluczowych może być zbyt dużym narzutem w porównaniu z kosztochłonnością całego procesu testowego. Podczas wyboru słów kluczowych należy wykazać się dużą ostrożnością. Dobrze dobrane słowa kluczowe będą wykorzystywane przez wielu testerów podczas tworzenia różnych testów. Słowa złe, nietrafione, będą użyte kilka razy

lub nie użyte wcale, więc korzyści z ich używania będą niewspółmiernie małe w porównaniu do kosztów ich implementacji. Przykład. Rozważmy system ELROJ z Dodatku A. Analitycy testowi wybrali słowa kluczowe zebrane w tabeli 30.1. Tabela 30.1. Zestaw słów kluczowych dla programu ELROJ

Słowo kluczowe

Parametry

Opis

WYCZYŚ Ć

(brak)

Us uwa ws zys tkie kurs y i linie z rozkładu

DODAJ_LINIĘ

nrLinii – liczba całkowita z zakres u 100–199

Dodaje do rozkładu linię o numerze nrLinii

US UŃ_LINIĘ

nrLinii – liczba całkowita z zakres u 100–199

Us uwa z rozkładu linię o numerze nrLinii wraz z ws zys tkimi jej kurs ami

DODAJ_KURS

nrLinii (100–199), minuta (0–59)

Dodaje kurs dla linii nrLinii przyjeżdżający o minucie minuta. Jeś li nie ma takiej linii, to najpierw jes t wywołane DODAJ_LINIĘ(nrLinii)

US UŃ_KURS

nrLinii (0–199), minuta (0–59)

Us uwa odpowiedni kurs dla zadanej linii; nie ma efektu, jeś li brak teg o kurs u lub linii

US TAW_KOMUNIKAT

tekst

Us tawia tekst jako komunikat

US UŃ_KOMUNIKAT

tekst

Us uwa komunikat

S PRAWDŹ_EKRAN

liczbaLinii – liczba linii ekranu bieżącaMinuta (0–59) treśćEkranu – zawartoś ć ekranu w formie par liczb linia minuta oddzielanych s pacją; os tatnim elementem może być komentarz, jeś li jes t us tawiony

Zwraca TRUE, jeś li zawartoś ć ekranu dla zadanych parametrów liczbaLinii oraz bieżącaMinuta jes t identyczna z wartoś cią zmiennej treśćEkranu; FALS E w przeciwnym przypadku

nrLinii (100–199)

Zwraca TRUE, jeś li linia nrLinii wys tępuje w rozkładzie; FALS E w przeciwnym przypadku

nrLinii (100–199) minuta (0–59)

Zwraca TRUE, jeś li kurs minuta linii nrLinii wys tępuje w rozkładzie; FALS E w przeciwnym przypadku

CZY_JES T_LINIA

CZY_JES T_KURS

DODAJ_MAX_KURS ÓW nrLinii (100–199)

Dodaje ws zys tkie możliwe 60 kurs ów dla linii nrLinii; jeś li linia nie is tnieje, to najpierw jes t tworzona

Przy użyciu tych słów kluczowych tester może tworzyć różne procedury testowe, na przykład takie, jak na listingu 30.6.

# Przypadek testowy PT-001: dublująca się linia WYCZYŚĆ DODAJ_LINIĘ(103) DODAJ_KURS(103, 57) DODAJ_LINIĘ(103) CZY_JEST_LINIA(103) CZY_JEST_KURS(103) SPRAWDŹ_EKRAN(2, 58, "103 59 103 119") # Przypadek testowy PT-002: dwa kursy o tym samym czasie WYCZYŚĆ DODAJ_KURS(101,4) DODAJ_KURS(100,4) DODAJ_KURS(100,5) SPRAWDŹ_EKRAN(2, 4, "100 0 101 0") # Przypadek testowy PT-003: interwał czasowy większy niż godzina WYCZYŚĆ DODAJ_KURS(135, 10) DODAJ_KURS(135, 40) SPRAWDŹ_EKRAN(6, 30, "135 10 135 40 135 70 135 100 135 130 135 160") Listing 30.6. Przypadki testowe dla systemu ELROJ oparte na słowach kluczowych Przypadek PT-001 sprawdza, co się stanie, gdy po dodaniu linii i jej kursu linia ta ponownie zostanie dodana. Weryfikuje to słowo CZY_JEST_LINIA. Słowo CZY_JEST_KURS sprawdza, czy kurs rzeczywiście został wpisany do rozkładu. Dodatkowo, tester sprawdza poprawność wyświetlenia informacji na ekranie – linia powinna istnieć, więc na ekranie o dwóch liniach powinna pojawić się odpowiednia informacja o dwóch kursach linii 103. Przypadek PT-002 sprawdza, czy w przypadku dwóch kursów o tym samym czasie kolejność wyświetlania linii

jest alfabetyczna. Przypadek PT-003 sprawdza poprawność wyświetlenia w przypadku, gdy linii ekranu jest więcej niż możliwych kursów. Jak widać, dzięki wykorzystaniu słów kluczowych tester ma dużą dowolność w tworzeniu różnych scenariuszy testowych. Definicje testów mogą być tworzone w różnych formatach. Na listingu 30.6 jest zawarta sekwencja słów w postaci pliku tekstowego, ale można wykorzystać także na przykład arkusz kalkulacyjny, tak jak to zostało pokazane na rysunku 30.11. W przypadku niektórych słów pewne ich parametry mogą być traktowane jako oczekiwany wynik, np. ostatnie parametry słów: SPRAWDŹ_EKRAN, CZY_JEST_LINIA oraz CZY_JEST_KURS.

Rysunek 30.11. Skrypt oparty na słowach kluczowych z wykorzystaniem programu Excel

30.5.8. Testowanie oparte na procesie

Opis podejścia. Podejście to jest podobne do testowania opartego na słowach kluczowych. Różnica dotyczy poziomu abstrakcji samych testów. W testowaniu opartym

na

procesie

skrypty

implementują

całe

scenariusze

testowe

odpowiadające poszczególnym przypadkom użycia. Skrypty te są parametryzowane danymi testowymi lub łączone w definicje testów wyższego poziomu. Z tak zdefinowanymi testami łatwiej jest sobie radzić, ponieważ w czytelny sposób reprezentują logiczne relacje między akcjami, np. „sprawdź status zamówienia” tuż po „złóż zamówienie” w testowaniu funkcjonalności, czy też „sprawdź status zamówienia” bez uprzedniego składania zamówienia w testowaniu odporności na błędy. Zalety. Wykorzystanie definicji testów opartych na procesie bądź scenariuszu pozwala definiować procedury testowe z perspektywy procesu biznesowego. Celem podejścia opartego na procesie jest implementacja tych wysokopoziomowych procesów za pomocą bibliotek testowych umożliwiających wykonanie szczegółowych kroków testowych. Podejście oparte na procesie pozwala nam na przeprowadzanie testowania zwłaszcza testów systemowych4.

na

wyższych poziomach,

Wady. Procesy biznesowe testowanego systemu mogą nie być dobrze rozumiane przez technicznego analityka testów. Problematyczne może być również tworzenie skryptów, jeśli wykorzystywane narzędzie nie wspiera logiki tych procesów biznesowych. Ponadto, aktualne pozostaje zastrzeżenie dotyczące trafnego doboru słów – to samo, co w podejściu opartym na słowach kluczowych. Trafny wybór procesów, przez wykorzystanie dobrych słów kluczowych, będzie z korzyścią i z zyskiem dla automatyzacji – dobre procesy będą wykorzystywane w definicjach innych procesów. Zły wybór procesów będzie nas kosztował czas i pieniądze na stworzenie rozwiązania niewykorzystywanego przez nikogo. Ponadto, źle zdefiniowany proces nie będzie w stanie wykryć określonych defektów. Przykład. Stosując podejście oparte na procesie do testowania sklepu internetowego można wyróżnić w szczególności następujące procesy: zaloguj się; potwierdź zamówienie (parametryzowane listą produktów); wypełnij dane dotyczące adresu dostawy (poprawne, niepoprawne);

zapłać kartą (ważną, nieważną, z poprawnym numerem, z błędnym numerem); sprawdź status zamówienia. Każdy z nich może być parametryzowany danymi podawanymi jako parametry tych procesów lub wczytywanych ze źródeł zewnętrznych. Ta ostatnia metoda będzie lepsza w przypadku procesu składania zamówienia złożonego z bardzo wielu elementów. Oto przykładowe testy, jakie może skonstruować tester: PT1. Zaloguj się, Potwierdź zamówienie, Zapłać kartą. Przypadek ten sprawdza, czy można złożyć zamówienie, nie wpisując adresu dostawy. PT 2. Zaloguj się, Wypełnij poprawne dane dotyczące adresu dostawy, Potwierdź zamówienie, Zapłać kartą z błędnym numerem. Przypadek sprawdza, czy da się zapłacić za zamówienie złą kartą kredytową. PT 3. Zaloguj się, Sprawdź status zamówienia. Przypadek sprawdza, czy można sprawdzić status zamówienia, którego się nie złożyło. PT 4. Zaloguj się, Wypełnij poprawne dane dotyczące adresu dostawy, Potwierdź zamówienie, Sprawdź status zamówienia, Zapłać poprawną kartą. Ten przypadek sprawdza tzw. happy path – oczekiwaną, poprawną sekwencję zdarzeń, składającą się na oczekiwany, poprawny przypadek użycia. Wymienione przypadki są przykładami testów dotyczących wysokopoziomowych procesów biznesowych wykonywanych przez testowany system. Nie interesują nas tu zagadnienia szczegółowe (takie jak np. zdublowana linia w rozkładzie jazdy z poprzedniego przykładu), ale całe procesy biznesowe, przeprowadzane od momentu zalogowania się użytkownika do systemu do momentu zamknięcia aplikacji. testowanie zgodne z procesem (ang. process-compliant testing) – testowanie, które postępuje zgodnie ze zbiorem zdefiniowanych reguł (procesów), np. zdefiniowanych przez zewnętrzne ciało takie jak komitet standaryzujący

30.5.9. Testowanie oparte na modelu

Opis podejścia. Testowanie oparte na modelu najczęściej rozumie się jako automatyczne generowanie przypadków testowych – w przeciwieństwie do ich automatycznego wykonywania. Omówione wcześniej podejścia – od metody „nagraj i odtwórz”, do automatyzacji opartej na procesie są metodami pozwalającymi na automatyczne wykonywanie przypadków testowych. W każdym z tych podejść musieliśmy najpierw ręcznie stworzyć skrypt, który definiuje bądź to przypadek, bądź to procedurę testową. Podejście oparte na modelu wykorzystuje formalne (bądź semi-formalne) modele, które abstrahują od konkretnej technologii tworzenia skryptów. Dla każdej z nich (opisanych w p. 30.5.3–30.5.8) istnieją różne metody generowania testów. Zalety. Testowanie oparte na modelu, przez wykorzystanie abstrakcji, umożliwia skupienie się na esencji testowania: logice biznesowej, danych, scenariuszach, konfiguracjach itp. rzeczach, które podlegają testowaniu. Modele mogą być wykorzystywane wielokrotnie, w różnych środowiskach i dla różnych technologii. W przypadku zmiany wymagań jedyna rzecz po stronie testerów, która wymaga zmian to modyfikacja modelu. Zbiór przypadków testowych będzie generowany automatycznie. Techniki projektowania testów są włączone w generatory przypadków testowych. Wady. Efektywne stosowanie podejścia opartego na modelu wymaga zaawansowanej wiedzy dotyczącej modelowania. Modelowanie przez abstrahowanie od interfejsów, danych czy zachowania testowanego systemu może być trudne. Ponadto, jeśli idzie o rozwój narzędzi wspierających to podejście, a także o samo zagadnienie modelowania systemów, wciąż jesteśmy na początku drogi, choć dziedzina ta rozwija się niezwykle intensywnie. Stosowanie podejścia opartego na modelu wymaga również zmian w procesach testowych. Zwykle należy utworzyć w zespole testowym rolę projektanta testów, który ma wiedzę w zakresie modelowania a także formalnych technik projektowania testów. Wreszcie, same modele muszą podlegać procesowi weryfikacji pod kątem ich jakości, a także procesowi zarządzania zmianą. Przykład. Przykład wykorzystania podejścia opartego na modelu podaliśmy podrozdziale 5.3 dla problemu testowania układu elektronicznego konwertującego słowa w kodzie Graya na ich pozycję w tym kodzie. Modelem systemu jest układ kombinacyjny złożony z bramek XOR. Tworząc automatycznie przypadki testowe, możemy np. wymagać, aby: w

pokryć każde wejście do każdej bramki każdą możliwą wartością przynajmniej raz; pokryć każde wyjście z każdej bramki każdą możliwą wartością przynajmniej raz; wymusić określone wyjścia z układu itp. Te kryteria mogą być spełnione przez wykorzystanie odpowiednich technik tworzenia przypadków testowych. Proces ten jest całkowicie zautomatyzowany. Jego wynikiem może być gotowy kod źródłowy skryptów wykonujących odpowiednie przypadki testowe.

30.5.10. Języki i notacje dla definicji testów Zarówno inżynier ds. automatyzacji, jak i techniczny analityk testów mają do dyspozycji wiele języków i notacji wykorzystywanych do tworzenia skryptów testowych. Rodzaj użytego narzędzia zależy od kontekstu oraz od upodobań poszczególnych testerów. Narzędzia te możemy podzielić na dwie zasadnicze grupy: języki/notacje ogólnego przeznaczenia oraz języki/notacje dedykowane dla testowania. Podział ten, przeprowadzony dla najczęściej wykorzystywanych rozwiązań, jest przedstawiony na rysunku 30.12.

Rysunek 30.12. Języki i notacje stosowane w automatyzacji testów Rysunek uwzględnia jedynie języki skryptowe, ale w automatyzacji testów można wykorzystywać również języki programowania ogólnego przeznaczenia, takie jak C, C++, Java czy C#. Języki skryptowe mają tę przewagę nad językami programowania, że pozwalają zwykle na szybszą implementację danej czynności [240]. Jednak skrypty są interpretowane, dlatego działają nieco wolniej niż skompilowane programy w binarnej formie. Nie jest to wielka wada – po

pierwsze, obecne komputery są na tyle szybkie, że różnice w czasie wykonania są praktycznie pomijalne; po drugie, Pettichord [241] zauważa, że w testach automatycznych większość czasu wykonywania skryptu to czekanie na odpowiedź od testowanego systemu; skrypty zwykle służą do wykonywania prostych czynności, takich jak wykonywanie programów z odpowiednimi parametrami, wczytywanie danych ze źródeł zewnętrznych itp. język skryptowy (ang. scripting language) – język programowania, w którym są pisane skrypty testowe wykorzystywane przez narzędzie do uruchamiania testów, np. narzędzie rejestrująco-odtwarzające; języki skryptowe są językami interpretowanymi, tzn. kod interpretowany linia po linii

źródłowy

nie

jest

kompilowalny,

lecz

Omówimy teraz krótko języki i notacje z rysunku 30.12. Perl. Język skryptowy wykorzystywany głównie do operacji na danych tekstowych, ze względu na bardzo efektywny silnik wspierający obsługę wyrażeń regularnych. Perl jest znany z tego, że określone zadanie można w nim wykonać na bardzo wiele zupełnie różnych sposobów, co dla jednych jest zaletą, a dla innych wadą. Nie wszyscy lubią Perla, ponieważ skrypty w nim pisane mogą przyjmować bardzo nieczytelną formę. Fragment przykładowego (niezbyt czytelnego) skryptu napisanego w Perlu jest przedstawiony na listingu 30.7.

%a = map map

{/^\d{6}$/ ? $_ : [ split(/\|/,$_,2)]} {split(/=/,$_,2)}

grep {/^\d{6}=[^|]+\|/}; Listing 30.7. Fragment skryptu napisanego w Perlu Python. Język ogólnego przeznaczenia, wspierający wiele różnych paradygmatów programowania (funkcyjne, strukturalne, obiektowe). W porównaniu z Perlem ma o wiele bardziej czytelną składnię. Python jest językiem o typowaniu dynamicznym, to znaczy typ mają wartości, a nie zmienne. Ruby5. Język ogólnego przeznaczenia, wysokiego poziomu. Programy w nim pisane są zwykle o wiele krótsze niż analogiczne programy tworzone w językach takich jak C++ czy Java. Jest językiem dynamicznie typowanym. Mimo że w pełni

obiektowy, pozwala na stosowanie innych paradygmatów programowania, np. imperatywnego czy funkcyjnego. SQL. SQL (ang. Structured Query Language) to strukturalny język zapytań opracowany w IBM, wykorzystywany do tworzenia i modyfikowania baz danych. Pozwala na interakcję z bazą przez tworzenie zapytań. Z punktu widzenia testera język ten może być wykorzystywany w testowaniu opartym na danych lub tam, gdzie dane testowe i zarządzanie nimi są istotną częścią procesu testowego. Jeśli dane testowe są gromadzone w bazie, to tester, aby je uzyskać, musi skonstruować odpowiednie zapytanie w SQL. Istnieje wiele różnych odmian tego języka, związanych z różnymi systemami bazodanowymi. Najpopularniejsze systemy to: MySQL, PostgreSQL, Oracle, DB2. Bash. Bash jest tzw. językiem powłoki systemów unixowych, czyli językiem, przez który możemy komunikować się z systemem operacyjnym takim jak Unix, Linux czy Mac OS. Istnieją porty tego języka dla systemu Windows (przez Cygwin i MinGW), DOS (projekt DJGPP) czy – przez wykorzystanie różnego rodzaju emulatorów – Novell NetWare i Android. Jest to język wsadowy, co oznacza, że operacje są wykonywane przez wysyłanie do systemu pojedynczych instrukcji. Istnieje również możliwość zapisywania/wykonywania sekwencji instrukcji bashowych do/z pliku. Plik taki jest wtedy nazywany skryptem. Dla testera bash jest wygodny jako element uprzęży testowej. Ze skryptów basha można wywoływać programy, tworzyć i modyfikować pliki (np. logi testowe) i katalogi, przeprowadzać wyszukiwanie tekstowe itd. Skrypty bashowe mogą wykonywać wszystkie polecenia, jakie oferuje dany system operacyjny. Bardzo pomocne są m.in. takie narzędzia jak: grep (wyszukiwanie tekstu), awk (wyszukiwanie i przetwarzanie wzorców w plikach lub strumieniach danych), diff (wypisywanie różnic w plikach), cron (harmonogramowanie zadań) oraz mnóstwo innych. W szczególności można wykorzystywać polecenia związane z obsługą sieci, monitorowaniem/debugowaniem programów, operacjami na plikach itp. Bash umożliwia strumieniowe wykonywanie poleceń (ang. pipeline), co oznacza, że wyjście zwrócone w wyniku działania jednego polecenia może być wejściem dla innej komendy. VBScript. Język skryptowy stworzony przez Microsoft, oparty na Visual Basic. Jego zaletą jest szybki interpreter oraz możliwość użycia w wielu środowiskach opartych na rozwiązaniach Microsoftu. Z punktu widzenia testera VBScript to bardzo ważny język – jest on bowiem wykorzystywany w oprogramowaniu HP

Unified Functional Testing (dawniej HP QuickTest Professional, QTP) – jednym z najczęściej używanych narzędzi do automatyzacji testów. Na listingu 30.8 przedstawiono zaczerpnięty ze strony www.qtphelp.com przykładowy skrypt stosowany w testowaniu opartym na danych lub słowach kluczowych. Wyszukuje on określone ciągi znaków w arkuszu Excelowym. Skrypt taki można łatwo zmodyfikować tak, aby zamiast wyszukiwać, pobierał dane wiersz po wierszu i np. uruchamiał testowany program z tymi danymi jako argumentami.

Dim appExcel, objWorkBook, objSheet, columncount, rowcount, 'stwórz obiekt aplikacji dla Excela Set appExcel = CreateObject("Excel.Application") 'otwórz arkusz Set objWorkBook = appExcel.Workbooks.open("c:\DaneTestowe.xls") 'wybierz zakładkę arkusza Set objSheet = appExcel.Sheets("Global") 'zlicz ile jest używanych kolumn i wierszy w arkuszu columncount = objSheet.usedrange.columns.count rowcount = objSheet.usedrange.rows.count 'ustal ciąg do wyszukania Find_Details="Report" 'wyszukuj w pętli po wszystkich komórkach For a = 1 to rowcount For b = 1 to columncount fieldvalue = objSheet.cells(a, b) If cstr(fieldvalue)= Cstr(Find_Details) Then msgbox cstr(fieldvalue) Exit For End If Next

Next Listing 30.8. Skrypt VBScript wyszukujący dane w arkuszu kalkulacyjnym OCL. OCL (ang. Object Constraint Language) to nakładka na UML. Jest to deklaratywny język zapisu ograniczeń w modelu obiektowym. Instrukcje OCL wzbogacają diagramy UML o wszelkiego rodzaju ograniczenia, których nie da się opisać standardową notacją UML. OCL może również służyć jako język nawigacji dla modeli opartych na strukturach grafowych. Innym ciekawym zastosowaniem OCL może być wykorzystanie go jako tzw. przybliżonej wyroczni (ang. imprecise oracle), czyli wyroczni testowej, która może zwrócić przybliżony oczekiwany wynik. Rozważmy przykład z [242]. Dana jest metoda calcArea klasy Rectangle obliczająca pole prostokąta o zadanych długościach boków: public int Rectangle::calcArea(int width, int height); ... Ograniczenia tej metody są nałożone na parametry wymiarów prostokąta, które muszą być większe od zera (warunek wejścia, ang. pre-condition), a wynikiem ma być pole tej figury, czyli iloczyn wysokości i szerokości (warunek wyjścia, ang. post-condition). Ograniczenia można zapisać w OCL tak, jak na listingu 30.9.

context Rectangle::calcArea pre: width > 0 pre: height > 0 post: result = width * height Listing 30.9. Kod w OCL definiujący ograniczenia dla metody calcArea Post-warunek dla tych ograniczeń (ostatnia linijka kodu OCL) może służyć jako wyrocznia testowa. Generator testów może łatwo wybrać dane wejściowe spełniające ograniczenia (pre-warunki), wykonać testowaną metodę i sprawdzić, czy zwrócony wynik jest taki sam, jak ten z post-warunku. Opisany przykład jest dosyć trywialny. W większości przypadków będziemy mieć do czynienia z wyroczniami przybliżonymi, czyli takimi, w których warunki

wejściowe i/lub wyjściowe zapisane będą w formie nierówności. Dla tego przykładu warunek wyjściowy mógłby mieć postać post: result >= 0 O wiele łatwiej jest zdefiniować taki warunek niż warunek dokładny, ale powoduje to, że nie możemy zweryfikować poprawności obliczeń. Możemy tego dokonać jedynie w sposób przybliżony, tzn. stwierdzić, czy rezultat jest liczbą dodatnią (bo taką powinien być). Jeśli w metodzie calcArea błędnie zaimplementujemy instrukcję zwracającą wartość jako width/height (dzielenie zamiast mnożenia), to dla dowolnych dodatnich wartości wejściowych warunek wyjścia będzie spełniony i wyrocznia nie będzie mogła wykryć błędu. Jeśli jednak ta sama instrukcja zostanie błędnie zaimplementowana jako width – height (odejmowanie zamiast mnożenia), to dla testów w których width < height otrzymamy wynik ujemny. Wyrocznia będzie mogła stwierdzić, że jest to wynik niepoprawny, ponieważ warunek wyjściowy jest nieprawdziwy. TTCN-3. TTCN-3 (ang. Testing and Test Control Notation) jest silnie typowanym językiem skryptowym używanym w testowaniu zgodności (ang. conformance testing) dla systemów telekomunikacyjnych. Wspiera testowanie oparte na danych, definiując infrastrukturę łączącą abstrakcyjne skrypty testowe z konkretnymi danymi opisującymi środowiska komunikacyjne. Chociaż przeznaczony głównie do zastosowań telekomunikacyjnych, TTCN-3 może być używany również w innych dziedzinach. Na przykład projekt AUTOSAR (www.autosar.org) promuje wykorzystanie TTCN-3 w testowaniu w przemyśle samochodowym. TSL. TSL (ang. Test Specification Language) jest – jak sama nazwa wskazuje – językiem specyfikacji testów. Specyfikacja w TSL jest podzielona na trzy części: parametry, zmienne środowiskowe oraz rezultaty. Generator TSL tworzy przypadki testowe na podstawie kombinacji wejść i warunków środowiskowych, wyspecyfikowanych w sekcji zawierającej rezultaty. VDM-SL. VDM (ang. Vienna Development Method), czyli Wiedeńska Metoda Produkcji [243] to formalna metoda produkcji systemów informatycznych. Obecnie VDM składa się z wielu technik i narzędzi bazujących na języku formalnej specyfikacji VDM-SL. Język ten ma rozszerzenie, VDM++, wspierające modelowanie systemów współbieżnych i obiektowo orientowanych. VDM-SL umożliwia modelowanie w paradygmacie funkcyjnym oraz opartym na maszynie stanowej.

XML. XML (ang. eXtensible Markup Language) to tekstowy język znaczników, czyli język definiowania struktury dokumentu w formacie czytelnym zarówno dla użytkownika, jak i maszyny. Z punktu widzenia testera jest on głównie użyteczny do definiowania i wykorzystywania danych testowych o bardziej skomplikowanej strukturze. XML jest ponadto używany w protokole SOAP (ang. Simple Object Access Protocol), wykorzystywanym przez webserwisy, służącym do zdalnego wywoływania obiektów. Na listingu 30.10 przedstawiono przykładowy plik XML zawierający rozkład jazdy dla systemu ELROJ z Dodatku A, złożony z jednej linii o numerze 100, mającej dwa kursy, w minutach 24 i 49.

100

24

49

Listing 30.10. Plik XML z rozkładem jazdy dla systemu ELROJ UML. UML (ang. Unified Modeling Language) jest językiem służącym do modelowania w obszarze inżynierii oprogramowania. Został zaprojektowany, aby ustandaryzować podejście do wizualizacji projektu systemu. UML oferuje wiele różnego rodzaju diagramów opisujących system zarówno statycznie (np. diagram klas), jak i dynamicznie (np. diagram aktywności). Diagramy wchodzące w skład standardu UML 2.5 są pokazane na rysunku 30.13. Istnieją narzędzia pozwalające na automatyczną generację testów, a nawet kodu testowego, na podstawie diagramów UML. Czytelnika zainteresowanego zagadnieniem testowania systemów obiektowych, w szczególności na podstawie diagramów UML, odsyłamy do obszernej monografii Bindera [162].

UTP. UTP (ang. UML Testing Profile) to rozszerzenie notacji UML wspierające projektowanie, wizualizację, specyfikację, analizę, konstrukcję oraz dokumentowanie

wszelkich

artefaktów

związanych

z

testowaniem

(www.utp.omg.org). Tak jak UML, UTP jest niezależny od konkretnych języków programowania i może być wykorzystany w wielu różnych zastosowaniach biznesowych. Trzy podstawowe cechy UTP to: minimalność – tam, gdzie jest to możliwe, zostały wykorzystane struktury UMLowe; klarowność testowych;



UTP

umożliwia

czytelne

wydzielenie

artefaktów

rozszerzalność – UTP może być rozszerzony o elementy specyficzne dla konkretnych domen biznesowych, np. telekomunikacji, lotnictwa czy medycyny. UTP może być wykorzystany w podejściu opartym na modelu. Testy generuje się wtedy na podstawie diagramów UTP. UTP mapuje się na TTCN-3, co pozwala automatycznie generować skrypty testowe napisane w tym języku.

Rysunek 30.13. Diagramy UML 2.5 Z. Notacja Z służy do opisu i modelowania systemów komputerowych. Jest to formalizm matematyczny oparty na teorii zbiorów Zermelo–Fraenkla, rachunku lambda oraz logice predykatów pierwszego rzędu. Wyrażenia w notacji Z są typowane6. Notacja Z jest oparta na modelu. System zwykle jest reprezentowany przez stany, ich wartości oraz operacje pozwalające na zmianę stanu. Istnieją narzędzia (np. Fastest [244]) pozwalające na automatyzację testowania opartego na modelu

zapisany w notacji Z. Programy te otrzymują na wejście model, a następnie analizują każdą zdefiniowaną w nim operację, aby uzyskać lub wygenerować abstrakcyjny przypadek testowy. Burton [245] opisuje technikę automatycznej generacji testów ze specyfikacji Z opartych na kryteriach definiowanych przez użytkownika. Hierons [246] zaproponował algorytm generujący podział dziedziny wejściowej na podstawie specyfikacji i pokazał, że podział ten może być wykorzystany zarówno do automatycznego tworzenia przypadków testowych, jak i modelu maszyny stanowej kontrolującej proces testowy. Legeard [247] podał technikę automatycznej generacji testów dla metody wartości brzegowych (patrz podrozdz. 8.2) na podstawie specyfikacji Z lub B (patrz dalej). Metoda unika tworzenia wszystkich stanów maszyny stanowej, wykorzystując tzw. DNF (ang. Disjunctive Normal Form) – formuły wyliczone na podstawie specyfikacji. Istnieje wsparcie narzędziowe dla tej metody, BZ-Testing Tool [248]. Framework TTF (ang. Test Template Framework) [249] wykorzystuje notację Z do przeprowadzania testowania czarnoskrzynkowego. Wzorzec testów (ang. test template) to schemat Z opisujący zbiór przypadków testowych. Reprezentuje on zbiór wartości równoważnych ze względu na przyjętą technikę testową (np. klasy abstrakcji w metodzie podziału na klasy równoważności) wykorzystaną do generacji wzorca. Inną notacją powiązaną z Z jest ASDL (ang. Architectural Style Description Language). W pracy [250] jest proponowane wykorzystanie ASDL do analizy i testowania oprogramowania na poziomie architektury. Opis ASDL składa się z trzech elementów: wzorców (ang. templates), ustawień (ang. settings) i jednostek (ang. units). Każdy z tych elementów opisuje generyczny schemat Z, niezmienniczy względem wszystkich styli. B. B jest formalną metodą opisu systemu komputerowego, opartą na tzw. notacji maszyny abstrakcyjnej. W porównaniu do notacji Z jest bardziej niskopoziomowa i skupiona bardziej na udoskonalaniu kodu niż tylko na formalnym specyfikowaniu systemu. Notacja B ma bardzo silne wsparcie narzędziowe pozwalające – tak jak w przypadku Z – na wspieranie specyfikacji, projektowania, dowodzenia poprawności oraz generację kodu. Na przykład w pracy [251] autorzy proponują metodę generowania przypadków testowych na podstawie specyfikacji modelowanych w notacji B.

Event-B. Notacja Event-B (www.event-b.org/) jest nową, prostszą wersją notacji B. Różnica w stosunku do B dotyczy przede wszystkim podejścia do modelowania: Event-B preferuje inkrementacyjny

styl modelowania.

Pomysł

ten został

zaczerpnięty z współczesnych języków programowania mających IDE (ang. Integrated Development Environment), czyli zintegrowane środowisko wytwarzania7. Dla Event-B takie środowisko dostarcza narzędzie Rodin-tool [252]. Jest ono łatwe w użyciu i rozszerzalne. Skupia się na kwestiach związanych z modelowaniem systemu. W szczególności Rodin jest rozszerzeniem Eclipsa (IDE dla Javy).

30.6. Katalog narzędzi testowych Na rynku jest dostępnych mnóstwo komercyjnych i darmowych narzędzi wspierających testowanie praktycznie we wszystkich jego aspektach. W rozdziale tym opiszemy tylko niektóre z nich. Wybór podyktowany jest głównie popularnością narzędzi wśród testerów i kierowników testów. Narzędzia są pogrupowane według ich rodzajów, ale jest to podział umowny, ponieważ wiele z nich przynależy do więcej niż jednej kategorii. Każde z narzędzi zostało umieszczone w kategorii, która najbardziej się z nim kojarzy.

30.6.1. Narzędzia do zarządzania testami HP Quality Center – potężna, komercyjna aplikacja do zapewniania jakości oprogramowania. Obejmuje zarządzanie wymaganiami, zarządzanie testami, testowanie w metodykach zwinnych, testowanie automatyczne, zarządzanie incydentami oraz testowanie procesów biznesowych [253]. IBM Rational Quality Manager – według informacji ze strony producenta jest to „centrum zespołowego, zorientowanego biznesowo zarządzania jakością oprogramowania i systemów, które może współdziałać praktycznie z każdą platformą i wspomagać każdy typ testów. Oprogramowanie to umożliwia zespołom bezproblemowe współużytkowanie informacji oraz szybsze realizowanie projektów dzięki mechanizmom automatyzacji. Oferuje także mechanizmy raportowania metryk projektów, przydatne przy podejmowaniu decyzji dotyczących wydawania produktów prac programistycznych” [254].

TestLink – darmowe narzędzie internetowe do zarządzania testami, wspomagające proces zapewniania jakości oprogramowania. Oferuje wsparcie w zakresie zarządzania przypadkami i suitami testowymi, planami testów, projektami testowymi a także użytkownikami. różnorodnych raportów i statystyk [255].

Umożliwia

tworzenie

30.6.2. Narzędzia do wykonywania/organizacji testów HP Unified Functional Testing (dawniej HP QuickTest Professional) – umożliwia testowanie funkcjonalne oraz regresyjne. Wykorzystuje VBScript jako język skryptowy. Pozwala testować wszystkie trzy warstwy operacyjne aplikacji: interfejs, warstwę usług oraz warstwę bazy danych. Wspiera testowanie oparte na danych. Może być rozszerzalne za pomocą pluginów. Współpracuje z HP Quality Center [256]. Fit/FitNesse – FitNesse to backendowe IDE dla narzędzia Fit (Framework for Integrated Test), automatyzującego testy klientów przez wspomaganie komunikacji klientów, analityków, testerów i deweloperów [257]. W Fit klienci dostarczają przykłady poprawnego działania testowanego programu. Przykłady te są tłumaczone przez programistów na tzw. stałe testy (ang. test fixtures), będące kodem formalnie sprawdzającym poprawność przesłanych przez klienta przykładów. Przykłady te są transformowane do postaci htmlowych tabel przy wykorzystaniu arkuszy kalkulacyjnych, takich jak MS Excel. Fit podczas sprawdzania dokumentu tworzy jego kopię, zaznaczając przykłady na czerwono, żółto lub zielono, w zależności od tego, czy zachowanie oprogramowania było poprawne czy błędne. soapUI – darmowe narzędzie do przeprowadzania testów funkcjonalnych, regresyjnych oraz obciążeniowych. Stosowane głównie do testowania webserwisów opartych na SOAP i REST, ale może być wykorzystane także do testowania baz danych czy aplikacji internetowych [258]. Robot Framework – generyczny framework do automatyzacji wykonywania testów akceptacyjnych oraz testów dla projektów wytwarzania sterowanego testami akceptacyjnymi (ang. Acceptance Test-driven Development, ATDD). Ma łatwą w użyciu tabelaryczną składnię dla danych testowych oraz wykorzystuje podejście oparte na słowach kluczowych. Jest rozszerzalne za pomocą bibliotek napisanych w Pythonie lub Javie. Użytkownicy mogą również tworzyć nowe

słowa kluczowe na podstawie już istniejących. Projekt jest wspierany przez Nokia Networks [259].

30.6.3. Narzędzia do testowania mutacyjnego, posiewu usterek i wstrzykiwania błędów Jester. Darmowy framework do testowania mutacyjnego [260]. Umożliwia użytkownikowi definiowanie własnych operatorów mutacyjnych, jednak ogranicza je do prostych podmian łańcuchów znaków w kodzie źródłowym. Wadą Jestera jest to, że nie przeprowadza analizy kodu, co może prowadzić do tworzenia niekompilowalnych mutantów. MuJava (μ-Java). Narzędzie do przeprowadzania testów mutacyjnych dla programów napisanych w Javie [261], o wiele bardziej wydajne od Jestera. Wykorzystuje dwie techniki tworzenia mutantów: opartą na modyfikacji kodu źródłowego oraz opartą na modyfikacji bytecode’u w celu dokonania zmian strukturalnych testowanego programu. Zawiera bardzo bogaty katalog operatorów mutacyjnych, zarówno strukturalnych, jak i obiektowych.

30.6.4. Narzędzia do testów bezpieczeństwa Metasploit – narzędzie wspierające przeprowadzanie testów penetracyjnych [262]. beStorm – komercyjne narzędzie do przeprowadzania testów bezpieczeństwa [263]. Spirent Studio Security – komercyjne narzędzie do przeprowadzania testów bezpieczeństwa komponentów sieciowych. Potrafi monitorować poprawność protokołów komunikacyjnych, replikować ataki typu DDoS8, pokrywać system testami negatywnymi, które często kończą się awarią systemu, symulować rzeczywiste ataki, jakie pojawiły się w ostatnim czasie w internecie. Wspiera języki TCL, Python i Perl oraz różne narzędzia do automatyzacji [264]. HP Application Security Center – narzędzie do testowania bezpieczeństwa stron www [265].

30.6.5. Symulatory i emulatory Symulacji i emulacji podlegać może sprzęt, oprogramowanie oraz niemal każda dziedzina biznesowa. Dlatego istnieje bardzo wiele symulatorów i emulatorów

i nie sposób wymienić nawet samej pełnej listy kategorii tych narzędzi. symulator (ang. simulator) – urządzenie, program komputerowy lub system używany podczas testowania, który przy zadanym zbiorze wejść zachowuje się lub działa podobnie do danego systemu [7], [225] emulator (ang. emulator) – urządzenie, program komputerowy lub system przyjmujący takie same wejścia i generujący takie same wyjścia jak dany system [7]

się

Różnica między symulatorem a emulatorem jest taka, że symulator zachowuje w sposób podobny do symulowanego systemu (i zwykle jest

zaimplementowany w sposób zupełnie odmienny od symulowanego systemu), natomiast emulator działa dokładnie tak samo jak emulowany system, jest jego wierną repliką9. Na stronie http://en.wikipedia.org/wiki/Simulation_software Czytelnik znajdzie odnośniki do stron opisujących m.in.: języki symulacji; narzędzia do symulacji ciągłej; narzędzia do symulacji dyskretnej; narzędzia symulujące oprogramowanie; narzędzia symulujące układy elektroniczne; narzędzia symulujące architektury komputerów; narzędzia

symulujące działanie mikroarchitekturę komponentów

sprzętowych; narzędzia do symulacji sieci; narzędzia do symulacji procesów; narzędzia do wirtualnego prototypowania.

30.6.6. Narzędzia do analizy statycznej i dynamicznej Dmalloc – debuger pamięci dla języka C. Wykrywa wycieki pamięci [266]. Daikon – program wykrywający możliwe niezmienniki w programach. Działa dla C, C++, Javy, Perla, a także dla arkuszy kalkulacyjnych [267]. Gcov – program do mierzenia pokrycia kodu źródłowego (na licencji GNU) [268].

Intel Thread Checker – narzędzie do analizy błędów w wątkach, umożliwiające wykrycie potencjalnych zakleszczeń lub tzw. wyścigów danych (ang. data race) w wielowątkowych aplikacjach dla systemów Windows i Linux [269]. Parasoft Jtest – analizuje błędy wykonania, aby wykryć wyścigi (ang. race conditions), wycieki pamięci i zasobów oraz miejsca podatne na ataki bezpieczeństwa [270]. Sonarqube – aplikacja do statycznej analizy kodu, znana wcześniej pod nazwą Sonar [271], [272]. Wykorzystuje inne popularne narzędzia do analizy statycznej, takie jak Squid, Checkstyle, PMD, FindBugs, Cobertura i Clover: Squid oblicza metryki obiektowe dla kodu; Checkstyle weryfikuje zgodność kodu źródłowego standardami;

z ustalonymi

PMD przegląda kod w poszukiwaniu potencjalnych defektów, martwego kodu, nieoptymalnego kodu, złożonych wyrażeń, zduplikowanego kodu; FindBugs przeprowadza analizę statyczną, w szczególności pod kątem podatności na ataki, efektywność, złośliwy kod i zgodność ze standardami kodowania; Cobertura i Clover obliczają metryki pokrycia kodu testami.

30.6.7. Narzędzia do testów wydajnościowych JMeter – darmowe narzędzie do przeprowadzania testów obciążeniowych oraz pomiaru wydajności. Oryginalnie stworzone do testowania aplikacji internetowych, jednak może być używane również do testowania innych programów. Działa dla wielu różnych typów serwerów oraz protokołów sieciowych i komunikacyjnych, takich jak: http, https, SOAP, ftp, JDBC, LDAP, JMS, smtp(s), pop3, imap(s), MongoDB, tcp [273]. HP Load Runner – komercyjne narzędzie dla testów obciążeniowych. Pozwala na interaktywne nagrywanie oraz wykorzystywanie skryptów. Może być stosowane w testowaniu aplikacji desktopowych, mobilnych oraz w chmurze. Pozwala na integrację z różnymi IDE oraz z systemami ciągłej integracji. Oferuje możliwość przeprowadzania analiz i generowania raportów o wydajności [274]. Spirent Studio Performance – pozwala przeprowadzać testy wydajnościowe, symuluje ruch sieciowy oraz sprawdza efektywność polityki QoS (Quality of

Service) dla zarządzania ruchem sieciowym [275]. FunkLoad – narzędzie do testów funkcjonalnych i obciążeniowych dla aplikacji internetowych [276].

30.6.8. Narzędzia typu capture & replay Selenium – popularne narzędzie do nagrywania i odtwarzania testów dla aplikacji internetowych. Pozwala również na samodzielne pisanie skryptów testowych [277], [278]. Abbot – narzędzie typu capture & replay dla

graficznych interfejsów

użytkownika. Pozwala na tworzenie testów jednostkowych dla GUI w formie metod Javy. Abbot umożliwia również specyfikację testów w języku XML [279].

30.6.9. Narzędzia do testów jednostkowych Istnieje wiele frameworków wspierających testy jednostkowe dla różnych języków programowania. Narzędzia te zwykle umożliwiają łatwe zarządzanie testami, graficzną wizualizację stopnia pokrycia oraz wykorzystanie namiastek czy sterowników. Wzorcem przemysłowym dla tego typu narzędzi stał się JUnit, wspomagający testowanie jednostkowe programów pisanych w Javie. W tabeli 30.2 są podane przykładowe narzędzia dla niektórych języków programowania. Tabela 30.2. Frameworki wspierające testowanie jednostkowe

Język

Narzędzia

ActionS cript FlexUnit, FUnit AppleS cript AS Unit, AS Tes t C

AceUnit, API S anity Checker, Paras oft C/C++ tes t, Check, CUnit,

C++

API S anity Checker, CppUnit, Goog le Tes t, mock++, Paras oft C/C++ tes t

Clojure

clojure.tes t (wbudowana), S peclj

Cobol

COBOLUnit

Delphi

DUnit, DUnit2, DUnitX

Erlang

EUnit

Fortran

FUnit, FRUIT, pFUnit

Groovy

S pock, Gmock

Has kell

HUnit, HUnit-Plus

Java

Jtes t, JUnit [280], Tes tNG [281]

JavaS cript

JS Unit, Unit.js

MATLAB

mlUnit, matlab.unittes t (częś ć Matlaba R2013a)

.NET / C#

cs Unit, MbUnit, NUnit, S pecFlow (framework BDD), xUnit.net

Objective-C

OCUnit, XCUnit

Perl

Tes t::Clas s , Tes t::Unit (znane jako PerlUnit), Tes t::Able

PHP

PHPUnit, PHP Unit Tes ting Framework, S impleTes t, S napTes t (dla PHP 5+)

Python

unittes t (znane jako PyUnit, częś ć biblioteki s tandardowej), py.tes t

Ruby

Tes t::Unit

S cilab

tes t_run (dos tarczany wraz ze S cilabem)

S cheme

S chemeUnit

S imulink

TPT (Time Partition Tes ting )

S mallTalk

S Unit

S QL

utMyS QL (dla MyS QL), S QL Developer (dla PL/S QL), db2Unit (dla DB2)

Tcl

tclUnit

V is ual Bas ic S implyV BUnit XML, XLS T

XMLUnit (plug in dla JUnit i NUnit)

30.6.10. Testowanie w metodyce BDD Cucumber – wspiera testowanie akceptacyjne w paradygmacie (programowania sterowanego zachowaniem, opisanego w p. 4.1.7.4).

BDD

Sam Cucumber jest napisany w Ruby, ale umożliwia tworzenie testów akceptacyjnych w innych językach [282]. JBehave – narzędzie podobne do Cucumber, ale oparte na Javie [283]. RSpec – framework wspierający BDD dla Ruby, inspirowany JBehave [284].

30.6.11. Narzędzia do testowania opartego na modelu Spec Explorer [285] – narzędzie będące rozszerzeniem MS Visual Studio wspierające tworzenie modeli zachowania oprogramowania, analizowania ich przy użyciu graficznej wizualizacji, sprawdzanie poprawności tych modeli oraz generowania na ich podstawie przypadków testowych. KLEE [236] – przez semantyczną analizę kodu (również w trakcie wykonywania) potrafi dobrać dane wejściowe tak, aby pokryć odpowiednie elementy kodu. Leirios Test Generator [286] – automatycznie generuje przypadki testowe na podstawie specyfikacji pisanej w notacji B. Fastest – implementuje technikę TTF (Test Template Framework) tworzenia modeli na podstawie języka specyfikacji Z. Automatycznie generuje przypadki testowe [287]. MaTeLo (Markov Test Logic) – tworzy model użycia na podstawie wymagań funkcjonalnych, wybiera odpowiednią strategię testowania na podstawie poziomu dojrzałości systemu oraz automatycznie generuje skrypty i przypadki testowe. Wykorzystuje model probabilistyczny zwany łańcuchami Markowa do opisu testowanego systemu. Wspiera testowanie funkcjonalne, integracyjne i akceptacyjne [288].

Qtronic [289] – tworzy przypadki testowe na podstawie modelu zapisanego we własnej notacji, QML (Qtronic Modeling Language).

30.6.12. Narzędzia do śledzenia defektów, zarządzania incydentami i raportowania Bugzilla – darmowa, bardzo popularna internetowa aplikacja do śledzenia defektów [290]. JIRA – narzędzie do zarządzania incydentami i śledzenia defektów. Ma możliwość integracji z repozytorium kodu, co sprawia, że jest również wygodnym narzędziem dla deweloperów. Wspiera produkty tworzone w metodykach zwinnych. Ma bogate możliwości analiz i raportowania [291]. Mantis – podobne do Bugzilli narzędzie do śledzenia defektów. Mimo prostego interfejsu oferuje bardzo bogatą funkcjonalność [292]. Trac – napisane w Pythonie, darmowe internetowe narzędzie do zarządzania incydentami [293]. IBM Rational ClearQuest – komercyjne narzędzie wspierające proces zarządzania incydentami. Ma możliwość integracji z wieloma narzędziami do automatyzacji testów. Ma system śledzenia defektów [294]. Redmine – darmowy, internetowy system śledzenia incydentów. Integruje się z systemami zarządzania kodem, rozszerzalny za pomocą wtyczek [295].

30.7. Wdrażanie narzędzi w organizacji Odpowiedzialność za wybór i wdrożenie narzędzi w organizacji spoczywa na kierowniku testów. Oczywiście w ramach tego procesu korzysta on z pomocy testerów, inżynierów jakości oraz innych potencjalnych interesariuszy projektu wdrożeniowego.

30.7.1. Cykl życia narzędzia Typowy cykl życia narzędzia jest przedstawiony na rysunku 30.14.

Rysunek 30.14. Cykl życia narzędzia Szczegółowe czynności wchodzące w skład fazy analizy i wyboru zostały opisane w podrozdziale 30.1. Faza pozyskania obejmuje zakup narzędzia, jego instalację oraz wyznaczenie administratorów narzędzia czy osób odpowiedzialnych

za

jego

utrzymanie.

Jeśli

narzędzie jest

tworzone na

indywidualne potrzeby organizacji, to faza obejmuje także negocjacje oraz przygotowanie kontraktu. Po instalacji narzędzie zaczyna być wykorzystywane i wchodzi w fazę wsparcia i utrzymania. W tej fazie pojawia się najwięcej problemów związanych z niepoprawnym działaniem narzędzia. Ze względu na zmiany, jakie dokonują się w organizacji oraz jej procesach, a także w samym narzędziu (np. nowa wersja, łatki, zaprzestanie wsparcia) często musi być ono dostosowane do nowych warunków. Odbywa się to w fazie ewolucji. Ważne jest, aby kierownik testów w sposób ciągły monitorował korzyści oraz koszty związane z użyciem narzędzia, utrzymaniem i ewolucją. Może się okazać, że narzędzie jest zbyt kosztowne i trzeba będzie zaprzestać jego używania. Wtedy cykl życia przechodzi do fazy wycofania. Faza ta następuje również, gdy narzędzie okaże się być przestarzałe i zupełnie nie pasujące do nowej rzeczywistości. W każdym z wymienionych przypadków należy zapewnić przejęcie funkcjonalności złomowanego narzędzia przez inne narzędzie. Dane wykorzystywane i generowane przez wycofywane narzędzie muszą zarchiwizowane lub migrowane do nowego środowiska.

być

30.7.2. Użycie narzędzi open source Narzędzia open source’owe są dostępne praktycznie dla każdego obszaru procesu testowego. Wybór darmowego narzędzia może być kuszący, ze względu na zerowe koszty pozyskania. Należy jednak pamiętać, że koszty początkowe to nie wszystko. Większość narzędzi darmowych nie ma wsparcia technicznego, a producenci zwykle nie oferują szkoleń w zakresie wdrażania czy używania. W fazie analizy należy wziąć to pod uwagę przy obliczaniu ROI. Tabela 30.3. Przykładowe problemy z używaniem narzędzi

Opis sytuacji

Możliwe rozwiązania

Interfejs narzędzia nie ws półpracuje z innymi używanymi narzędziami

Uważnie zapoznać s ię z informacjami o wydaniu (ang . release notes), s próbować wykorzys tać wers ję demons tracyjną narzędzia, którą można użyć dla rzeczywis teg o s ys temu, zapewnić ws parcie od dos tawcy/producenta, przeg lądać fora dys kus yjne poś więcone narzędziu

Zmiana elementu ś rodowis ka tes towaneg o s ys temu nie jes t ws pierana przez narzędzie (np. ins talacja nowej wers ji Javy)

Zs ynchronizować aktualizacje ś rodowis ka produkcyjneg o i tes toweg o z narzędziem automatyzacji tes tów

Obiekt GUI nie może być przechwycony (narzędzie nie może g o przeanalizować)

Używać dobrze znanych, bezpiecznych technolog ii i obiektów podczas produkcji, przeprowadzić projekt pilotażowy przed zakupem narzędzia

Narzędzie wyg ląda na bardzo s komplikowane (np. ma dużo opcji, a chcemy użyć tylko niektórych)

S próbować og raniczyć zbiór funkcjonalnoś ci np. przez zakup innej licencji, znaleźć inne, alternatywne narzędzie s kupiające s ię bardziej na interes ującej nas funkcjonalnoś ci

Konflikt z innymi Zapoznać s ię z informacjami o wydaniu przed s ys temami (np. po ins talacją, uzys kać od dos tawcy potwierdzenie, ins talacji narzędzia że narzędzie nie wpłynie na inne s ys temy, zapytać

jakiś prog ram przes tał działać)

użytkowników odpowiednich forów dys kus yjnych

Wpływ na tes towany s ys tem (np. tes towany s ys tem działa wolniej po wdrożeniu narzędzia)

Użyć narzędzia, które nie będzie wymag ało zmiany czy dos tos owania tes towaneg o s ys temu (np. ins talacji dodatkowych bibliotek)

Dos tęp do kodu (np. narzędzie zmienia kod źródłowy tes towaneg o s ys temu)

Użyć narzędzia, które nie będzie wymag ało zmiany czy dos tos owania tes towaneg o s ys temu (np. ins talacji dodatkowych bibliotek)

Og raniczone zas oby, g łównie w s ys temach wbudowanych

Zapoznać s ię z informacjami o wydaniu i uzys kać zapewnienie od dos tawcy, że narzędzie nie wpłynie neg atywnie na ś rodowis ko (np. nie zajmie zbyt dużo pamięci), zapytać użytkowników odpowiednich forów dys kus yjnych

Uaktualnienia (np. uaktualnienie nie powoduje mig racji danych lub wymag a inneg o ś rodowis ka)

Przetes tować uaktualnienie w ś rodowis ku tes towym, uzys kać zapewnienie od dos tawcy, że uaktualnienie będzie działać, zapoznać s ię z wymag aniami technicznymi dla aktualizacji i zdecydować, czy aktualizacja jes t warta nas zeg o wys iłku, pos zukać ws parcia na odpowiednich forach dys kus yjnych

Bezpieczeńs two (np. narzędzie

wymag a informacji Inżynier ds . automatyzacji powinien podpis ać niedos tępnych dla umowę o poufnoś ci (ang . Non-Disclosure inżyniera ds . Agreement, NDA) automatyzacji) Niekompatybilnoś ć między różnymi ś rodowis kami i platformami (np. automatyzacja nie działa na ws zys tkich platformach)

Implementować tes ty automatyczne tak, aby zmaks ymalizować niezależnoś ć narzędzia od ś rodowis ka, minimalizując jednocześ nie kos zty użycia wielu narzędzi

narzędzie o otwartym kodzie (ang. open source tool) – narzędzie programistyczne, które jest dostępne dla wszystkich potencjalnych użytkowników w postaci kodu źródłowego, zwykle przez internet; użytkownicy mogą, zwykle przez określoną licencję, badać, zmieniać, ulepszać oraz – czasami – dystrybuować takie oprogramowanie Zaletą narzędzi open source’owych jest to, że można je zwykle modyfikować dostosowując do specyficznych potrzeb organizacji lub na potrzeby integracji z innymi wykorzystywanymi w firmie rozwiązaniami. Kierownik testów musi dobrze rozumieć zasady licencjonowania oprogramowania. Czasami licencja może zabraniać jakichkolwiek modyfikacji kodu źródłowego pozyskanej aplikacji.

30.7.3. Przykładowe problemy związane z użyciem narzędzi Sylabus [233] podaje wiele przykładowych problemów związanych z wykorzystaniem narzędzi w organizacji. Przytaczamy je tutaj w całości (patrz tab. 30.3). opis wydania, opis przekazywanego produktu, raport przekazania elementu testowego (ang. release note, (test) item transmittal report) – dokument

opisujący elementy testowe, ich konfiguracje, status oraz inne ważne informacje przekazywane przez programistów interesariuszom, na początku fazy testowania; również informacje przekazywane klientowi wraz z oprogramowaniem lub jego nową wersją [5]

1 Formalnie, debugowanie nie jest częścią testowania, ale procesy te są ze sobą na tyle ściśle powiązane, że narzędzia do usuwania defektów można zaliczyć do kategorii narzędzi wspierających proces testowy. 2 W Dodatku C Czytelnik znajdzie więcej informacji na temat teorii prawdopodobieństwa. 3 W Dodatku C Czytelnik znajdzie więcej informacji na temat wyrażeń regularnych. 4 Końcowe testy akceptacyjne nie powinny podlegać automatyzacji – nie ma to większego

sensu, skoro

ich celem

ma

być walidacja

systemu przez

użytkownika. Nic jednak nie stoi na przeszkodzie, aby automatyzować testy akceptacyjne dla poszczególnych fragmentów systemu (np. modułów), jeśli taki poziom testów jest zdefiniowany i wymagany w procesie testowym, a oczekiwania użytkownika wobec tego fragmentu systemu są zrozumiałe i stanowią zamknięty, kompletny zbiór wymagań. 5 Prawidłowa wymowa słowa „ruby”, oznaczającego w języku angielskim słowo „rubin” to /'ru:bi/, a nie często spotykana w Polsce /'rabi/. Nazwa jest nawiązaniem do języka Perl (z ang. perła). 6 Dzięki stosowaniu typów unika się paradoksów występujących w naiwnej teorii zbiorów. 7 Przykładowymi IDE dla Javy są Eclipse oraz NetBeans. 8 DDoS (ang. Distributed Denial of Service) – typ ataku na system komputerowy lub usługę sieciową polegający na uniemożliwieniu działania przez zajęcie wszystkich wolnych zasobów tego systemu lub usługi. Przeprowadzany przez wiele rozproszonych komputerów. 9 Na przykład emulator komputera Atari 65XE na komputerze PC działa dokładnie tak samo jak Atari. Natomiast program komputerowy będący symulatorem lotu myśliwcem nie odzwierciedla idealnie rzeczywistego lotu.

Więcej na: www.ebook4all.pl

Część VI Udoskonalanie procesu testowego

Never stop investing. Never stop improving. Never stop doing something new Bob Parsons

Część szósta książki jest poświęcona zagadnieniom doskonalenia procesu testowego. Nigdy nie jest tak dobrze, żeby nie mogło być lepiej. Niezależnie od poziomu dojrzałości organizacji, zawsze będą istniały jakieś rzeczy w procesie testowym, które będzie można usprawnić. Doskonalenie jest procesem ciągłym i tak naprawdę nigdy się nie kończy. W rozdziale 31 omówimy kontekst udoskonalania procesu oraz pięć różnych spojrzeń na jakość, która jest nieodzownym elementem procesu ulepszania. Opiszemy też generyczne procesy udoskonalania, takie jak koło Deminga czy model IDEAL. Rozdziały 32 i 33 są poświęcone dwóm zasadniczym technikom doskonalenia: podejściu opartemu na modelu oraz podejściu analitycznemu. Krótki rozdział 34 zawiera wskazówki pomocne w podjęciu decyzji o tym, jakie podejście wybrać w swojej organizacji. W rozdziale 35 wykorzystamy omówiony wcześniej model IDEAL do szczegółowego opisania procesu doskonalenia testów. Osiągnięcie sukcesu wymaga jednak dobrej organizacji oraz właściwych ludzi mających zestaw specyficznych umiejętności. Aspektom tym jest poświęcony rozdział 36. Na zakończenie, w rozdziale 37 omówimy czynniki sukcesu dla projektu doskonalenia testowania. Większość materiału tej części książki jest oparta na dwóch źródłach: sylabusie [296] oraz monografii [219].

31. Kontekst udoskonalania procesu

31.1. Po co udoskonalać? Branża IT rozwija się w niesamowitym tempie. To, co dziś jest standardem, za rok będzie bezużyteczne. Powstają coraz to nowe technologie, metody wytwarzania oprogramowania, czy też modele zarządzania jego produkcją. Wszystko to odnosi się również do testowania oprogramowania. Potrzeby biznesowe (np. szybkość wydania produktu na rynek w celu zyskania przewagi konkurencyjnej) wymuszają intensywniejszą pracę, wykorzystywanie najnowszych technologii oraz zatrudnianie ludzi z wysoko specjalistyczną wiedzą. Jednocześnie, nikt nie chce i nie może poświęcać jakości tworzonego oprogramowania na ołtarzu wymagań rynku czy biznesu. Jakość ta bowiem bezpośrednio przekłada się na sukces organizacji. Aby ją zwiększyć, firmy zwykle skupiają się na usprawnianiu procesów produkcyjnych. Często wykorzystywanym modelem jest CMMI (Capability Maturity Model Integration), który postrzega się jako standard przemysłowy w tej dziedzinie. Mimo że testowanie pochłania średnio 30–40% całkowitych kosztów projektu, modele usprawniania procesów produkcyjnych, takie jak CMMI, nie poświęcają mu zbyt wiele uwagi. W konsekwencji społeczność testerska stworzyła wiele własnych modeli poświęconych usprawnianiu procesu testowego, takich jak TMMi czy TPI Next. Zawierają one cenne wskazówki oraz reguły postępowania, których wdrożenie ma w zamyśle ulepszyć proces testowania. Typowe powody udoskonalania procesu testowego to [219], [296]: zwiększenie jakości tworzonego produktu; skrócenie czasu produkcji (np. przez integrację testowania z procesem produkcji oprogramowania) przy jednoczesnym zachowaniu poziomu

jakości; oszczędność pieniędzy (np. przez obniżenie kosztów awarii); zwiększenie efektywności pracy; usprawnienie zbierania metryk i raportowania; lepsza przewidywalność jakości produktu;

Rysunek 31.1. Kontekst udoskonalania procesu testowego lepsze spełnianie potrzeb i wymagań klienta/użytkownika; osiągnięcie odpowiedniego poziomu dojrzałości1; zapewnienie zgodności z określonymi standardami. Praktycznie każdy projekt udoskonalania jest oparty na generycznym modelu Deminga lub też jego dookreśleniu, np. IDEAL. Metody te opiszemy w podrozdziale 31.4. Usprawnianie2 procesu testowego odbywa się zwykle w kontekście udoskonalania procesów organizacyjnych oraz biznesowych (patrz rys. 31.1) zarządzanych przez następujące, przykładowe metodyki: Total Quality Management (TQM); ISO 9000; Excellence Model (European Foundation for Quality Management); Six Sigma; Lean.

Omówimy je dokładniej w podrozdziale 32.3. Usprawnianie procesu testowego może również odbywać się w nieco węższym kontekście – udoskonalania procesu produkcji oprogramowania. Może on być zarządzany według następujących metodyk: CMMI (Capability Maturity Model Integration); ISO/IEC 15504; ITIL; TSP (Team Software Process). Metodyki te omówimy w podrozdziale 32.3. Osoba odpowiedzialna za usprawnianie procesu testowego musi być świadoma kontekstu, w jakim działa. Powinna znać nie tylko techniki udoskonalania samego testowania, lecz także wymienione metody. doskonalenie procesu (ang. process improvement) – 1) program działań zaprojektowany tak, aby poprawić wydajność i dojrzałość procesów w organizacji; 2) wyniki takiego programu [197] SPI, doskonalenie procesu tworzenia (wytwarzania, produkcji) oprogramowania (ang. Software Process Improvement, SPI) – program czynności zaprojektowanych w celu poprawy wydajności i dojrzałości procesów wytwarzania oprogramowania w organizacji oraz wyniki tego programu [197] Wartości istotne dla doskonalenia procesu testowego zostały zgrabnie ujęte przez van Veenendaala [297] w formie manifestu, analogicznego do manifestu Agile. manifest doskonalenia procesu testowego (ang. test process improvement manifesto) – deklaracja, nawiązująca do manifestu Agile, określająca wartości ważne dla poprawy procesu testowego; są to: − elastyczność ponad szczegółowe procesy; − najlepsze praktyki ponad szablony; − ukierunkowanie na praktykę ponad ukierunkowanie na procesy; − przeglądy koleżeńskie ponad departamenty zarządzania jakością; − orientacja na cele ponad orientację na model

31.2. Co można udoskonalić? W poprzednim podrozdziale stwierdziliśmy, że udoskonalanie testowania odbywa się w kontekście procesu wytwórczego, organizacyjnego i biznesowego. Istnienie tego kontekstu oznacza, że ulepszanie może wykraczać poza sam proces testowy3. Na rysunku 31.2 zobrazowano przykładowe obszary podlegające doskonaleniu w ramach usprawniania testowania [28]. Proces. Cykl życia testowania powinien podlegać usprawnianiu w całości. Oznacza to, że ulepszaniem należy objąć wszystkie fazy: planowania i kontroli, przygotowania, specyfikowania i wykonania testów, a także fazę zamykania testowania. Doskonalenie powinno dotyczyć wszystkich poziomów testowania stosowanych w organizacji.

Rysunek 31.2. Przykładowe obszary udoskonalania testowania Infrastruktura i narzędzia. Infrastruktura obejmuje: środowisko testowe, narzędzia testowe oraz pozostałe narzędzia będące na wyposażeniu biura. Zwykle spotyka się trzy typy środowisk testowych: środowisko „laboratoryjne” dla testów niskiego poziomu, środowisko dla testów systemowych oraz środowisko symulujące warunki produkcyjne dla przeprowadzania testów akceptacyjnych. Często nie mamy możliwości wyboru środowiska testowego lub narzędzi, ponieważ istnieją już i są stosowane w organizacji. Udoskonalanie w postaci wymiany części środowiska lub narzędzi można przeprowadzać po dokładnej analizie opłacalności takiego działania.

Organizacja. Organizacja testów to reprezentacja zależności między funkcjami, zdolnością oraz aktywnościami testowymi ukierunkowanych na przeprowadzenie zorganizowanych testów [298]. W kontekście ich usprawniania należy wziąć pod uwagę następujące aspekty: operacyjny (działający) proces testowy; zorganizowanie testowania; zarządzanie testowaniem; personel oraz jego umiejętności i szkolenia. Umiejętności. Obejmują znajomość i wykorzystanie różnorakich typów testów, technik projektowania testów, narzędzi wspomagających automatyzację testowania czy modeli predykcyjnych. W ramach doskonalenia umiejętności można zapewnić odpowiednie szkolenia lub certyfikacje.

31.3. Spojrzenia na jakość Jakość jest pojęciem rozumianym wielorako. Nawet w samym procesie testowania można na nią patrzeć z różnych perspektyw. Przed wdrożeniem procesu udoskonalania należy określić, które spojrzenie na jakość będzie punktem wyjścia do działań usprawniających. Trienekens i van Veenendaal [299], opierając się na pracy Garvina [300], podają następujących pięć „typów” jakości: oparta na produkcie; oparta na użytkowniku; oparta na wytwarzaniu; oparta na wartości; transcendentna.

31.3.1. Jakość oparta na produkcie Z punktu widzenia testowania jakość oparta na produkcie jest głównie związana z testowaniem niefunkcjonalnym. Oznacza to, że jakość produktu jest oceniana na podstawie tzw. charakterystyk jakościowych: niezawodności, pielęgnowalności, przenaszalności, użyteczności itd. To spojrzenie na jakość jest często spotykane w projektach dotyczących systemów o znaczeniu krytycznym. W podejściu

zapewniania jakości opartej na produkcie można wykorzystać istniejące normy i standardy, takie jako ISO 9216 [3] czy nowsza norma ISO 25000. Charakterystyki jakościowe omówiliśmy dokładnie w rozdziale 16. jakość oparta na produkcie (ang. product-based quality) – spojrzenie na jakość, w którym jest ona oparta na dobrze zdefiniowanym zbiorze atrybutów (charakterystyk) jakościowych; atrybuty te muszą być mierzalne ilościowo w sposób obiektywny; różnice w jakości produktów tego samego typu można odnieść do sposobu, w jaki atrybuty jakościowe zostały zaimplementowane w tych produktach [300]

31.3.2. Jakość oparta na użytkowniku W spojrzeniu tym jakość dotyczy dopasowania produktu do oczekiwań użytkownika (ang. fitness for use). Oznacza to, że powinna być ona określana przez użytkownika wykorzystującego produkt w konkretnej sytuacji biznesowej. Od jej rodzaju zależy sposób definiowania jakości. Podejście to jest silnie związane z testowaniem funkcjonalnym. W testowaniu jakość tę zapewnia się przez proces walidacji oraz testów akceptacyjnych i jeśli przyjmujemy to spojrzenie na jakość, czynności testowe powinny skupiać się na tych dwóch obszarach, a także na procesie inżynierii wymagań. Można tu wykorzystać normę ISO 25010 [2] opisującą model jakości użytkowej. jakość oparta na użytkowniku (ang. user-based quality) – spojrzenie na jakość traktowaną jako zdolność do zaspokojenia potrzeb, pragnień i wymagań użytkownika; podejście to jest warunkowe i zależne od kontekstu, gdyż różne właściwości biznesowe wymagają różnych jakości produktu [300]

31.3.3. Jakość oparta na wytwarzaniu Spojrzenie to w centrum zainteresowania stawia proces wytwórczy, tzn. czynności specyfikacji, projektu i budowy systemu. Poziom jakości opartej na wytwarzaniu zależy od stopnia, w jakim wymagania zostały zaimplementowane w końcowym produkcie. Jakość opartą na wytwarzaniu można zapewniać przez wykorzystanie inspekcji, przeglądów, metod statystycznych, modeli defektów, modeli efektywności usuwania defektów. Z punktu widzenia testowania

spojrzenie oparte na wytwarzaniu jest silnie związane z fazą weryfikacji oraz testów systemowych i integracyjnych. jakość oparta na wytwarzaniu (ang. manufacturing-based quality) – spojrzenie na jakość, w którym jest ona mierzona przez stopień, w jakim produkt lub usługa jest zgodna z zamierzonym projektem i wymaganiami; jakość wynika z użytego procesu [300]

31.3.4. Jakość oparta na wartości W podejściu opartym na wartości jakość jest determinowana przez proces decyzyjny uwzględniający kompromisowe rozwiązanie między czasem, wysiłkiem i kosztem. Przykładem projektu, w którym takie spojrzenie może być wykorzystane, jest produkcja innowacyjnego oprogramowania, gdzie głównym elementem sukcesu jest czas wydania produktu na rynek (aby nie ubiegła nas konkurencja). W takim przypadku można poświęcić część funkcjonalności lub zgodzić się na obniżenie wartości niektórych charakterystyk jakościowych, aby zdążyć z wydaniem produktu na czas. Z punktu widzenia testowania spojrzenie to jest silnie związane z podejściem testowania opartego na ryzyku. Nie możemy przetestować wszystkiego, dlatego skupiamy się przede wszystkim na tych testach, które minimalizują możliwie najwięcej najpoważniejszych ryzyk. Jakość oparta na ryzyku stawia w centrum uwagi wartość biznesową tworzonego produktu. jakość oparta na wartości (ang. value-based quality) – spojrzenie na jakość, gdzie jest ona definiowana przez cenę; jakość produktu lub usługi rozumiemy jako to, co dostarcza pożądane wykonanie przy akceptowalnym koszcie; jakość jest określana w procesie decyzyjnym interesariuszy z uwzględnieniem kompromisów między czasem, wysiłkiem i kosztem [300]

31.3.5. Jakość transcendentna Podejście transcendentne do jakości zakłada, że może być ona rozpoznana tylko przez indywidualną percepcję czy odczucia poszczególnych osób. Choć definicja ta w ogóle nie jest operacyjna (nie podaje sposobu pomiaru), nie należy jej odrzucać. Często może być punktem wyjścia do stosowania bardziej precyzyjnych podejść.

Dobrym

przykładem

transcendentnego

spojrzenia

na

jakość

może

być

„grywalność” gier komputerowych: uchwycenie czynników, które powodują, że jedne gry są niezwykle popularne, a inne okazują się klapą, jest niezwykle trudne. Jednak szczegółowe badania, np. zachowań graczy lub wykorzystujące różnego rodzaju kwestionariusze, mogą w sposób bardziej precyzyjny zidentyfikować konkretne czynniki decydujące o sukcesie (a więc o wysokiej jakości transcendentnej). Z punktu widzenia testowania jakość transcendentną bardzo ciężko zmierzyć. Można wykorzystać testy alfa i beta oraz zwinne metodyki wytwórcze, w których intensywny kontakt z klientem oraz częste wydania mogą dobrze wpasować się w proces „odgadywania” indywidualnych odczuć użytkowników. jakość transcendentna, jakość oparta na przekraczaniu oczekiwań (ang. transcendent-based quality) – spojrzenie na jakość, gdy nie może być ona dokładnie zdefiniowana, ale rozpoznajemy ją, gdy ją widzimy lub jesteśmy świadomi jej braku, gdy nie występuje; zależy ona od percepcji oraz odczucia jednostki lub grupy w stosunku do produktu [300]

31.4. Generyczny proces udoskonalania W tym podrozdziale opiszemy trzy generyczne procesy udoskonalania. Praktycznie wszystkie metodyki ulepszania procesu testowego są oparte na jednym z tych modeli.

31.4.1. Cykl Deminga–Shewarta (PDCA) Cykl Deminga–Shewarta, zwany również cyklem Deminga lub kołem Deminga [301] opisuje ciągły proces udoskonalania. Model ten ma postać cyklu PDCA (akronim od angielskich słów: Plan, Do, Check, Act) złożonego z czterech etapów: zaplanuj (Plan) – w fazie tej rozpoznaje się możliwość potencjalnego usprawnienia procesu; określa się cel udoskonalania w postaci charakterystyk jakościowych, kosztów i poziomów usług; wykonaj (Do) – faza, w której zmiana jest wdrażana i testowana, najlepiej na zasadzie projektu pilotażowego;

sprawdź (Check) – faza, wprowadzonych zmian;

w

której

mierzy

się

efektywność

zastosuj (Act) – faza wyciągania wniosków z przeprowadzonych zmian: jeśli zmiana nie spowodowała pozytywnych skutków, to cały cykl przeprowadza się ponownie dla innego planu; jeśli zmiana przyniosła pozytywny skutek, to należy zastosować zmianę w całej organizacji oraz przeprowadzić ponownie cały cykl, biorąc pod uwagę doświadczenie z właśnie ukończonego cyklu. W fazie planowania przeprowadza się ocenę istniejącego procesu, aby w ogóle dowiedzieć się, co można udoskonalić. Wiele modeli udoskonalania procesu testowego (zwłaszcza tzw. modele referencyjne procesu z reprezentacją etapową) służy do takiej oceny, określają one liczbowo dojrzałość procesu. ocena procesu (ang. process assessment) – uporządkowana ocena procesów wytwarzania oprogramowania w organizacji w stosunku do modelu wzorcowego [302] Cykl Deminga oraz schematyczne ukazanie go w kontekście usprawniania są przedstawione na rysunku 31.3. cykl Deminga, koło Deminga, cykl Deminga–Shewarta (ang. Deming cycle) – iteracyjny, czterokrokowy (zaplanuj – wykonaj – sprawdź – zastosuj) proces rozwiązywania problemów, mający zastosowanie w doskonaleniu procesów [301]

Rysunek 31.3. Cykl Deminga i jego zastosowanie w kontekście udoskonalania Przykład. Kierownik testów w firmie Qualium IT decyduje o wprowadzeniu nowego, nieobecnego do tej pory elementu w procesie zapewniania jakości – inspekcji formalnych. Firma prowadzi 6 równoległych projektów. W każdym z nich jest zespół testowy. W jednym projekcie testy obejmują od 20 do 200 modułów.

Faza cyklu

Opis

Bieżąca efektywnoś ć znajdowania defektów, liczona jako liczba wykrytych podczas produkcji defektów na 1000 linii kodu, wynos i ś rednio 65 z odchyleniem s tandardowym 20 (s ą dos tępne dane his toryczne dla 400 dotychczas s tworzonych modułów). Wiadomo, że całkowita Zaplanuj efektywnoś ć og raniczania defektów4 wynos i około 85%, a więc is tnieje możliwoś ć poprawy proces u znajdowania defektów podczas produkcji. Kierownik tes tów okreś la cel jakoś ciowy: zwięks zenie w ciąg u 6 mies ięcy efektywnoś ci znajdowania defektów do 70/KLOC/moduł (ś rednio) przez zas tos owanie ins pekcji formalnych. Kierownik przeprowadza s zkolenia z ins pekcji dla jedneg o z zes połów tes towych, a nas tępnie zarządza – tylko w ramach teg o jedneg o zes połu – przeprowadzanie Wykonaj ins pekcji dla 30% tes towanych modułów. Dla każdeg o modułu s ą zbierane dane o liczbie wykrytych defektów na 1000 linii kodu. Kierownik tes tów porównuje dane o efektywnoś ci znajdowania defektów dla modułów, wobec których zas tos owano ins pekcje (ś rednio 70,5/KLOC/moduł z odchyleniem 23) z modułami tes towanymi bez ins pekcji S prawdź (ś rednio 66,1/KLOC/moduł z odchyleniem 26). S tos ując odpowiedni tes t s tatys tyczny, kierownik s twierdza, że

zmiana jes t is totna s tatys tycznie, a cel jakoś ciowy zos tał os iąg nięty. Kierownik zarządza s zkolenia z ins pekcji dla ws zys tkich zes połów, wprowadza reg uły ich s tos owania (np. dotyczące Zas tos uj teg o, które moduły powinny jej podleg ać) i zbiera dane o efektywnoś ci znajdowania defektów we ws zys tkich projektach.

31.4.2. IDEAL Struktura usprawniania IDEAL [303] jest uszczegółowieniem cyklu Deminga opisanego w poprzednim podrozdziale. Metoda jest pokazana schematycznie na rysunku 31.4. Nazwa IDEAL jest akronimem pochodzącym od pierwszych liter angielskich słów: Initiating (Rozpoczęcie), Diagnosing (Diagnozowanie), Establishing (Ustanowienie), Acting (Działanie), Learning (Uczenie się, wyciąganie wniosków). Omówimy teraz krótko każdą z faz procesu IDEAL. Inicjalizacja procesu. Proces rozpoczyna się fazą Inicjalizacji. Ustanawia się początkową infrastrukturę dla udoskonalania, definiuje role i odpowiedzialności związane z procesem ulepszania, a także określa odpowiednie zasoby. W fazie Inicjalizacji jest ponadto tworzony plan procesu udoskonalania, który ma prowadzić organizację przez fazy inicjalizacji, diagnozowania oraz ustanowienia. Jest uzyskiwane wsparcie kierownictwa dla przeprowadzenia całego procesu, jak również zaangażowanie zasobów i ludzi, którzy teraz lub w przyszłości będą w jakiś sposób zaangażowani w proces udoskonalania. Diagnozowanie. Ta część modelu IDEAL kładzie podwaliny pod kolejne fazy procesu. Jest tworzony plan działania, biorąc pod uwagę wizję organizacji, strategiczne plany biznesowe, wnioski z poprzednich działań w zakresie udoskonalania (ang. lessons learned), główne wyzwania stojące przed organizacją, a także długoterminowe cele. Są przeprowadzane różnego rodzaju szacowania tak, aby określić bieżący stan organizacji oraz jej procesów (ang. baseline), a także stan pożądany. Wyniki i zalecenia powstałe na skutek tych działań są uzgadniane z istniejącymi lub planowanymi czynnościami doskonalącymi pod kątem ewentualnego ich włączenia do planu działania.

Ustanowienie (planu). W fazie tej dokonuje się priorytetyzacji kwestii, co do których organizacja zamierza zastosować czynności doskonalące. Tworzy się strategie dla wdrożenia rozwiązań, a plan akcji uzupełnia się, biorąc pod uwagę te same czynniki,

Rysunek 31.4. Model IDEAL co w fazie Diagnozowania (wizję, strategię, plany, wnioski, cele). Z celów ogólnych określonych w fazie Inicjalizacji wyprowadza się cele mierzalne. Będą one włączone do ostatecznej wersji planu akcji. Ponadto, są definiowane metryki potrzebne do monitorowania postępu, a zasoby i ludzi przydziela się do konkretnych zadań i szkoleń. Plan akcji będzie sterował czynnościami w ramach procesu udoskonalania. Mogą być również stworzone wzorce planów działań taktycznych. Wzorce te przesyłane są odpowiednim grupom zaangażowanym w proces doskonalenia celem ich wypełnienia, a następnie realizacji. Działanie. W fazie Działania są tworzone i wdrażane rozwiązania problemów dotyczące obszaru usprawniania, które zidentyfikowano w fazie Diagnozowania.

Rozwiązania pilotażowe testują nowe, zmienione procesy. Na podstawie tego, jak sprawdziły się możemy stwierdzić, czy jesteśmy gotowi na wdrożenie ich w całej organizacji. Uczenie się (wyciąganie wniosków). Faza uczenia się potrzebna jest po to, aby kolejny cykl IDEAL był jeszcze bardziej efektywny. Do tego momentu wszystkie rozwiązania zostały zastosowane, wnioski wyciągnięte, a metryki mierzące wydajność i stopień osiągnięcia celów – zebrane. Wszystkie te informacje są gromadzone w repozytorium i będą służyły jako cenne źródło informacji w kolejnych fazach cyklu oraz w innych projektach. Zebrane dane można również wykorzystać do oceny przyjętej strategii. Przed rozpoczęciem kolejnego cyklu można dzięki temu wprowadzić poprawki do stosowanych metod, technik czy infrastruktury. IDEAL (ang. IDEAL) – model doskonalenia organizacji, który może być wykorzystywany jako tzw. mapa drogowa przy rozpoczynaniu, planowaniu i wdrażaniu ulepszeń; nazwa IDEAL jest akronimem od angielskich nazw pięciu faz tego modelu: Initializing (Rozpoczęcie), Diagnosis (Diagnozowanie), Establishing (Ustanowienie), Acting (Działanie) oraz Learning (Uczenie się) IDEAL jest oparty na dwóch komponentach: strategicznym i taktycznym. Na komponent strategiczny składają się fazy Inicjalizacji i Uczenia się, na komponent taktyczny – fazy Diagnozowania, Ustanowienia i Działania. Strategia dotyczy długookresowych celów organizacji, taktyka to plan działania, nastawiony na osiągnięcie celów krótkookresowych, będących składowymi celu „głównego”.

31.4.3. Podstawowe zasady doskonałości (Fundamental Concepts of Excellence) Podstawowe zasady doskonałości (ang. Fundamental Concepts of Excellence) [304] to zbiór zasad, których spełnienie pozwala osiągnąć trwałą doskonałość w każdej firmie, niezależnie od jej profilu. Podstawowe zasady są używane w modelach doskonałości organizacyjnej do pomiaru dziewięciu kryteriów, opisanych w punkcie 32.2.3. Model został opracowany przez Europejską Fundację Zarządzania Jakością i może być wykorzystany nie tylko jako metodyka udoskonalania procesu, lecz także jako model zarządzania jakością. Model definiuje osiem podstawowych zasad doskonałości:

tworzenie wartości dodanej dla klientów – doskonałe organizacje nieustannie dostarczają wartość dodaną klientom przez rozumienie, antycypowanie i spełnianie potrzeb, oczekiwań i możliwości; tworzenie

trwałej

przyszłości



doskonałe

organizacje

mają

pozytywny wpływ na otaczający świat przez zwiększanie ich wydajności z równoczesnym polepszaniem warunków ekonomicznych, środowiskowych i socjalnych społeczności, na które oddziałują; rozwój potencjału organizacyjnego – doskonałe organizacje zwiększają swój potencjał przez efektywne zarządzanie zmianą w ramach swoich ograniczeń oraz ponad nimi; spożytkowanie kreatywności i innowacji – doskonałe organizacje generują zwiększoną wartość oraz poziom wydajności przez ciągłe udoskonalanie i innowację, wykorzystując potencjał kreatywności interesariuszy; wizjonerskie, inspirujące i integrujące przywództwo – doskonałe organizacje mają liderów kształtujących przyszłość przez tworzenie inspirujących idei oraz potrafiących integrować wokół nich ludzi; zwinne zarządzanie – doskonałe organizacje mają zdolności do szybkiego identyfikowania oraz efektywnej i wydajnej odpowiedzi na możliwości i zagrożenia; sukces dzięki talentowi ludzi – doskonałe organizacje cenią swoich pracowników oraz tworzą kulturę zachęcającą do osiągania celów całej organizacji oraz indywidualnych; trwałość nadzwyczajnych rezultatów – doskonałe organizacje osiągają nadzwyczajne, trwałe rezultaty, spełniające zarówno krótko-, jak i długookresowe cele interesariuszy.

31.5. Przegląd podejść do udoskonalania Podejścia usprawniania procesów można generalnie podzielić na cztery grupy. Podział ten jest przedstawiony na rysunku 31.5. Omówimy teraz krótko te cztery podejścia, natomiast konkretne metody (oznaczone na rysunku 31.5 w białych prostokątach) przedstawimy w kolejnych rozdziałach.

31.5.1. Podejścia oparte na modelu Wykorzystanie modelu to najczęstsza forma udoskonalania. Modele, zwłaszcza te uznane, istniejące przez długi czas, są sprawdzone i godne zaufania. Główną zasadą leżącą u podstaw podejść opartych na modelu jest to, że zdolność procesu (organizacji) jest zależna od stopnia jego dojrzałości, rozumianej w terminach możliwości kontroli oraz przewidywalności wykonywanych działań. W zakresie udoskonalania procesu tworzenia oprogramowania standardem przemysłowym stały się dwa modele: CMMI oraz ISO/IEC 15504. model dojrzałości (ang. maturity model) – ustrukturalizowany zbiór elementów opisujący pewne aspekty dojrzałości organizacji, stanowiący również pomoc w definiowaniu i rozumieniu procesów w organizacji; model dojrzałości często dostarcza ujednoliconą terminologię, wspólną wizję i strukturę dla ustalania priorytetów dla działań doskonalących Modele udoskonalania procesu testowania można podzielić na dwie grupy, ze względu na sposób ich wykorzystania: modele referencyjne procesu; modele referencyjne zawartości. Modele referencyjne procesu definiują zbiór dobrych praktyk oraz wskazują dokładną drogę ulepszania procesu. Zwykle modele te wyróżniają wiele stopni dojrzałości organizacji. Modele referencyjne procesu są normatywne i narzucają sekwencję czynności doskonalących proces. Przykładami modeli referencyjnych procesu są TMMi (Test Maturity Model integration) [305], TPI Next (Test Process Improvement) [306] będący nową wersją modelu TPI [307] oraz rzadziej spotykany TIM (Test Improvement Model) [308]. Istnieją dwa podejścia określające sposób osiągania poziomu dojrzałości: reprezentacja etapowa; reprezentacja ciągła.

Rysunek 31.5. Przegląd metod udoskonalania

W reprezentacji etapowej model opisuje kolejne poziomy dojrzałości. Osiągnięcie danego poziomu wymaga wykonania określonych czynności (w tym czynności wymaganych na niższych poziomach dojrzałości). Na przykład TMMi definiuje 5 poziomów dojrzałości. Reprezentacja etapowa jest zrozumiała – zawsze wiadomo, w którym momencie procesu doskonalenia się znajdujemy i co jeszcze musimy wykonać, aby wejść na kolejny, wyższy poziom. Jednocześnie, jej stosowanie może wytworzyć negatywne wrażenie o poziomie dojrzałości procesu: jeśli spełnione jest tylko jedno z 20 wymagań dla poziomu dojrzałości 2, to organizacja jest na poziomie 1. Ale jeśli jest spełnione 19 z nich (brakuje tylko jednego), to organizacja... nadal znajduje się na poziomie 1. reprezentacja etapowa (ang. staged representation) – struktura modelu, w którym osiągnięcie celów zbioru obszarów procesowych określa poziom dojrzałości; każdy poziom stanowi podstawę dla następnego poziomu [197] Reprezentacja ciągła jest bardziej elastyczna niż etapowa. Nie definiuje się poziomów dojrzałości na poziomie całej organizacji. Słowo „ciągła” odnosi się do „ciągłej” skali dojrzałości. Innymi słowy, każdy obszar procesowy może mieć inną dojrzałość. Reprezentacja ciągła występuje np. w modelu TPI Next. reprezentacja ciągła (ang. continuous representation) – struktura modelu dojrzałości organizacyjnej, w której poziomy dojrzałości określają zalecaną kolejność usprawnień procesów w określonym obszarze procesowym [197] Modele referencyjne zawartości nie definiują poziomów dojrzałości i nie narzucają kolejności wykonywania działań. Oferują zestaw dobrych praktyk, które można wdrażać według uznania osoby odpowiedzialnej za doskonalenie procesu. Przykładami modeli referencyjnych zawartości są CTP (Critical Testing Processes) [309], STEP (Systematic Test and Evaluation Process) [176] oraz TOM (Test Organization Maturity) autorstwa Paula Gerrarde’a [310]. model referencyjny zawartości, model oparty na zawartości (ang. contentbased model) – model procesu zawierający szczegółowy opis dobrych praktyk inżynierskich, np. praktyki stosowane w testowaniu Z jednej strony modele referencyjne procesu są łatwiejsze w użyciu. Zwykle nie potrzeba zewnętrznego konsultanta, aby ocenił obecny stan procesu (można to

zrobić samemu – model dokładnie mówi, co należy sprawdzić). Nie jest również konieczne tworzenie analizy opłacalności czy studium przypadku (ang. business case). Przy zastosowaniu modeli referencyjnych zawartości business case jest koniecznością. Z drugiej strony modele zawartości lepiej się wdraża, gdyż łatwiej wpasowują się one w strategię osiągania celów biznesowych organizacji. Większość modeli, które opiszemy, to tzw. modele procesów, czyli modele składające się z szeregu procesów podporządkowanych jakiemuś jednemu, wspólnemu celowi, w naszym przypadku – doskonaleniu procesu testowego. model procesów (ang. process model) – struktura, w której procesy o tej samej naturze są klasyfikowane w ogólnym modelu, np. model doskonalenia testów

31.5.2. Podejścia analityczne Podejścia analityczne skupiają się na identyfikowaniu problematycznych obszarów w procesach, definiowaniu celów związanych z ich poprawą oraz wykorzystaniem zestawu metryk, których późniejsza analiza pozwala mierzyć stopień osiągnięcia tych celów. W rozdziale 33 opiszemy dwie popularne techniki analityczne usprawniania procesu: analizę przyczyn oraz GQM (Goal-QuestionMetric) [311]. Ponadto, w podrozdziale 40.5 omówimy kilka narzędzi i technik pomocnych w analizie przyczyn źródłowych.

31.5.3. Podejścia hybrydowe Podejście hybrydowe polega na połączeniu technik opartych na modelu z technikami analitycznymi. Większość projektów udoskonalania procesu testowego wykorzystuje tak naprawdę podejścia hybrydowe, czy to explicite, czy też niejawnie. Na przykład, wiele obszarów procesowych zdefiniowanych w modelach CMMI czy TMMi wykorzystuje analizę przyczyn do identyfikowania problemów w tych obszarach. Podejście hybrydowe działa lepiej w organizacjach o wyższym stopniu dojrzałości procesu. Jest tak dlatego, gdyż metody analityczne związane z wykorzystaniem metryk są skuteczne tylko wtedy, gdy w organizacji istnieje program metryk i pomiarów. Budowa takiego programu w sytuacji, gdy organizacja nie wdrożyła procesów planowania, projektowania, kontroli czy raportowania nie ma większego sensu.

Bardzo rzadko zdarza się, aby organizacja przez dłuższy czas stosowała wyłącznie podejście analityczne. Jest to metoda typu bottom-up, dlatego nie uwzględnia szerszego kontekstu w procesie doskonalenia. Większość organizacji prędzej czy później zaczyna stosować jakiś rodzaj podejścia modelowego (typu top-down), pozwalającego na wdrożenie usprawnień we wszystkich obszarach procesowych.

31.5.4. Inne podejścia do udoskonalania procesu testowego Doskonalenie testowania można osiągnąć nie tylko za pomocą formalnego, całościowego podejścia, lecz także przez skupienie się na wybranych aspektach procesu testowego. Metody omówione we wcześniejszych rozdziałach pokrywają większość tych aspektów. Doskonalenie procesu testowego przez zwiększanie umiejętności pracowników. Podejście to polega na ciągłym inwestowaniu w ludzi, celem zwiększenia ich wiedzy, umiejętności oraz doświadczenia. Inwestowanie to nie ogranicza się wyłącznie do testerów, ale obejmuje wszystkich mających wpływ na efektywność procesu testowego (a więc analityków biznesowych, programistów, kierowników zespołów deweloperskich itd.). Zwiększanie kompetencji może odbywać się przez szkolenia, mentoring, coaching, indywidualną naukę w zakresie: wiedzy testerskiej (zasady testowania, techniki projektowania testów, narzędzia itp.); wiedzy o inżynierii oprogramowania (inżynieria wymagań, analiza biznesowa, wzorce projektowe, metodyki wytwarzania oprogramowania itp.); wiedzy dziedzinowej (procesy biznesowe, charakterystyka użytkowników, pojęcia właściwe danej dziedzinie itp.); umiejętności danych itp.).

miękkich

(komunikacja,

umiejętność

prezentowania

Wzrost kompetencji można powiązać ze ścieżką kariery w organizacji. W ten sposób bardziej doświadczeni testerzy mogą w naturalny sposób stać się w przyszłości liderami, kierownikami czy menedżerami testów.

W podejściu ukierunkowanym na rozwój ludzi można wykorzystać takie modele jak PSP (Personal Software Process) autorstwa Humphreya [312] czy PCMM (People Capability Maturity Model) stworzony przez Curtisa, Hefleya i Millera [313]. PSP został zaprojektowany, aby pomóc indywidualnym inżynierom oprogramowania w zwiększaniu efektywności ich pracy. Główną rolę w modelu PSP odgrywają dane (w tym historyczne). PSP dzieli dane na 4 kategorie: skrypty, miary, standardy i formularze, oraz stosuje 4 podstawowe miary: rozmiar, wysiłek, jakość i harmonogram. PSP pomaga inżynierom w [219]: ulepszaniu ich umiejętności planowania i szacowania; przyjmowaniu zobowiązań, których będą w stanie dotrzymać; zarządzaniu jakością ich projektów; redukcji liczby defektów w ich pracy. People CMM jest rozwijany i utrzymywany przez Software Engineering Institute. Pomaga organizacjom w rozwijaniu dojrzałości potencjału pracowników przez dostarczenie mapy drogowej praktyk, których stosowanie przyczynia się do ciągłego doskonalenia możliwości i zdolności członków zespołu. Implementacja wszystkich praktyk naraz jest niemożliwa, dlatego model podaje sekwencyjną drogę, prowadząc nas krok po kroku na coraz wyższe poziomy dojrzałości pracowników organizacji. Struktura modelu odzwierciedla pięciopoziomową strukturę modelu CMM (patrz rys. 31.6). Doskonalanie procesu testowego przez wykorzystanie narzędzi. Kwestie związane z wykorzystaniem narzędzi wspomagających testowanie omówiliśmy w rozdziale 30. Doskonalenie procesu testowego na podstawie standardów i regulacji. W procesie ulepszania testowania można wykorzystać różne istniejące standardy, w szczególności: ISO/IEC 29119 (Software Testing Standard), IEEE 829 (Software and System Test Documentation Standard) czy IEEE 1028 (Reviews). Doskonalenie procesu może być wymagane przez stosowane standardy. Zapewnienie zgodności z tymi standardami może wynikać z regulacji prawnych lub powodów natury komercyjnej, a nawet marketingowej.

Rysunek 31.6. Model People CMM

1 Chodzi tu o poziom dojrzałości według ustalonego modelu oceny dojrzałości procesu wytwórczego, np. CMMI. Modele te dostarczają wiele cennych wskazówek, co i w jaki sposób można usprawniać. Firma może je wykorzystać do usprawnienia swoich procesów bez jakiegokolwiek ubiegania się o przyznanie poziomu dojrzałości. Formalny audyt organizacji i przyznanie jej tego poziomu jest zwykle dosyć kosztowne. Głównym powodem, dla którego firmy decydują się na te koszty, są tak naprawdę kwestie marketingowe. 2 W dalszej części książki będziemy stosować wymiennie pojęcia „udoskonalanie”, „usprawnianie” oraz „ulepszanie”. 3 Niektórzy autorzy, np. [218], twierdzą, że lepiej mówić o „udoskonalaniu testowania” niż „udoskonalaniu procesu testowego”. 4 Całkowita efektywność ograniczania defektów (ang. total defect containment effectiveness) jest liczona jako liczba defektów znalezionych przed wydaniem oprogramowania podzielona przez całkowitą liczbę znalezionych defektów, wliczając w to defekty operacyjne, czyli znalezione po wydaniu oprogramowania do klienta. Należy oczywiście określić czas, w którym mierzy się defekty operacyjne (np. 1 rok od daty wydania).

32. Udoskonalanie oparte na modelu

32.1. Wprowadzenie Organizacje, które chcą wdrożyć doskonalenie procesu testowego na podstawie podejścia opartego na modelu, często stoją przed dylematem: istnieje bardzo wiele modeli – który z nich wybrać? Jest to istotne i nietrywialne pytanie, ponieważ nie jest tak, że każdy model nadaje się do stosowania w każdej organizacji. Zły wybór modelu może nieść ze sobą bardzo przykre konsekwencje, z których strata czasu, wysiłku i pieniędzy to najmniejsze problemy. Jak zatem dokonać wyboru? Najpierw zdefiniujmy (na podstawie [296]) cechy, które powinien mieć każdy dobry model, niezależnie od tego, czy pasuje do naszej konkretnej sytuacji: Łatwość użycia. Model musi być praktyczny i łatwy w stosowaniu. Przekonanie interesariuszy do stosowania modelu zbyt skomplikowanego, niepraktycznego i trudnego we wdrożeniu będzie nie lada wyzwaniem, a samo jego stosowanie – drogą przez mękę. Publiczna dostępność. Model powinien być powszechnie znany. Jeśli jest wykorzystywany przez wiele organizacji, to jest „bezpieczny” – wiadomo wtedy, że jest wartościowy. Dostępne wsparcie konsultantów. Jeśli model jest popularny, to są dostępni konsultanci mogący pomóc we wdrożeniu go w organizacji. Stosowanie nieznanego, ezoterycznego modelu niemającego żadnego wsparcia jest ryzykowne. Nie tylko powoduje, że jesteśmy zdani sami na siebie, ale taki model przede wszystkim prawdopodobnie w ogóle nie spełni swojej funkcji. Niezależność. Model nie powinien być produktem marketingowym sprzedawanym przez tę czy inną organizację. W przeciwnym razie może

nie mieć niezależności, która jest bardzo istotna podczas udoskonalania procesów testowych. Model nie może zależeć od żadnych komercyjnych wpływów. Akceptacja środowiska. Model powinien być zaakceptowany przez społeczność profesjonalnych testerów, konsultantów i przemysł IT. Używanie niewypróbowanego przez nikogo modelu jest bardzo ryzykowne. Wsparcie dla użytkownika. Model musi wspierać osoby korzystające z niego w identyfikowaniu, proponowaniu i mierzeniu ulepszeń odpowiadającym konkretnym problemom procesowym. Model proponujący jedno, „dogmatyczne” rozwiązanie problemu lub skupiający się na jednym tylko obszarze procesowym ma niską wartość. Umożliwienie wielu małych udoskonaleń. Model powinien pozwalać na osiągnięcie doskonałości przez wiele małych kroków, które łatwo poddają się zarządzaniu i kontroli. Solidność, czyli model jest zbudowany na solidnych podstawach: praktycznych, teoretycznych, empirycznych, opublikowanych i zweryfikowanych. Informatywność. Model powinien pomóc szacowaniu i przeprowadzaniu ulepszeń.

w

identyfikowaniu,

Mierzalność. Model powinien dostarczać ilościowych (mierzalnych) metod ulepszania procesu. Elastyczność. Model powinno dać się dostosować (skroić na miarę) do danego projektu, konkretnego cyklu życia oprogramowania, szczegółowych celów biznesowych itd. Niektóre modele mogą ponadto oferować lub wymagać certyfikację organizacji przez upoważnionych do tego celu audytorów. Audyt pozwala na ocenę stopnia dojrzałości procesu bądź organizacji. Należy być świadomym istnienia zarówno korzyści, jak i ryzyk wynikających ze stosowania modelu. Ich zestawienie, opracowane na podstawie [219], przedstawiono w tabeli 32.1. Tabela 32.1. Doskonalenie oparte na modelu – korzyści i ryzyka

Korzyści

Ryzyka

Zorg anizowane, us trukturalizowane podejś cie Wykorzys tanie dobrych praktyk w tes towaniu S zerokie pokrycie obs zarów proces owych Obiektywnoś ć wykorzys tywanych metod Porównywalnoś ć z innymi org anizacjami

Nieuwzg lędnienie konteks tu projektu „ Ś lepota modelu” (model nie myś li za nas !) Wybór złeg o podejś cia Brak umiejętnoś ci

32.2. Udoskonalanie procesów organizacyjnych i biznesowych W rozdziale tym przedstawimy kilka modeli udoskonalania procesów organizacyjnych i biznesowych. Choć nie są one związane bezpośrednio z procesem testowym, warto je znać. Są to podejścia bardzo często stosowane w organizacji i może się zdarzyć, że doskonalenie testowania będzie musiało odbywać się w ramach jednego z tych procesów. Przedstawione modele opiszemy bardzo krótko, gdyż nie będą nas one interesować tak bardzo, jak modele udoskonalania procesu testowego.

32.2.1. TQM (Total Quality Management) TQM, czyli kompleksowe zarządzanie jakością (zwane również zarządzaniem przez jakość) jest metodą całościowego spojrzenia na organizację, w którym każdy aspekt jej działania podlega działaniom projakościowym. W proces ten są zaangażowani wszyscy pracownicy organizacji. TQM wykorzystuje podejście zwane myśleniem systemowym (ang. system thinking) – koncepcję opracowaną przez Petera Senge’a, teoretyka zarządzania, opublikowaną w jego książce „Piąta dyscyplina” [314]. Równie istotne jest wykorzystanie narzędzi kontroli jakości (patrz np. rozdz. 40). TQM, zarządzanie przez jakość (ang. Total Quality Management, TQM ) – stosowane w całej organizacji podejście do zarządzania koncentrujące się na

jakości, oparte na udziale w nim wszystkich jej (organizacji) członków i mające na celu długofalowy sukces przez satysfakcję klientów oraz korzyści dla wszystkich członków organizacji i społeczeństwa; zarządzanie przez jakość składa się z 5 faz: planowania, organizowania, kierowania, kontrolowania i zapewniania [56] Choć nie istnieje jedno, ściśle zdefiniowane podejście TQM, każde z nich wykorzystuje podstawowe zasady tej metody, zwane 8 zasadami zarządzania jakością: orientacja na klienta – jakość jest definiowana przez jego wymagania; przywództwo – wsparcie kierownictwa projakościowych jest niezbędne;

w prowadzeniu działań

zaangażowanie pracowników – w proces muszą być zaangażowani wszyscy pracownicy; podejście procesowe – dzięki czemu unikamy „odkrywania na nowo koła”, powtarzania tych samych błędów, niejasności i chaosu; systemowe podejście do zarządzania – należy mieć opracowaną strategię działania obejmującą całą organizację i biorącą pod uwagę wszystkie zależności między obszarami procesowymi; ciągłe doskonalenie – nic nie jest tak dobre, żeby nie mogło być jeszcze lepsze; podejmowanie decyzji na podstawie faktów – wykorzystanie metryk, miar i wskaźników do pomiarów, analiz i predykcji; dobre relacje z otoczeniem – zarówno z klientem, jak i z dostawcami, firmami wdrożeniowymi, outsourcingowymi itp. Obecnie TQM utraciła nieco na znaczeniu, na korzyść nowych podejść, takich jak ISO 9000, Lean czy Six Sigma.

32.2.2. ISO 9000 ISO 9000 to zbiór norm opisujących wymagania dla systemów zarządzania jakością. Składa się z 4 zasadniczych części: ISO 9000 – Quality management system – Fundamentals and vocabulary (opisuje podstawy systemów zarządzania jakością oraz słownik pojęć);

ISO 9001 – Quality management systems – Requirements wymagania nakładane na system zarządzania jakością);

(opisuje

ISO 9004 – Quality management systems – Guidelines for performance improvements (dotyczy kwestii ciągłego udoskonalania procesów); ISO 19011 – Guidelines for auditing management systems (zawiera wytyczne dla audytowania systemów zarządzania). ISO 9004 rozszerza podstawową wersję normy ISO 9001 o następujące aspekty [315]: ukierunkowanie działań organizacji na zadowolenie nie tylko klientów, lecz także innych interesariuszy; stosowanie 8 zasad zarządzania jakością (opisanych w p. 32.2.1); uwzględnienie takich elementów zasobów, jak: informacje, dostawcy i partnerzy, zasoby naturalne, finanse; ocena finansowa; samoocena; proces ciągłego doskonalenia.

32.2.3. EFQM Excellence Model Model doskonałości EFQM opiera się na Podstawowych zasadach doskonałości opisanych w punkcie 31.4.2. Składa się z dziewięciu kryteriów, z których pięć nazywanych jest „potencjałem” (ang. enablers), a cztery – wynikami (ang. results) (patrz rys. 32.1). Kryteria potencjału opisują działania podejmowane przez organizację, a kryteria wyników – jej osiągnięcia. Strzałki podkreślają dynamiczny charakter modelu: wyniki są efektem potencjału, a potencjał jest doskonalony przez wykorzystanie informacji zwrotnych z pomiaru wyników. Budowa modelu odzwierciedla założenia leżące u jego podstaw: „doskonałe rezultaty w zakresie wyników działalności, klientów, pracowników i społeczeństwa są osiągane poprzez przywództwo będące siłą napędową dla polityki i strategii, która jest realizowana przez pracowników, partnerstwo, zasoby i procesy” [316]. Więcej szczegółowych informacji o modelu oraz dokładny opis jego elementów Czytelnik znajdzie na stronie [316]. Model doskonałości EFQM (Europejskiej Fundacji Zarządzania Jakością) (ang. (European Foundation for Quality Management) excellence model, EFQM) –

nienormatywny model zarządzania jakością w organizacji, zdefiniowany i będący własnością Europejskiej Fundacji Zarządzania Jakością, oparty na pięciu kryteriach „potencjału” (ang. enabling criteria) opisujących to, co organizacja robi oraz czterech kryteriach „wynikowych” (ang. results criteria) opisujących to, co organizacja osiąga w wyniku podejmowanych działań

Rysunek 32.1. Model doskonałości EFQM (Excellence Model)

32.2.4. Six Sigma Six Sigma jest podejściem ilościowym, mającym na celu zwiększenie efektywności i wydajności w organizacji. Stworzono je w latach 80. XX wieku w firmie Motorola. Obecnie jest jedną z najpopularniejszych metodyk poprawy jakości [317]. Six Sigma koncentruje się na pozyskaniu zaangażowania kierowników na wszystkich szczeblach zarządzania oraz stosuje tzw. taktykę DMAIC, podobną do koła Deminga czy metody IMPROVE. DMAIC składa się z 5 kroków: Define (definiuj) – faza zdefiniowania problemu biznesowego; Measure (mierz) – faza zbierania danych i oceny aktualnego poziomu procesu; Analyze (analizuj) – faza identyfikacji, oceny i wyboru przyczyn źródłowych problemu, które będziemy chcieli usuwać;

Improve (ulepszaj) – faza definiowania, sformułowania, weryfikacji oraz wdrożenia rozwiązania usuwającego zidentyfikowane problemy; Control (kontroluj) – faza mająca na celu utrzymanie korzyści z zastosowanego rozwiązania, polegająca na monitorowaniu ulepszeń. Cechą charakterystyczną Six Sigma jest wykorzystanie metod statystycznych dla oceny, kontroli i mierzenia poprawy jakości. Ostatecznym celem Six Sigma jest uzyskanie takiej akceptowalnej zmienności procesu, która pozwoli uzyskać liczbę 3,4 defektów na milion operacji1.

Rysunek 32.2. Six Sigma – rozkład normalny i zmienność procesu Na rysunku 32.2 (zaczerpniętym z www.sixsigma-institute.org/) przedstawiono schematycznie tę sytuację. Każdy proces cechuje się pewną zmiennością. Jeśli możemy go mierzyć i wyrazić te pomiary liczbowo, zakładając, że zmienna losowa opisująca proces ma rozkład normalny, to stopień zmienności tego procesu jest cechowany wariancją (lub odchyleniem standardowym) rozkładu normalnego. Dla zmiennej o rozkładzie normalnym ze średnią µ i odchyleniem standardowym σ 68,25% obserwacji będzie zawierać się w przedziale (µ – σ, µ + σ). Natomiast przedział (µ – 6σ, µ + 6σ) zawierać będzie aż 99,999998% obserwacji, co

oznacza, że tylko 3,4 na milion znajdzie się poniżej wartości µ – 6σ lub powyżej wartości µ + 6σ. Jeśli możemy tak usprawnić proces, że jego zmienność w granicach ±6σ jest dla nas dopuszczalna, to osiągnęliśmy poziom „six sigma”2.

32.2.5. Lean Metoda lean, tłumaczona na polski jako „szczupłe zarządzanie” jest koncepcją zarządzania organizacją, która kładzie nacisk na dostarczanie klientowi możliwie najwyższej jakości przy możliwie jak najniższym koszcie oraz zużyciu zasobów [318]. Lean jest oparta na 7 zasadach: 1) eliminuj stratę (ang. waste), czyli niepotrzebne czynności, takie jak: nadprodukcja, niepotrzebny transport, zbędny inwentarz, ruch, defekty, zbędne procesy oraz bezproduktywne czekanie; 2) twórz jakość wbudowaną w produkt – w metodykach zwinnych metodami takiego tworzenia jakości jest wykorzystanie programowania w parach, TDD, częsty kontakt z klientem, ciągła integracja, automatyzacja itp. Każda z tych czynności przyczynia się do zwiększenia jakości tworzonego produktu; 3) twórz wiedzę – baza wiedzy, tworzona na podstawie doświadczenia pracowników, przyda się w rozwiązywaniu przyszłych problemów; może służyć również jako dane historyczne dla modeli predykcyjnych; 4) podejmuj decyzje możliwie jak najpóźniej – tak, aby mieć maksymalnie dużą wiedzę potrzebną do ich podjęcia; 5) dostarczaj rozwiązania jak najwcześniej – tak, aby mieć szybko zwrotną informację od klienta; 6) szanuj ludzi – w szczególności polega to na umiejętności słuchania, 7)

dyskutowania i nielekceważenia opinii i pomysłów innych; optymalizuj totalnie – usprawnieniu podlega całość w organizacji.

procesów

Lean wykorzystuje takie narzędzia jak: VSM (ang. Value Stream Mapping), czyli mapowanie strumienia wartości – pozwala na zgromadzenie i przedstawienie danych

o rzeczywistym przepływie informacji oraz fizycznych artefaktów w procesie; 5S – metoda systematycznego uczenia się, dyscypliny oraz dążenia do perfekcji, złożona z pięciu kroków: selekcja (jap. seiri), systematyka (jap. seiton),

sprzątanie

(jap.

seiso),

standaryzacja

(jap.

seiketsu),

samodyscyplina (jap. shitsuke); Kanban – metoda optymalizacji produkcji, zaadaptowana przez Andersona [319] do wytwarzania oprogramowania; diagram Ishikawy (patrz p. 29.5.3); poka yoke – technika produkcji rzeczy w sposób, który uniemożliwia lub utrudnia użycie ich błędnie, niezgodnie z przeznaczeniem (patrz p. 40.5.4).

32.3. Udoskonalanie procesu produkcji oprogramowania W rozdziale tym przedstawimy krótko metody doskonalenia procesu produkcji oprogramowania (ang. Software Process Improvement, SPI). Ulepszanie procesu testowego często odbywa się w kontekście procesu produkcji. Niestety techniki opisane w tym rozdziale nie kładą nacisku na zagadnienia testowania, traktując je dosyć ogólnie. Dlatego w podrozdziale 32.4 oraz 32.5 omówimy szczegółowo modele stworzone specjalnie z myślą o procesie testowym.

32.3.1. CMMI Oficjalny opis modelu CMMI jest zawarty w podręczniku [197]. CMMI to podejście do usprawniania procesów. Definiuje pięć poziomów dojrzałości (w wersji z reprezentacją etapową) lub cztery poziomy możliwości (w wersji z reprezentacją ciągłą). Aby organizacja osiągnęła dany poziom, musi spełnić określone warunki, opisane w modelu. CMMI, zintegrowany model dojrzałości organizacyjnej (ang. Capability Maturity Model Integration, CMMI) – struktura opisująca kluczowe elementy efektywnego rozwoju produktu i procesu utrzymania; model dojrzałości organizacyjnej składa się z najlepszych praktyk w planowaniu, inżynierii i zarządzaniu rozwojem i utrzymaniem produktu [197]

poziom dojrzałości (ang. maturity level) – stopień w procesie doskonalenia względem wcześniej zdefiniowanego zbioru obszarów procesowych, dla których mają być spełnione wszystkie cele w tym zbiorze [320] CMMI nie opisuje samych procesów, ale ich pożądane cechy, dostarczając wskazówki dla osób doskonalących te procesy. Na rysunku 32.3 przedstawiono model w reprezentacji etapowej. Do każdego poziomu są tu przypisane obszary procesowe, które muszą być zapewnione, aby organizacja znalazła się na danym poziomie dojrzałości.

Rysunek 32.3. CMMI – model z reprezentacją etapową Różnice w reprezentacjach są natury czysto organizacyjnej – zawartość obu modeli jest identyczna. CMMI ma dwa obszary procesowe bezpośrednio związane z procesem testowym: weryfikację oraz walidację, jednak poświęcono im jedynie 20 spośród ok. 400 stron dokumentacji opisującej model. Proces walidacji ma dwa cele szczegółowe (ang. specific goals): przygotowanie do walidacji oraz walidację produktu lub jego komponentów. Proces weryfikacji ma trzy cele szczegółowe: przygotowanie do weryfikacji, przeprowadzenie

przeglądów oraz weryfikację wybranych artefaktów. Na każdy z tych celów składa się kilka szczegółowych praktyk, przedstawionych na rysunku 32.4.

Rysunek 32.4. Obszary procesowe CMMI: weryfikacja i walidacja

32.3.2. ISO/IEC 15504 Standard ISO/IEC 15504 – Software Process Improvement and Capability Determination [302], znany również pod nazwą SPICE jest modelem SPI, uwzględniającym procesy testowe: testowanie oprogramowania (ang. software testing) oraz testowanie systemowe (ang. system testing). ISO/IEC 15504 jest mniej szczegółowy niż CMMI, który definiuje różne pod-praktyki wspierające implementację szczegółowych praktyk. CMMI w reprezentacji etapowej dobrze sprawdza się dla dużych organizacji. ISO/IEC 15504 pozwala na ocenę pojedynczych procesów czy projektów.

32.3.3. ITIL (Information Technology Infrastructure Library) ITIL to zbiór procesów służących poprawnemu zarządzaniu infrastrukturą IT, tzn. na oczekiwanym przez użytkowników poziomie wydajności i dostępności zasobów [321]. Dwie zasadnicze grupy procesów to wsparcie usług oraz dostarczanie usług. Dwie pozostałe to zarządzanie bezpieczeństwem oraz – interesująca z testerskiego punktu widzenia – grupa procesów zarządzania

aplikacjami. W skład tej grupy

wchodzą

procesy: cyklu życia

rozwoju

oprogramowania oraz usług testowania. Innym wartym uwagi procesem definiowanym przez ITIL jest zarządzanie incydentami (patrz też rozdz. 27).

32.3.4. TSP (Team Software Process) TSP, czyli Team Software Process, to model autorstwa Humphreya [322]. TSP dostarcza zbiór dobrze zdefiniowanych procesów pomocnych w organizacji projektów informatycznych. Może być stosowany do projektów o dowolnej skali, od małych po te liczące wiele milionów linii kodu. Proces opiera się na modelu przyrostowym, gdzie całkowity wysiłek dzielony jest na tzw. cykle deweloperskie. TSP omawia procesy zachodzące w czasie całego cyklu życia oprogramowania: rozpoczęcie projektu; planowanie; strategia; wymagania; projekt; implementacja; testowanie; analiza post mortem. Poza procesem testowania, omawiającym testy integracyjne i systemowe, czynności związane z testowaniem są omówione w obszarze projektowania (projektowanie testowalnego oprogramowania) oraz implementacji (przeglądy i inspekcje, w tym metody szacowania pozostałych w kodzie defektów). Metodyka TSP omawia również rolę kierownika ds. jakości.

32.3.5. BOOTSTRAP BOOTSTRAP [323] to metoda oceny i poprawy procesu rozwoju oprogramowania, której celem było przyspieszenie wdrożenia technologii inżynierii oprogramowania w europejskim przemyśle informatycznym w ramach projektu Esprit. Opiera się na modelu CMM i rozszerza go tak, aby wdrożenie gwarantowało jednocześnie spełnienie przepisów normy jakościowej ISO 9000 oraz normy Europejskiej Agencji Kosmicznej ESA-PSS-05 dotyczącej inżynierii oprogramowania. Tak jak CMM i CMMI, BOOTSTRAP wyróżnia 5 poziomów

dojrzałości, ale w przeciwieństwie do nich nie wymaga ścisłego trzymania się określonych kluczowych praktyk, pozwalając użytkownikowi alternatywne podejścia. Głównymi cechami BOOTSTRAP są:

na

inne,

kwestionariusze do oceny projektu i organizacji; ujednolicone procedury i obowiązkowe szkolenia dla osoby oceniającej (audytora); podejście konstruktywne, a nie normatywne; pytania otwarte; natychmiastowa informacja zwrotna i planowanie akcji.

32.4. Udoskonalanie procesu testowego – modele referencyjne procesu 32.4.1. TPI Next (Test Process Improvement) Wprowadzenie. TPI Next jest uaktualnioną wersją modelu TPI. Metoda została stworzona w firmie Sogeti i jest zgodna z TMap – metodą również opracowaną w tej firmie (opisaliśmy ją krótko w podrozdz. 19.10). TPI Next wskazuje logiczne i praktyczne kroki, które prowadzą do udoskonalenia procesu testowego w organizacji. Terminologia oraz metody stosowane w TPI Next są zgodne z terminologią i metodami używanymi przez ISTQB. Niniejszy punkt jest napisany na podstawie [306] – oficjalnego podręcznika opisującego model TPI Next. TPI Next (ang. TPI Next) – struktura doskonalenia procesu testowego będąca modelem referencyjnym procesu z reprezentacją ciągłą, gdzie proces doskonalenia jest oparty na celach (ang. business-driven); opisuje podstawowe elementy efektywnego i skutecznego procesu testowego Struktura modelu TPI Next jest pokazana na rysunku 32.5. Model dzieli proces testowy na kluczowe obszary. Każdy z nich składa się z poziomów dojrzałości, z którymi są związane tzw. punkty kontrolne oraz sugestie poprawy. Omówimy je w dalszej części tego rozdziału.

Rysunek 32.5. Model TPI Next Różnicą w stosunku do modelu TPI jest wprowadzenie tzw. klastrów oraz potencjałów (ang. enablers). Klaster to zbiór spójnych punktów kontrolnych z różnych obszarów procesowych. Wykorzystanie klastrów umożliwia stosowanie modelu w podejściu opartym na biznesie. Umożliwiają one ponadto stosowanie reprezentacji ciągłej poziomów dojrzałości (TPI oferował wyłącznie reprezentację etapową). Testowanie jest integralną częścią procesu rozwoju oprogramowania. Wpływa na inne procesy, a inne procesy wpływają na testowanie. Interakcja między tymi procesami jest wyrażona w TPI Next explicite przez tzw. potencjały: procesy, praktyki lub dyscypliny, które są ściśle związane z testowaniem i jeśli są wykonywane poprawnie, to wpływają korzystnie na dojrzałość testowania, a testowanie – na dojrzałość innych procesów. Potencjały odgrywają ważną rolę

w określaniu tzw. szybkich korzyści (ang. quick wins) i pozwalają na lepsze wpasowanie się TPI Next w modele udoskonalania produkcji oprogramowania, takie jak CMMI czy ISO/IEC 15504. Opis modelu. W każdej organizacji na testowanie składa się wiele różnych procesów. TPI Next wyróżnia 16 takich kluczowych obszarów, przypisując je do jednej z trzech grup: relacje z interesariuszami (kluczowe obszary 1–6); zarządzanie testowaniem (kluczowe obszary 7–11); rzemiosło testowe (ang. test profession) (kluczowe obszary 12–16). Każdy kluczowy obszar może mieć inny poziom dojrzałości, a ich kombinacja, złożona ze wszystkich kluczowych obszarów definiuje poziom dojrzałości procesu testowego. Na rysunku 32.6 jest pokazana struktura tego modelu w postaci tzw. macierzy dojrzałości testów.

Rysunek 32.6. TPI Next – macierz dojrzałości testów TPI Next wyróżnia 4 poziomy dojrzałości: Wstępny, Kontrolowany, Efektywny i Zoptymalizowany. Poziom Wstępny nie ma żadnych warunków wstępnych, dlatego proces testowy każdej organizacji początkowo znajduje się na tym właśnie poziomie. Aby osiągnąć poziom wyższy, należy spełnić wszystkie wymogi dotyczące poziomu niższego.

Dla każdego poziomu dojrzałości są zdefiniowane tzw. punkty kontrolne (ang. checkpoints), których spełnienie gwarantuje osiągnięcie tego poziomu. Punkty te są sformułowane w postaci pytań, na które można odpowiedzieć „tak” lub „nie”. Odpowiedź „tak” oznacza spełnienie danego punktu (oczywiście należy dostarczyć wiarygodny dowód na to, że dane wymaganie zostało spełnione). Każdy punkt kontrolny jest związany z dokładnie jednym obszarem kluczowym i jednym poziomem dojrzałości. Pojedynczy obszar kluczowy znajduje się na określonym poziomie dojrzałości, jeśli wszystkie punkty kontrolne tego oraz niższych poziomów zostały spełnione. Cały proces znajduje się na określonym poziomie dojrzałości, jeśli wszystkie kluczowe obszary osiągnęły ten poziom. Na przykład, kluczowy obszar „Zobowiązanie interesariuszy” ma 4 punkty kontrolne na poziomie Kontrolowanym, 3 na poziomie Efektywnym i 3 na poziomie Zoptymalizowanym (patrz rys. 32.6). Aby znalazł się na poziomie Efektywnym, muszą być dla niego spełnione wszystkie 4 punkty poziomu Kontrolowanego i wszystkie 3 punkty poziomu Efektywnego. Na rysunku 32.7 jest pokazany fikcyjny, bieżący stan procesu testowego pewnej organizacji.

Rysunek 32.7. TPI Next – macierz dojrzałości dla przykładowego procesu Siedem kluczowych obszarów w tym procesie osiągnęło poziom kontrolowany: Strategia testowa, Zarządzanie procesem testowym, Szacowane i planowanie, Zarządzanie defektami, Zarządzanie testaliami, Stosowanie

metodyki testowania oraz Narzędzia testowe. Proces Metryki osiągnął poziom Efektywny, a pozostałe procesy są na poziomie Wstępnym. W szczególności proces Komunikacja, mimo osiągnięcia wszystkich punktów kontrolnych poziomu Efektywnego znajduje się na poziomie Wstępnym, gdyż nie zostały spełnione dla niego 3 z 4 punktów kontrolnych poziomu Kontrolowanego. Istnieją procesy na poziomie Wstępnym, dlatego cały proces testowy również jest na tym poziomie. Na rysunku 32.7 przedstawiono graficzną postać oceny bieżącego procesu testowego. Zanim zaczniemy proces doskonalenia, musimy wiedzieć, w jakim miejscu się znajdujemy, gdyż – jak mawia Watts H. Humphrey z Carnegie Mellon University: „jeśli nie wiesz, gdzie jesteś, mapa jest bezużyteczna”. Proces doskonalenia jest taką właśnie mapą. Aby móc ją efektywnie wykorzystać, musimy najpierw zlokalizować na niej swoje miejsce. Klastry. Udany proces doskonalenia polega na usprawnianiu metodą małych kroków. Przejście z niższego poziomu dojrzałości na wyższy jest zadaniem trudnym i wymagającym dużo czasu i wysiłku. Aby więc umożliwić nieustanny, „częściowy” postęp, w którym każdy krok nie wymaga aż tyle nakładów pracy, TPI Next oferuje koncept zwany klastrami. Klaster jest zbiorem punktów kontrolnych z kilku różnych kluczowych obszarów, których spełnienie oznacza wykonanie jednego, małego kroku doskonalącego proces testowy. TPI Next definiuje 13 klastrów, oznaczonych literami od A do M. Bazowy układ klastrów jest pokazany na rysunku 32.8. Układ ten jest zbudowany na założeniu, że udoskonalenie powinno osiągać kolejne poziomy dojrzałości (np. spełnienie wszystkich punktów kontrolnych dla klastrów A, B, C, D, E oznacza osiągnięcie poziomu Kontrolowanego, klastrów F, G, H, I, J – poziomu Efektywnego, a klastrów K, L, M – poziomu Zoptymalizowanego).

Rysunek 32.8. TPI Next – bazowy układ klastrów Na przykład, klaster A zawiera pierwsze punkty kontrolne obszarów: Zobowiązanie interesariuszy, Stopień zaangażowania, Organizacja testów, Raportowanie oraz dwa pierwsze punkty kontrolne obszarów: Strategia testowa, Zarządzanie procesem testowym, Zarządzanie defektami i Projektowanie przypadków testowych. Bazowy układ klastrów nie odpowiada żadnemu szczególnemu czynnikowi biznesowemu (ang. business driver). Można jednak modyfikować układ klastrów, jeśli chcemy uwypuklić jakiś jeden lub kilka celów biznesowych. W zależności od celu biznesowego kluczowe obszary są priorytetyzowane jako wysokie, neutralne lub niskie, a następnie punkty kontrolne przechodzą z jednych klastrów do innych w następujący sposób: dla obszarów o priorytecie wysokim punkty przesuwają się „w lewo”: punkty z klastra A zostają w A, punkty B stają się punktami A, C – punktami B itd. Punkty dla obszarów neutralnych się nie zmieniają. Punkty w obszarach o niskim priorytecie przesuwają się „w prawo”: punty A stają się punktami B, B – punktami C itd. Należy przy tym pamiętać, że poszczególne punkty kontrolne nie zmieniają swojego miejsca w macierzy dojrzałości testów. Badania przeprowadzone przez IT Governance Institute [324] identyfikują następujące najpopularniejsze cele biznesowe: ulepszenie orientacji na klienta oraz poziomu usług;

zapewnienie zgodności z regulacjami prawnymi; ustanowienie ciągłości i dostępności usług; zarządzanie ryzykiem biznesowym (dotyczącym IT); oferowanie konkurencyjnych produktów i usług; ulepszenie oraz pielęgnacja funkcjonalności procesów biznesowych; zapewnienie odpowiedniego zwrotu z inwestycji (związanej z IT); pozyskanie,

rozwój

i

utrzymanie

zmotywowanych

ludzi

o specjalistycznych umiejętnościach; możliwość szybkiego biznesowych.

reagowania

na

zmiany

w

wymaganiach

W podręczniku [306] można znaleźć dokładne informacje o tym, jak poszczególne cele wpływają na priorytetyzację kluczowych obszarów.

Rysunek 32.9. TPI Next – przykładowa sekwencja doskonalenia oparta na klastrach Na rysunku 32.9 pokazano, jak koncepcja klastrów może pomóc w określeniu wykonania czynności doskonalących. Punkty kontrolne o jasnoszarym kolorze są spełnione (jest to sytuacja wzięta z rys. 32.7). W pierwszej kolejności dążymy do spełnienia punktów kontrolnych oznaczonych kolorem szarym – w ten sposób spełnimy wszystkie wymagania dla klastrów A, B i C. W drugiej kolejności

spełniamy punkty kontrolne oznaczone kolorem ciemnoszarym tak, aby wejść z procesem na poziom Kontrolowany. Sugestie poprawy i potencjały. Z każdym punktem kontrolnym TPI Next wiąże określone sugestie poprawy. O ile punkty kontrolne definiują, co należy osiągnąć, o tyle sugestie informują, jak może to być zrobione. Sugestie poprawy, poza pokazaniem drogi do spełnienia danego punktu kontrolnego, dostarczają również cennych wskazówek związanych z całym kluczowym obszarem, którego dotyczą. Potencjały wskazują, jak proces testowy oraz pozostałe procesy mogą razem działać i wspomagać się nawzajem. Kluczowe obszary. Dokładny opis kluczowych obszarów, punktów kontrolnych, sugestii poprawy oraz potencjałów Czytelnik znajdzie w [306]. Dalej opiszemy dokładniej tylko wybrane obszary. Strategia testowa. Wejściem dla strategii testowej jest analiza ryzyka produktowego. Na poziomie Kontrolowanym obszar Strategii testowej ma 4 punkty kontrolne. 1. Główny udziałowiec projektu zgadza się z udokumentowaną strategią testową. 2. Strategia testowa jest oparta na analizie ryzyka produktowego. 3. Istnieje rozróżnienie na poziomy testów, typy testów, pokrycie testowe i szczegółowość testów dla różnych poziomów ryzyka. 4. Istnieje strategia dla retestów oraz testów regresji. Na poziomie Efektywnym obszar Strategii testowej ma 3 punkty kontrolne. 1.

Wszyscy

istotni

udziałowcy

zgadzają

się

ze

zdefiniowaną

i udokumentowaną strategią testową. 2. Nakładanie się lub braki w pokryciu testowym między poziomami i typami testów są rozpoznane i brane pod uwagę. 3. Strategia testowa zawiera odpowiednie techniki projektowania testów. Na poziomie Zoptymalizowanym obszar Strategii testowej ma 2 punkty kontrolne. 1. Proces tworzenia strategii testowej jest regularnie poddawany ocenie oraz, jeśli to konieczne, adaptacji dla przyszłego użycia.

2. Strategia testowa jest oceniana na podstawie wewnątrzprocesowych3 metryk dla incydentów. Zarządzanie procesem testowym. Na poziomie Kontrolowanym obszar Zarządzania procesem testowym ma 4 punkty kontrolne. 1. Na początku projektu jest tworzony plan testów, zawierający co najmniej: cel testowania, zakres testów, planowanie testów, role oraz obowiązki. 2. Plan testów jest zatwierdzany przez głównego udziałowca. 3. Każda aktywność testowa jest monitorowana, a tam gdzie to konieczne, dostrajana i modyfikowana. 4. Plan testów jest zatwierdzony przez wszystkich ważnych udziałowców. Na poziomie Efektywnym obszar Zarządzania procesem testowym ma 3 punkty kontrolne. 1. Odchylenia od planu testów są dyskutowane z głównym udziałowcem oraz innymi istotnymi interesariuszami. 2. Modyfikacje planu testów są dokumentowane. 3. Kierownik testów jest upoważniony do (re-)alokowania środków/zasobów. Na poziomie Zoptymalizowanym obszar Zarządzania procesem testowym ma 2 punkty kontrolne. 1. Zarządzanie procesem testowania jest regularnie poddawane ocenie, zarówno wewnętrznej (przez samą organizację testową), jak i dokonywanej przez interesariuszy. 2. Z ukończonych projektów są wyciągane wnioski (ang. lessons learned) na przyszłość, w celu usprawniania procesu zarządzania. Szacowanie i planowanie. Na poziomie Kontrolowanym obszar Szacowania i planowania ma 4 punkty kontrolne. 1. Do szacowania wysiłku testowego są wykorzystywane proste techniki takie jak metryki ilorazowe.

2. Dla każdej aktywności testowej są określone: czas jej trwania, wymagane przez nią zasoby oraz oczekiwane produkty będące wynikiem jej przeprowadzenia. Aktywności, które należy rozważyć to: planowanie i zarządzanie testami, definiowanie wykonywanie przypadków testowych.

przypadków

testowych

oraz

3. Zależności między fazami testów lub aktywnościami testowymi są ujęte graficznie w planowaniu testów. Dozwolone jest nachodzenie na siebie poszczególnych faz lub aktywności. 4. Szacowanie i planowanie testów jest konsultowane z głównym udziałowcem. Na poziomie Efektywnym obszar Szacowania i planowania ma 4 punkty kontrolne. 1.

W celu

uzyskania

możliwie wysokiej dokładności

szacunków,



wykorzystywane co najmniej dwie techniki szacowania. 2. Fazy i/lub aktywności testowe są szacowane i planowane z wykorzystaniem technik formalnych. 3. Wykorzystuje się metryki do wspierania aktywności szacowania i planowania. 4. Planowanie testów uwzględnia przeglądy testowalności podstawy testów oraz ocenę projektu testowego. Na poziomie Zoptymalizowanym obszar Szacowania i planowania ma 3 punkty kontrolne. 1. Planowanie testów uwzględnia zachowanie (archiwizację) testaliów dla przyszłego, ponownego użycia. 2. Zbiór zasad, modeli i technik szacowania jest utrzymywany na poziomie całej organizacji. 3. Kluczowe wykresy i dane dla zdefiniowanych technik szacowania są udostępniane na poziomie całej organizacji. Metryki. Na poziomie Kontrolowanym obszar Metryk ma 3 punkty kontrolne.

1. Metryki są zdefiniowane i używane do szacowania i nadzoru projektu testowego. 2. Niezbędne dane wejściowe dla metryk są zbierane w sposób ujednolicony, a zdefiniowane metryki są systematycznie zachowywane. 3. Dane wejściowe dla metryk są dokładne i można to udowodnić. Na poziomie Efektywnym obszar Metryk ma 4 punkty kontrolne. 1. Wysiłek wymagany do zebrania, analizy oraz oceny niezbędnych danych jest mierzony względem potencjalnych zysków. 2. Zbieranie metryk nie przeszkadza ani nie wpływa negatywnie na postęp i jakość procesu testowego. 3. W procesie testowym metryki są zdefiniowane i używane do pomiaru efektywności tego procesu. 4. Wnioski z analizowanych metryk są dyskutowane z interesariuszami. Na poziomie Zoptymalizowanym obszar Metryk ma 2 punkty kontrolne. 1. Sposób, w jaki metryki przyczyniają się do dostarczania potrzebnych informacji jest monitorowany. 2. Zmiany w potrzebach informacyjnych prowadzą do definiowania nowych lub optymalizowania istniejących metryk. Profesjonalizm testerów. Na poziomie Profesjonalizmu testerów ma 4 punkty kontrolne. 1.

Testerzy

odbyli

odpowiednie

szkolenia

Kontrolowanym

i/lub

mają

obszar

wystarczające

doświadczenie w zakresie zorganizowanego testowania. 2. Testerzy są zaznajomieni z zaadaptowaną przez organizację metodą testową i ją stosują. 3. Wszystkie niezbędne źródła ekspertyz (przemysłowych, biznesowych, technicznych) są dostępne dla zespołu testowego. 4. Testerzy poddawani są regularnej, okresowej ocenie w zakresie umiejętności testerskich oraz ogólnych umiejętności informatycznych.

Na poziomie Efektywnym obszar Profesjonalizmu testerów ma 4 punkty kontrolne. 1. Testerzy są certyfikowani w testowaniu (zob. Dodatek D). 2. Testerzy potrafią wyjaśnić powody wykorzystania konkretnej techniki. 3. Zespół testowy lubi swoją pracę i ma dobre relacje z innymi zespołami w projekcie. 4. Zadania testerskie są definiowane, alokowane i wykonywane zgodnie z oczekiwaniami. Na poziomie Zoptymalizowanym obszar Profesjonalizmu testerów ma 3 punkty kontrolne. 1. Testerzy aktywnie uczestniczą w tzw. SIG (ang. Special Interest Groups), czyli grupach zainteresowań, biorą udział w seminariach, konferencjach i czytają literaturę przedmiotu, aby być na bieżąco z wszystkim, co dzieje się w świecie testerskim. 2. Funkcje (role) testowe są częścią organizacyjnego procesu zarządzania zasobami ludzkimi oraz elementem osobistej kariery zawodowej. 3. Testerzy są odpowiedzialni za swoją pracę oraz ciągłe udoskonalanie podległych im procesów. Projektowanie przypadków testowych. Na poziomie Kontrolowanym obszar Projektowania przypadków testowych ma 3 punkty kontrolne. 1. Przypadki testowe są tworzone na poziomie logicznym. 2. Przypadki testowe składają się z opisu: a) sytuacji początkowej, b) procesu zmiany, tzn. akcji do przeprowadzenia, c) oczekiwanego wyniku. 3. Przypadki testowe dają wgląd w to, która część podstawy testów, opisująca odpowiednie zachowanie systemu, jest przedmiotem testów. Na poziomie Efektywnym obszar Projektowania przypadków testowych ma 4 punkty kontrolne. 1. Przypadki testowe są zrozumiałe i pielęgnowalne przez współpracowników w ramach organizacji testowej.

2. Jest znany stopień pokrycia bazy testowej przez przypadki testowe. 3. Przypadki testowe są tworzone z wykorzystaniem formalnych technik projektowania testów (patrz rozdz. 8, 9, 10). 4. W testowaniu charakterystyk jakościowych, dla których nie jest możliwe stworzenie przypadków testowych wykorzystywane są listy kontrolne. Na poziomie Zoptymalizowanym obszar Projektowania przypadków testowych ma 3 punkty kontrolne. 1. Defekty pojawiające się w kolejnej fazie są analizowane, co prowadzi do usprawnień w dokładności i efektywności przypadków testowych. 2. Przypadki testowe są sprawdzane i poddawane ocenie niezależnie, pod kątem poprawności i utrzymywalności. 3. Techniki projektowania testów są oceniane i dostosowywane do przyszłego ponownego użycia.

32.4.2. TMMi (Test Maturity Model integration) Wprowadzenie. TMMi jest modelem referencyjnym zawartości z reprezentacją etapową. Został stworzony przez TMMi Foundation jako schemat udoskonalania procesu testowego, komplementarny do modelu CMMI w wersji 1.3. Podstawą do stworzenia TMMi był model TMM [325]. TMMi czerpie z wielu źródeł: ewolucji modelu testowego [19], modelu Beizera opisującego sposób myślenia testerów [14], standardów międzynarodowych takich jak IEEE 829 [5] czy słownika pojęć ISTQB [6]. Ponadto uwzględnia on wiele tzw. dobrych praktyk, których skuteczność została dowiedziona empirycznie w ciągu wielu lat ich stosowania. W przeciwieństwie do innych modeli (takich jak TPI Next czy TPI) TMMi nie koncentruje się na jednym lub kilku wybranych wyższych poziomach testowania, ale uwzględnia wszystkie poziomy: modułowe, integracyjne, systemowe i akceptacyjne. TMMi, zintegrowany model dojrzałości testów (ang. Test Maturity Model integration, TMMi) – pięciostopniowa struktura umożliwiająca doskonalenie procesu testowego, zgodna ze zintegrowanym modelem dojrzałości organizacyjnej, która opisuje kluczowe elementy efektywnego procesu testowego

Poziomy dojrzałości. TMMi definiuje 5 poziomów dojrzałości: 1. Początkowy. Testowanie jest chaotycznym, niezdefiniowanym procesem i często jest traktowane jako część debugowania. Testy są wytwarzane ad hoc po napisaniu kodu. Celem testowania jest pokazanie, że działa bez większych awarii. Nie występuje zarządzanie jakością ani ryzykiem. W zespole testowym brakuje zasobów, narzędzi i wykwalifikowanych pracowników. 2. Zarządzany. Na poziomie 2 testowanie staje się procesem zarządzanym. Jeśli porównamy obszary procesowe TMMi na poziomie Zarządzanym z Podstawowym Procesem Testowym ISTQB opisanym w podrozdziale 3.1, to zobaczymy, że osiągnięcie poziomu 2 TMMi wiąże się tak naprawdę z wdrożeniem w pełni tego procesu testowego. Na poziomie Zarządzanym jest zdefiniowana strategia testowa oraz plany testów, które określają podejście do testowania. Są używane techniki zarządzania ryzykiem do identyfikacji ryzyk produktowych. Testowanie jest monitorowane i nadzorowane pod kątem zgodności jego przebiegu z założonym planem. Są stosowane techniki projektowania testów ze specyfikacji. Testowanie może jednak wciąż rozpoczynać się późno w cyklu życia, tzn. podczas projektu lub nawet podczas fazy kodowania. Główny cel testowania na poziomie 2 to weryfikacja spełnienia wymagań nałożonych na oprogramowanie. 3. Zdefiniowany. Na tym poziomie testowanie nie jest już postrzegane jako faza następująca po kodowaniu. Testowanie jest w pełni zintegrowane z cyklem wytwórczym oraz kamieniami milowymi projektu. Planowanie testów odbywa się we wczesnych fazach (np. podczas fazy wymagań) i jest dokumentowane w głównym planie testów. Istnieje wydzielona organizacja testowa oraz programy szkoleniowe związane z testowaniem. Testowanie postrzegane jest jako pełnoprawna rola w procesie wytwórczym. Udoskonalanie procesu testowego jest w pełni zinstytucjonalizowane jako część praktyk stosowanych w organizacji. Organizacja na poziomie 3 rozumie rolę przeglądów w zapewnianiu jakości. Przeglądy są stosowane w ciągu całego cyklu życia, na wszystkich etapach. Testowanie – w porównaniu z poziomem 2 – jest rozszerzone o testy charakterystyk jakościowych, czyli o testowanie niefunkcjonalne. Ponadto, na poziomie 3 standardy, opisy procesów i procedury nie są definiowane niezależnie dla

każdego projektu, jak to ma miejsce na poziomie 2, ale są wywodzone ze zbioru standardowych procesów zdefiniowanych na poziomie całej organizacji. 4. Mierzalny. Mając infrastrukturę techniczną, zarządczą i organizacyjną, zagwarantowaną przez poziomy 2 i 3, na poziomie 4 testowanie może być – z wykorzystaniem tej infrastruktury – mierzone, staje się więc dobrze zdefiniowanym, mierzalnym procesem. Na poziomie organizacji jest ustanowiony program pomiarów, który pozwala na kontrolę poziomu jakości procesu testowego, ocenę produktywności oraz monitorowanie usprawnień. Jakościowe atrybuty produktów procesu testowego są oceniane ilościowo. Przeglądy, wymagane na poziomie 3, stają się w pełni zintegrowane z dynamicznymi procesami testowymi, tzn. są elementem strategii, planu i podejścia do testów. 5. Optymalizujący. Osiągnięcie czterech pierwszych poziomów umożliwia organizacji wdrożenie czynności w sposób ciągły doskonalących istniejące procesy. Zwiększanie wydajności procesu testowego jest uzyskiwane za pomocą przyrostowych i innowacyjnych procesów oraz usprawnień technologicznych. Metody i techniki testowania są optymalizowane. Proces jest statystycznie kontrolowalny i skupia się na zapobieganiu defektów. Wykorzystuje techniki i narzędzia statystyczne takie jak: próbkowanie, testy statystyczne, przedziały ufności, modele predykcyjne itd. Jest wspierany przez automatyzację oraz przez efektywne wykorzystanie zasobów. Może przeprowadzić transfer technologii z przemysłu do organizacji testowej i wspiera ponowne użycie wytworzonych testaliów. W organizacji testowej ustanawia się formalnie grupę ds. doskonalenia procesu testowego, która identyfikuje, planuje i wdraża ulepszenia procesu. Struktura TMMi, opisująca poziomy dojrzałości wraz z odpowiadającymi im obszarami procesowymi pokazana jest na rysunku 32.10.

Rysunek 32.10. TMMi – poziomy dojrzałości i odpowiadające im obszary procesów Struktura modelu. Struktura TMMi bazuje na strukturze CMMI, dzięki czemu w organizacjach zaznajomionych z CMMI zrozumienie i wdrożenie TMMi jest łatwiejsze. Istnieje wyraźny rozdział między praktykami wymaganymi do wdrożenia (cele, ang. goals) a rekomendowanymi (specyficzne praktyki, przykładowe produkty prac itp.). Model TMMi składa się z następujących typów komponentów: komponenty wymagane – opisują, co organizacja musi osiągnąć, aby spełnić wymagania danego obszaru procesowego; osiągnięcia te muszą

być w widoczny sposób zaimplementowane w procesach organizacji; komponentami wymaganymi są cele specyficzne oraz cele generyczne; komponenty oczekiwane – opisują, co organizacja implementuje, aby spełnić określone wymagania;

zwykle

komponenty informacyjne – opisują szczegółowe kwestie pomagające organizacjom zdefiniować podejście do zaimplementowania wymaganych i oczekiwanych komponentów. Sam model składa się z następujących komponentów: poziomy dojrzałości – opisują kolejne stopnie jakości organizacyjnego procesu testowego; obszary procesowe – obszary, które wchodzą w skład poszczególnych poziomów dojrzałości; cel – wyjaśnia rolę danego obszaru w procesie testowym; jest komponentem informacyjnym; uwagi wstępne – odnoszą się do poszczególnych obszarów procesowych, opisują główne idee zawarte w tych obszarach; są komponentami informacyjnymi; zakres – odnosi się do danego obszaru procesowego i opisuje praktyki testowe odpowiednie dla niego; cele specyficzne – opisują cechy procesu, które muszą być obecne, aby spełnić wymagania danego obszaru procesowego; cel specyficzny jest komponentem wymaganym; cele generyczne – pojawiają się na końcu obszarów procesowych; nazwa pochodzi stąd, że cel taki występuje w wielu różnych obszarach procesowych; cel generyczny opisuje cechy, których obecność jest konieczna do zinstytucjonalizowania procesów w obrębie danego obszaru; są wymaganymi komponentami modelu; specyficzne praktyki – opisują działania ważne dla osiągnięcia związanego z nimi celu specyficznego; są komponentami oczekiwanymi; przykładowe produkty prac – zbiór przykładowych rezultatów wynikających z zastosowania określonej specyficznej praktyki; są komponentami informacyjnymi;

pod-praktyki – szczegółowe opisy pomagające zinterpretować i zaimplementować określoną praktykę specyficzną; są komponentami informacyjnymi; praktyki generyczne – pojawiają się na końcu obszarów procesowych; nazwa pochodzi stąd, że praktyka taka występuje w wielu różnych obszarach procesowych; omówienie praktyk generycznych – podpowiadają, jak praktyki generyczne powinny być wdrażane; są komponentami informacyjnymi. Struktura modelu TMMi schematycznie jest pokazana na rysunku 32.11.

Rysunek 32.11. TMMi – struktura modelu Cele i praktyki generyczne. TMMi wyróżnia dwa cele generyczne: 1. Ustanowienie zarządzanego procesu. 2. Ustanowienie zdefiniowanego procesu. Proces zarządzany to taki, który pozwala realizować pracę potrzebną do wytworzenia określonych produktów. Jest planowany i wykonywany zgodnie

z

ustalonymi

zasadami

przez

wykwalifikowanych

pracowników

i

ma

odpowiednie zasoby do realizacji kontrolowanych celów. Proces zdefiniowany to proces zarządzany, skrojony do potrzeb ze zbioru standardowych procesów organizacyjnych. Proces zdefiniowany – w odróżnieniu od zarządzanego – jest stosowany w obrębie całej organizacji, a nie tylko w pojedynczym projekcie. TMMi określa 10 praktyk generycznych dla celu Ustanowienie zarządzanego procesu: ustanowienie polityki organizacyjnej, planowanie procesu, dostarczenie zasobów, przypisanie odpowiedzialności, szkolenie ludzi, zarządzanie konfiguracją, identyfikacja i zaangażowanie odpowiednich interesariuszy, nadzór i kontrola nad procesem, obiektywna ewaluacja zgodności działań z procesem oraz przeglądy projektu z udziałem kierownictwa wyższego szczebla. TMMi określa 2 praktyki generyczne dla celu Ustanowienie zdefiniowanego procesu: wdrożenie zdefiniowanego procesu oraz zbieranie informacji dotyczących doskonalenia. Wsparcie CMMI. Jeśli TMMi jest wdrażany w organizacji wykorzystującej już model CMMI, to pewne obszary procesowe CMMI mogą wspierać procesy zdefiniowane w TMMi. Przykładami takich procesów CMMI są: Zarządzanie konfiguracją, Zapewnianie jakości procesu i produktu, Nadzór i kontrola nad projektem, Planowanie projektu, Pomiary i analizy, Zarządzanie wymaganiami, Rozwój wymagań, Zarządzanie ryzykiem, Organizacyjna definicja procesu, Organizacyjne szkolenia, Weryfikacja, Walidacja, Ilościowe zarządzanie projektem, Analiza przyczyn i rozwiązań. Obszary procesowe, cele i praktyki na poziomach dojrzałości 2–5. W tabeli 32.2 opisano obszary procesowe poziomów dojrzałości 2–5, ich specyficzne cele oraz specyficzne praktyki. Tabela 32.2. Obszary procesowe TMMi oraz ich cele i praktyki specyficzne

Obszar procesowy

Specyficzne cele

Us tanowienie polityki tes tów

Specyficzne praktyki Zdefiniowanie celów tes towania Zdefiniowanie polityki tes tów

Rozes łanie polityki do udziałowców 2.1. Polityka i s trateg ia tes tów

Przeprowadzenie g enerycznej oceny ryzyka Us tanowienie s trateg ii Zdefiniowanie s trateg ii tes tów tes tów Rozes łanie s trateg ii do udziałowców Us tanowienie ws kaźników wydajnoś ci tes tów

Zdefiniowanie ws kaźników wydajnoś ci tes tów Wdrożenie ws kaźników wydajnoś ci tes tów

Przeprowadzenie s zacowania ryzyka produktoweg o

Zdefiniowanie kateg orii i parametrów ryzyk Identyfikacja ryzyk produktowych Analiza ryzyk produktowych

Us tanowienie podejś cia do tes towania

Identyfikacja cech i elementów tes towych Zdefiniowanie podejś cia do tes towania Zdefiniowanie kryteriów wejś cia Zdefiniowanie kryteriów wyjś cia Zdefiniowanie kryteriów zawies zenia i wznowienia Us talenie s truktury podziału prac

2.2. Planowanie tes tów

2.3. Kontrola i nadzór nad tes tami

Us tanowienie s zacowania tes tów

Zdefiniowanie cyklu życia tes tów S zacowanie wys iłku i kos ztu tes tów

Opracowanie planu tes tów

Us tanowienie harmonog ramu tes tów Planowanie przydziału zas obów ludzkich Planowanie zaang ażowania interes arius zy Identyfikacja ryzyk projektu tes toweg o Us tanowienie planu tes tów

Uzys kanie zaang ażowania dla planu tes tów

Przeg ląd planu tes tów Uzys kanie zg ody dla etapów prac i zas obów Uzys kanie zaang ażowania dla planu tes tów

Monitorowanie pos tępów tes tów wobec planu

Monitorowanie parametrów planowania tes tów Monitorowanie danych i używanych zas obów ś rodowis ka tes toweg o Monitorowanie zaang ażowania w tes towanie Monitorowanie ryzyk

projektu tes toweg o Monitorowanie udziału interes arius zy Przeprowadzenie przeg lądów pos tępu tes tów Przeprowadzenie przeg lądów kamieni milowych pos tępu tes tów

Monitorowanie jakoś ci produktu wobec planu i oczekiwań

Kontrola kryteriów wejś cia Monitorowanie defektów Monitorowanie ryzyk produktowych Monitorowanie kryteriów wyjś cia Monitorowanie kryteriów zawies zenia i wznowienia Przeprowadzenie przeg lądów jakoś ci produktu Przeprowadzenie przeg lądów kamieni milowych jakoś ci produktu

Zarządzanie akcjami naprawczymi

Analiza problemów Podjęcie akcji naprawczej Zarządzanie akcją naprawczą

Przeprowadzenie analizy i projektu

Identyfikacja i priorytetyzacja warunków tes towych Identyfikacja

z wykorzys taniem i priorytetyzacja technik projektowania przypadków tes towych Identyfikacja koniecznych tes tów danych tes towych Utrzymanie ś ledzenia powiązań z wymag aniami

Przeprowadzenie implementacji tes tów

S tworzenie i priorytetyzacja procedur tes towych S tworzenie s pecyficznych danych tes towych S pecyfikacja procedur tes tów dymnych Opracowanie harmonog ramu wykonania tes tów

Przeprowadzenie wykonania tes tów

Przeprowadzenie tes tów dymnych Wykonanie przypadków tes towych Raportowanie incydentów Log owanie wyników tes towania

2.4. Projektowanie i wykonanie tes tów

Zarządzanie incydentami

Decydowanie o przekazywaniu incydentów radzie kontroli konfig uracji Przeprowadzenie odpowiednich akcji w celu zamknięcia incydentu

Ś ledzenie s tatus u incydentu

Opracowanie wymag ań na ś rodowis ko tes towe

2.5. Ś rodowis ko tes towe

Pozys kanie potrzeb dot. ś rodowis ka tes toweg o Opracowanie wymag ań na ś rodowis ko tes towe Analiza wymag ań na ś rodowis ko tes towe

Implementacja ś rodowis ka tes toweg o Tworzenie g enerycznych danych tes towych Implementacja S pecyfikacja procedury ś rodowis ka tes toweg o tes tów dymnych dla ś rodowis ka tes toweg o Przeprowadzenie tes tów dymnych dla ś rodowis ka Zarządzanie s ys temami Zarządzanie danymi tes towymi Koordynacja dos tępnoś ci Zarządzanie i kontrola i użycia ś rodowis k ś rodowis kiem tes towych tes towym Raportowanie i zarządzanie incydentami związanymi ze ś rodowis kiem tes towym Zdefiniowanie org anizacji tes towej Uzys kanie ws parcia dla

Us tanowienie org anizacji tes towej

org anizacji tes towej Implementacja org anizacji tes towej

Us tanowienie funkcji tes towych dla s pecjalis tów

Identyfikacja funkcji tes towych Opracowanie opis ów s tanowis k Przypis anie pers onelu do ról (funkcji) tes towych

Us tanowienie ś cieżki kariery

Us tanowienie ś cieżki kariery dla tes terów Opracowanie planów rozwoju kariery dla pos zczeg ólnych os ób

Okreś lenie, plan i implementacja udos konaleń proces u tes toweg o

Ocena proces ów tes towych w org anizacji Identyfikacja udos konaleń proces ów tes towych w org anizacji Zaplanowanie uleps zeń proces u tes toweg o Implementacja uleps zeń proces u tes toweg o

3.1. Org anizacja tes towa

Wdrożenie org anizacyjnych proces ów tes towych

Wdrożenie s tandardowych proces ów tes towych Monitorowanie implementacji Włączenie proces u wyciąg ania wnios ków

(ang . lessons learned) do org anizacyjneg o proces u tes toweg o

Us tanowienie org anizacyjnych możliwoś ci s zkoleń

Identyfikacja s trateg icznych potrzeb s zkoleniowych Dopas owanie projektowych i org anizacyjnych potrzeb s zkoleniowych dotyczących tes towania Us tanowienie planu s zkoleń z tes towania w org anizacji Umożliwienie s zkoleń z tes towania

Dos tarczenie s zkoleń

Dos tarczenie s zkoleń z tes towania Dokumentowanie przebieg u s zkoleń Ocena efektywnoś ci s zkoleń

3.2. Prog ram s zkoleń

Us tanowienie aktywów

Us tanowienie s tandardowych proces ów tes towych Us tanowienie modelu cyklu życia tes tów z opis em ws zys tkich poziomów tes towania Us tanowienie kryteriów dopas owania proces u

3.3. Cykl życia tes towania i integ racja

org anizacyjneg o proces u tes towania

Integ racja cyklu życia tes tów z modelem wytwórczym

Us tanowienie g łówneg o planu tes tów

Us tanowienie org anizacyjnej bazy danych proces u tes toweg o Us tanowienie org anizacyjnej biblioteki aktywów proces u tes towania Us tanowienie s tandardów ś rodowis ka pracy

Us tanowienie zinteg rowanych modeli cyklu życia Przeg ląd zinteg rowanych modeli cyklu życia Uzys kanie zaang ażowania dla roli tes towania w ramach zinteg rowaneg o modelu cyklu życia Przeprowadzenie oceny ryzyka produktoweg o Us tanowienie podejś cia do tes towania Us tanowienie es tymat do tes towania Zdefiniowanie org anizacji dla tes towania Opracowanie g łówneg o planu tes tów Uzys kanie zaang ażowania dla g łówneg o planu tes tów

3.4. Tes towanie niefunkcjonalne

Przeprowadzenie oceny niefunkcjonalneg o ryzyka produktoweg o

Identyfikacja niefunkcjonalnych ryzyk produktowych Analiza niefunkcjonalnych ryzyk produktowych

Us tanowienie podejś cia do tes tów niefunkcjonalnych

Identyfikacja cech podleg ających tes towaniu Zdefiniowanie podejś cia do tes towania niefunkcjonalneg o Zdefiniowanie kryteriów wyjś cia dla tes towania niefunkcjonalneg o

Przeprowadzenie analizy i projektu tes tów niefunkcjonalnych

Identyfikacja i priorytetyzacja niefunkcjonalnych warunków tes towych Identyfikacja i priorytetyzacja niefunkcjonalnych przypadków tes towych Identyfikacja niezbędnych, s pecyficznych danych tes towych Utrzymanie ś ledzenia ws teczneg o do wymag ań niefunkcjonalnych Opracowanie i priorytetyzacja procedur tes towania

Implementacja tes tów niefunkcjonalnych

Wykonanie tes tów niefunkcjonalnych

Wykonanie niefunkcjonalnych przypadków tes towych Raportowanie niefunkcjonalnych incydentów Log owanie wyników tes tów niefunkcjonalnych

Us tanowienie podejś cia do przeg lądów

Identyfikacja produktów podleg ających przeg lądom Zdefiniowanie kryteriów przeg lądu

Przeprowadzenie przeg lądów

Przeprowadzenie przeg lądów Przeg ląd dokumentów bazy tes towej przez tes terów Analiza danych z przeg lądów

Uzg odnienie czynnoś ci pomiarów i analizy tes tów

Us tanowienie celów pomiaru tes tów Wys pecyfikowanie miar tes towych Okreś lenie kolekcji danych i procedur archiwizacji Okreś lenie procedur analiz

3.5. Przeg lądy

4.1. Pomiary tes tów

niefunkcjonalneg o S tworzenie s pecyficznych danych tes towych

Zebranie danych o pomiarach tes tów Dos tarczenie wyników Analiza danych o pomiarach tes tów pomiarów tes tów Komunikowanie wyników Zachowanie danych i wyników

4.2. Ocena jakoś ci oprog ramowania

Us tanowienie celów projektowych dla jakoś ci produktu

Identyfikacja potrzeb jakoś ciowych produktu Zdefiniowanie iloś ciowo celów jakoś ci produktu Okreś lenie podejś cia pomiaru pos tępu os iąg ania celów jakoś ciowych produktu

S kwantyfikowanie i zarządzanie pos tępem dla os iąg nięcia celów jakoś ciowych produktu

Pomiar iloś ciowy jakoś ci produktu w ciąg u całeg o cyklu życia Analiza pomiarów jakoś ci produktu i porównywanie ich z iloś ciowymi celami

Koordynacja przeg lądów z tes tami dynamicznymi

Odnies ienie produktów pracy do elementów i cech podleg ających tes towaniu Zdefiniowanie s koordynowaneg o podejś cia do tes tów Zdefiniowanie ws kazówek dla pomiaru przeg lądów

4.3. Zaawans owane przeg lądy

Zdefiniowanie kryteriów Wczes ny pomiar jakoś ci produktu przez przeg lądów na pods tawie celów jakoś ciowych przeg lądy produktu Pomiar jakoś ci produktów pracy przez przeg lądy Wczes ne dos tos owanie podejś cia tes toweg o na pods tawie przeg lądów

Analiza wyników przeg lądów Przeg ląd ryzyk produktowych, jeś li konieczny Przeg ląd podejś cia do tes towania, jeś li konieczne

Us talenie ws pólnych przyczyn defektów

Zdefiniowanie parametrów wyboru defektów oraz s chematu klas yfikacji defektów Wybór defektów do analizy Analiza przyczyn wybranych defektów

Priorytetyzacja i działania na rzecz eliminacji przyczyny źródłowej

Proponowanie rozwiązań w celu eliminacji ws pólnych przyczyn Zdefiniowanie propozycji działań i zg łos zenie propozycji us prawnień

5.1. Zapobieg anie defektom

Us talenie celów wydajnoś ci proces u tes toweg o Us talenie miar wydajnoś ci

5.2. Kontrola jakoś ci

Us tanowienie s tatys tycznej kontroli proces u tes toweg o

proces u tes toweg o Us talenie odnies ienia (ang . baseline) dla wydajnoś ci proces u tes toweg o Wykorzys tanie metod s tatys tycznych do zrozumienia wariancji proces u Monitorowanie wydajnoś ci dla wybranych proces ów tes towych

Tes towanie przeprowadzane przy użyciu metod s tatys tycznych

Opracowanie profili operacyjnych Generacja i wykonanie przypadków tes towych wybranych s tatys tycznie Zas tos owanie danych s tatys tycznych do podejmowania decyzji o zakończeniu tes towania

Wybór uleps zeń proces u tes toweg o

Zebranie i analiza propozycji uleps zeń proces u Pilotażowe zas tos owanie uleps zeń Wybór uleps zeń do wdrożenia

Ocena wpływu nowych technolog ii

Identyfikacja i analiza nowych technolog ii tes towych Pilotażowe wykorzys tanie

5.3. Optymalizacja proces u tes toweg o

tes towych na proces tes towy

technolog ii tes towych Wybór technolog ii do wdrożenia

Wdrożenie uleps zeń

Planowanie wdrożenia Zarządzanie wdrożeniem Pomiar efektów wdrożenia

Us tanowienie reużywalnych tes taliów

Identyfikacja reużywalnych tes taliów Wybór tes taliów dodanych do biblioteki dla ponowneg o użycia Wdrożenie reużywalnych tes taliów Zas tos owanie reużywalnych tes taliów w projektach

Szczegółowe informacje o obszarach procesowych, celach, praktykach oraz sposobach ich wdrożenia Czytelnik znajdzie np. w [305].

32.4.3. Porównanie TPI Next i TMMi TPI Next oraz TMMi są dwoma najbardziej znanymi metodami doskonalenia procesu testowego na podstawie modelu. W tabeli 32.3, opracowanej na podstawie [296] i [219], jest przedstawione porównanie tych dwóch podejść.

32.5. Udoskonalanie procesu testowego – modele referencyjne zawartości 32.5.1. STEP (Systematic Test and Evaluation Process)

Wprowadzenie. Metoda STEP jest opisana w podręczniku [176]. Powstała jako „uzupełnienie” standardów IEEE dotyczących dokumentacji testowej. Standardy te dokładnie specyfikują, jakie dokumenty należy tworzyć, ale nie wskazują, jak to zrobić. Nie informują Tabela 32.3. Porównanie TPI Next i TMMi

Aspekt

T PI Next

T MMi

Reprezentacja

Etapowa lub ciąg ła

Etapowa

Metody tes towania

Wykorzys tuje g eneryczne praktyki zdefiniowane w TMap

Niezależna od konkretnych metod tes towania

Terminolog ia

Oparta na TMap [28]

Oparta na s tandardowej terminolog ii tes towej, ś ciś le związanej ze s łownikiem IS TQB

Nie ma formalnych związków Związek z konkretnym z proces em modelem, ale dos konalenia możliwe jes t wytwarzania mapowanie oprog ramowania (opis ane dla CMMI i IS O/IEC 15504)

16 kluczowych obs zarów

Ś ciś le związany z CMMI

S zczeg ółowe omówienie pewnej liczby obs zarów proces owych związanych z różnymi

Na co kładzie nacis k

związanych z tes towaniem

poziomami dojrzałoś ci org anizacji; s kupia s ię również na przeg lądach, kontroli jakoś ci, zapobieg aniu defektów i prog ramie pomiaru tes tów

Podejś cie

Oparte na celach (ang . businessS ilny nacis k na zaang ażowanie driven), kierownictwa inżyniers kie

Ws parcie narzędziowe

Dos tępny Narzędzie do oceny dos tępne darmowy arkus z podczas s zkoleń na audytorów s amooceny

Opublikowane benchmarki

Brak

Rozwój i popularnoś ć

S ilny w Europie, S zybko ros nąca popularnoś ć ale także w US A, w Europie, Indiach, Korei, Indiach, Chinach i Brazylii Chinach

Tak [326]

również, jak opracować i wdrożyć sam proces testowy. STEP jako model referencyjny zawartości nie jest zbiorem nakazów, lecz wskazówek, które można dostosować i wykorzystać według własnego uznania w swojej organizacji. Głównym celem, dla którego został stworzony, jest pomoc w organizacji procesu testowego, jednak może być również wykorzystywany w doskonaleniu tego procesu. STEP, proces systematycznego testowania i oceny (ang. Systematic Test and Evaluation Process, STEP) – ustrukturalizowana metodyka testowania, wykorzystywana również jako model referencyjny zawartości doskonalenia procesu testowania Struktura modelu. Elementy modelu STEP są pokazane na rysunku 32.12.

Rysunek 32.12. STEP – elementy modelu STEP wprowadza cztery role: menedżera, analityka, technika i kontrolera (ang. reviewer). Role analityka i technika są analogiczne do ról opisywanych w sylabusach ISTQB jako Analityk testów oraz Techniczny analityk testów. STEP przypisuje rolom następujące odpowiedzialności: menedżer (kierownik testów): komunikacja z interesariuszami;

planowanie,

koordynowanie,

analityk: szczegółowe planowanie, określanie celów testów, analiza, projektowanie i specyfikacja testów; technik: implementacja przypadków testowych, wykonywanie testów, sprawdzanie problemów;

wyników,

logowanie

rezultatów,

raportowanie

kontroler: sprawdzanie i ocena produktów prac. W podejściu STEP zakłada się, że projekt testowy jest dzielony na poziomy testów. Każdy poziom reprezentuje określone aktywności, które są wspólnie organizowane

Rysunek 32.13. STEP – struktura aktywności w podziale na poziomy testów i zarządzane. Przykładami mogą być klasyczne poziomy testów: jednostkowe, integracyjne, systemowe, akceptacyjne. Poziomy składają się z faz, fazy z aktywności, a aktywności z zadań. Architektura ta jest pokazana schematycznie na rysunku 32.13. Struktura pojedynczego poziomu testów w STEP jest przedstawiona w tabeli 32.4. Oczekuje się, że użytkownik zaadoptuje tę strukturę w swoim projekcie. Tabela 32.4. STEP – struktura poziomu testów

Faza

Główne czynności

Zadania

Us tanowienie g łówneg o planu tes tów na początku projektu (pokrywa

Proces dla analizy ryzyka produktoweg o Ryzyka projektowe, założenia, plany awaryjne Projektowanie

T ypowe produkty fazy

Lis ta ryzyk Główny plan tes tów (oparty na IEEE 829)

Planowanie ws zys tkie s trateg ii poziomy)

Opracowanie s zczeg ółowych planów

Pozys kanie tes taliów

g łówneg o planu tes tów Opracowanie planów s zczeg ółowych dla każdeg o poziomu, oparte na planie g łównym

Jednopoziomowy plan tes tów

Analiza tes tów w celu s tworzenia lis ty s priorytetyzowanych Okreś lenie celów celów Macierz tes towania Tworzenie lis ty identyfikowalnoś c celów tes towania i wymag ań dotyczących pokrycia

Projektowanie tes tów

Implementacja planów i projektów

Techniki projektowania tes tów

S konfig urowanie ś rodowis ka tes toweg o Zes poły tes towe

Dokumenty wg IEEE 829 S pecyfikacje dla projektu, przypadków i procedur tes towych

Wyniki oceny tes taliów

Narzędzia Ocena tes taliów

Wykonanie tes tów

Pomiar zachowania Weryfikacja odpowiednioś ci zbioru tes tów

Wykonanie przypadków tes towych Log owanie incydentów Raporty o s tatus ie tes tów

Raporty o incydentach Log i tes towe S tatus i wyniki tes tów

Pomiar efektywnoś ci Miary defektów tes tów Miary pokrycia

Ocena proces ów tes towania Udos konalanie Plan i tworzenia proces ów tes towych udos konalenia oprog ramowania Wykorzystanie STEP do udoskonalania procesu testowego. STEP wykorzystuje podejście analogiczne do modelu IDEAL, przy czym proces usprawniania dzieli na nieco więcej kroków: 1. Ocena bieżącego stanu wszystkich procesów testowych. 2. Opracowanie wizji oraz celów. 3. Sformułowanie i priorytetyzacja wymagań potrzebnych na przejście z kroku 1. do 2. 4. Ustanowienie projektu udoskonalania. 5. Opracowanie planu działania. Plan powinien zawierać harmonogram, budżet, ryzyka itp. 6. Inkrementalne wprowadzenie zmian (przy wykorzystaniu wdrożeń pilotażowych). 7. Pomiar wyników wprowadzonych zmian (porównanie wyników do kryteriów zaliczenia/niezaliczenia w planie testów – patrz p. 5).

8. Powrót do kroku 1.

32.5.2. CTP (Critical Testing Processes) Wprowadzenie. CTP to model referencyjny zawartości dla doskonalenia procesu testowego opracowany przez Blacka [309]. Skupia się na niewielkiej liczbie 11 najistotniejszych obszarów procesowych, co czyni model minimalistycznym i łatwym w użyciu. Istotność procesu jest zdefiniowana następująco: proces jest istotny (krytyczny), jeśli jego dobre wykonanie prawie zawsze zapewnia sukces, a złe wykonanie – prawie zawsze porażkę. Cechami charakterystycznymi procesów krytycznych są: częste wykonywanie (wpływa na wydajność), angażowanie dużej liczby osób (wpływa na komunikację i kohezję zespołu), widoczność dla współpracowników i kierownictwa (wpływa na zaufanie wśród pracowników) oraz powiązanie z sukcesem projektu (wpływa na efektywność). CTP jest elastyczny i umożliwia dostosowanie go do potrzeb organizacji oferując m.in. wiele ilościowych i jakościowych atrybutów dobrych procesów. CTP, krytyczne procesy testowania (ang. Critical Testing Processes, CTP) – model referencyjny zawartości, służący poprawie procesu testowania, zbudowany na podstawie dwunastu procesów krytycznych; zawierają one widoczne procesy, za pomocą których pracownicy i kierownictwo oceniają kompetencje oraz najważniejsze procesy, których wydajność wpływa na zysk i reputację organizacji Struktura modelu. Głównym procesem jest Testowanie, złożone z 11 krytycznych procesów ujętych w cztery grupy, odpowiadające czterem zasadniczym krokom procesu testowego: planowanie – czyli zrozumienie i oszacowanie wysiłku testowego; przygotowanie – czyli zdefiniowanie zespołów oraz środowiska testowego; wykonanie – czyli przeprowadzenie testowania i zebranie wyników; doskonalenie – czyli wyciągnięcie wniosków i ulepszanie. Schemat modelu CTP jest pokazany na rysunku 32.14.

Rysunek 32.14. CTP – model procesu Wykorzystanie metryk. CTP opisuje pewną liczbę metryk pozwalających w ilościowy sposób ująć ocenę krytycznych procesów testowania. Mogą one być użyte zarówno przez kierownika testów (np. w celu nadzoru czy kontroli), jak i do udoskonalania procesu testowego. Przykłady metryk są podane w tabeli 32.5. Tabela 32.5. CTP – przykładowe metryki

Krytyczny proces testowania

Przykładowe metryki

Planowanie: zrozumienie konteks tu

Rozumienie bieżąceg o s tanu projektu liczba znalezionych defektów liczba wykonanych przypadków tes towych

Planowanie: es tymacja tes tów

S zacowanie ROI dla proces u tes toweg o kos zt tes towania kos zt awarii

Dos konalenie: raportowanie

Pokazanie bieżąceg o s tanu projektu pokrycie tes towe dla każdej kateg orii ryzyka

wyników

Dos konalenie: raportowanie defektów

liczba znalezionych defektów w podziale na poziomy dotkliwoś ci Pomiar jakoś ci raportów o defektach częs toś ć ponowneg o otwarcia zg łos zenia o defekcie ś redni czas od otwarcia do zamknięcia zg łos zenia o defekcie

1 Liczba 3,4⋅10–6 oznacza pole pod krzywą rozkładu normalnego, które wychodzi poza ±6 odchyleń standardowych tego rozkładu. Więcej informacji o statystyce i rachunku prawdopodobieństwa jest podanych w Dodatku C. 2 W praktyce taki poziom jest nieosiągalny, dlatego częściej występującą odmianą metody jest „4,5 sigma”, zakładająca, że dopuszczalna wartość procesu może odbiegać od wartości średniej o 4,5 odchylenia w prawo lub w lewo. 3 Metryki wewnątrzprocesowe mierzą wszystko to, co dzieje się podczas procesu produkcyjnego, przed wydaniem produktu do klienta.

33. Podejście analityczne

33.1. Wprowadzenie O ile podejścia oparte na modelu mają charakter „proaktywny” (tzn. mówią, co należy zrobić, żeby było lepiej), o tyle metody analityczne są „reaktywne”, to znaczy oparte na problemie. Udoskonalenia, które metody te proponują, nie polegają na wykorzystaniu najlepszych praktyk, lecz na identyfikacji i usuwaniu istniejących problemów. Cechą charakterystyczną podejść analitycznych jest silne oparcie się na analizie zbieranych danych oraz analiza procesu testowego w celu znalezienia problematycznych obszarów testowych. Definicja i pomiar kluczowych parametrów procesu jest niezbędna do oceny tego, czy udoskonalenie zakończyło się sukcesem. Podejścia analityczne mogą być wykorzystywane równolegle z modelami opartymi na zawartości. Kierownicy testów stosują je na poziomie projektu, a osoby odpowiedzialne za doskonalenie – na poziomie procesu. W kolejnych rozdziałach omówimy trzy podejścia analityczne: analizę przyczynową; metodę Cel–Pytanie–Metryka (ang. Goal–Question–Metric); wykorzystanie metryk, miar i wskaźników.

33.2. Analiza przyczynowa 33.2.1. Opis metody Analiza przyczynowa to metoda usprawniania procesu przez znalezienie oraz eliminację przyczyny źródłowej występujących problemów. Dotarcie do źródeł

kłopotów powoduje, że likwidujemy rzeczywiste przyczyny, a nie tylko objawy. Raz wyeliminowana przyczyna zwykle już się nie pojawi, przez co osiągamy trwałą poprawę efektywności procesu. analiza przyczynowa (ang. casual analysis) – analiza defektów, mająca określić podstawowe (źródłowe) przyczyny [197] podstawowa przyczyna, przyczyna źródłowa (ang. root cause) – przyczyna defektu, której wyeliminowanie redukuje lub usuwa występowanie tego typu defektów [197] Główna trudność w znajdywaniu przyczyny źródłowej polega na tym, że gdy pojawi się problem, zwykle dysponujemy ogromną ilością informacji z nim związanych, pochodzących z różnych źródeł i niekoniecznie wiarygodnych. Przykładowe źródła to: komunikacja werbalna (np. dyskusja podczas przeglądowego, rozmowa w stołówce, telefon od klienta);

spotkania

komunikacja pisemna (e-maile, różnego rodzaju dokumentacja, raporty, skargi użytkowników, informacje z systemu zarządzania defektami itp.); metryki, miary i wskaźniki (zwykle w postaci raportów lub przez systemy business intelligence). Techniki wykorzystywane w analizie przyczyn pomagają przefiltrować informacje, ocenić ich wiarygodność oraz zwizualizować możliwe związki między widocznymi skutkami czy konsekwencjami a ukrytymi jeszcze ich przyczynami. Często przyczyna źródłowa nie jest bezpośrednią przyczyną sprawczą kłopotów, lecz powoduje ich wystąpienie pośrednio, przez ciąg kolejnych skutków, z których każdy jest przyczyną kolejnego. Warunkiem koniecznym dla efektywnego przeprowadzenia analizy źródłowej jest posiadanie dobrze działającego procesu zarządzania defektami bądź incydentami. Procedura przeprowadzenia analizy przyczynowej składa się z czterech następujących kroków: 1. Wybór elementów do analizy przyczynowej. 2. Zebranie i zorganizowanie informacji.

3. Identyfikacja przyczyny źródłowej przez analizę zebranych informacji. 4. Wyciągnięcie wniosków (np. znalezienie przyczyn generycznych). Opiszemy teraz bardziej szczegółowo każdy z tych kroków.

33.2.2. Wybór elementów do analizy przyczynowej W kontekście udoskonalania procesu testowego, elementy wybrane do analizy przyczynowej mogą należeć do jednej z następujących kategorii: defekty, awarie i incydenty (np. awaria systemu, defekt znaleziony podczas testowania, incydent podczas użytkowania aplikacji, zgłoszenie o niskiej wydajności systemu, błędy w danych wyjściowych); problemy procesowe zgłoszone przez interesariuszy (np. zgłoszenie o brakach lub niewystarczających informacjach zawartych w różnego rodzaju dokumentach, raportach bądź planach, kłopoty z dostępnością określonego oprogramowania, licencji, środowiska testowego, niska efektywność zespołu zidentyfikowana podczas spotkania retrospektywnego); problemy odkryte podczas przeprowadzania analizy (np. niski poziom wykrywania defektów w określonych częściach oprogramowania lub na określonych poziomach testów, zbyt wysoka wartość metryki MTTR (Mean Time To Repair), czyli czasu od zgłoszenia defektu do jego naprawienia itp.). retrospektywa projektu (ang. project retrospective) – ustrukturalizowane podejście do zapoznania się z wyciągniętymi wnioskami i stworzenia konkretnych planów działań mających na celu poprawę następnego projektu lub następnego etapu projektu Nie zawsze można i nie zawsze należy wybierać do analizy wszystko, co się da. Wybór właściwych elementów jest kluczowy dla powodzenia całej analizy. Zły wybór elementów doprowadzi do wykrywania przyczyn źródłowych mniej istotnych problemów, a dotarcie do prawdziwej przyczyny źródłowej będzie utrudnione lub wręcz niemożliwe. W proces identyfikacji elementów należy zaangażować odpowiednich interesariuszy, którzy mogą dysponować

merytoryczną wiedzą i będą w stanie pomóc prawidłowo określić, co należy poddać analizie. Pomocne mogą być również następujące narzędzia: analiza Pareto; kategoryzacja defektów; analiza statystyczna (np.: wariancja, wartości odstające); spotkania retrospektywne projektu. Analiza Pareto. Wykres Pareto jest wizualizacją tzw. zasady Pareto, która mówi, że zwykle około 20% przyczyn powoduje 80% skutków. W kontekście analizy przyczynowej zasada ta mówi, że około 20% defektów powoduje 80% awarii. Jeśli więc skupimy się na analizie i usunięciu tych 20% defektów, poradzimy sobie ze znakomitą większością problemów. Te 20% „reprezentatywnych” defektów może być postrzegane jako przyczyna źródłowa. analiza Pareto (ang. Pareto analysis) – statystyczna technika podejmowania decyzji, używana do wyboru ograniczonej liczby czynników, które powodują znaczący ogólny efekt; wyrażona w kategoriach poprawy jakości brzmi następująco: większość problemów (80%) jest powodowana przez niewielką liczbę przyczyn (20%) Aby skonstruować wykres Pareto, musimy podzielić istniejące, zidentyfikowane defekty na kilka kategorii i uszeregować te kategorie malejąco ze względu na ich rozmiar. Następnie wybieramy 20% najliczniejszych kategorii, które powinny zawierać około 80% wszystkich defektów. Te 20% kategorii poddajemy następnie analizie. Rozważmy następujący przykład: w fazie testów systemowych zidentyfikowano 325 defektów, z których każdy sklasyfikowano do jednej z 10 kategorii w następujący sposób: 150 to defekty dokumentacji; 100 to defekty interfejsu graficznego i użyteczności; 30 to defekty związane z raportowaniem; 15 to błędy stylistyczne w pomocy online; 12 to błędy przypisania i inne związane z kodowaniem; 11 to defekty dotyczące bezpieczeństwa; 3 to defekty związane z wydajnością;

2 to błędy związane z instalacją; 1 to błąd arytmetyczny (dzielenie przez 0); 1 to defekt związany z współdziałaniem. Na rysunku 33.1 przedstawiono wykres Pareto dla tej sytuacji. Kategorie zostały posortowane malejąco. Linia ciągła oznacza skumulowany odsetek defektów, np. błędy dokumentacji stanowią 150/325 ≈ 46% wszystkich defektów, błędy GUI – ok. 31%, zatem skumulowana wartość tych dwóch pierwszych kategorii to 46% + 31% = 77%. Oczywiście wartość skumulowana osiąga 100% przy ostatniej kategorii. Dwie pierwsze kategorie stanowią 20% spośród wszystkich dziesięciu kategorii. Łącznie zawierają 77% błędów. Istnieje więc przesłanka, że błędy dokumentacji oraz GUI są główną przyczyną naszych problemów i w tych dwóch obszarach należy przede wszystkim przedsięwziąć środki zaradcze (np. inspekcje dokumentacji, testy użyteczności GUI, prototypowanie interfejsu użytkownika). Kategoryzacja defektów. Problemem w analizie Pareto jest to, że musimy poświęcić czas na dokonanie klasyfikacji defektów. Co więcej, klasyfikacja taka jest sama w sobie uznaniowa. Zamiast tworzyć ją samodzielnie, możemy wykorzystać istniejące, predefiniowane klasyfikacje defektów, w szczególności możemy wspomóc się metodą Orthogonal Defect Classification omówioną w punkcie 27.2.2. Wykorzystanie istniejących typologii niesie ze sobą kilka zalet [219]: możemy odkryć powracające przyczyny źródłowe defektów, które występują w naszej organizacji, projekcie lub dziedzinie biznesowej produktu; jeśli czynności doskonalące zostały wdrożone w celu redukcji częstości występowania tych defektów, to będą one dla nas szczególnie istotne do zidentyfikowania; będziemy świadomi istnienia konkretnych typów defektów, które nas w danej chwili szczególnie interesują;

Rysunek 33.1. Analiza Pareto Tabela 33.1. Przykłady kategoryzacji defektów dla ich wyboru w analizie przyczynowej

Kategoria defektów

Opis

Te defekty s ą pods tawowym źródłem dla analizy przyczyn. Wpływ (dotkliwoś ć) może być oparty na s chemacie klas yfikacji taki jak IEEE 1044 lub być Wys oki wpływ wywiedziony z opis u defektów jako częś ć analizy. Ocena ryzyka może być cennym narzędziem do oceny wpływu pos zczeg ólnych defektów. Identyfikacja i naprawa przyczyny źródłowej dla częs to Wys oka powracających defektów będzie bardziej wartoś ciowa, częs totliwoś ć niż identyfikacja przyczyny źródłowej dla pojawiania s ię pojedynczych, pojawiających s ię tylko raz defektów.

Awarie w produkcji

Defekty powodujące awarie podczas użytkowania oprog ramowania s ą g łównymi kandydatami do wyboru i analizy zwłas zcza, jeś li oczekuje s ię, że tes towanie powinno zapobiec tym typom awarii.

Metoda wykrycia

Defekty wykryte za pomocą analizy s tatycznej i przeg lądów mog ą dos tarczyć więcej ws kazówek związanych z przyczynami źródłowymi (np. źle wys pecyfikowane wymag ania, kieps ki s tyl kodowania) niż w przypadku tes tów dynamicznych. Defekty znalezione podczas tes tów reg res ji mog ą ujawnić problemy związane z zarządzaniem konfig uracją lub nis ką jakoś ć projektu oprog ramowania (np. zbyt wys okie powiązanie (ang . coupling) modułów).

Powiązane atrybuty jakoś ciowe

Defekty zwykle związane s ą z okreś lonymi atrybutami jakoś ciowymi, np. funkcjonalnoś cią, wydajnoś cią, użytecznoś cią, niezawodnoś cią. Do analizy możemy wybrać defekty z ws zys tkich tych g rup, aby s prawdzić, czy wykorzys tana s trateg ia tes towa nie pomija okreś loneg o typu defektów, a co za tym idzie – okreś lonej charakterys tyki jakoś ciowej.

Poziom dos tępnej informacji

Łatwiej znajdziemy przyczynę źródłową dla defektów, które s ą dobrze udokumentowane. Ta kateg oryzacja s kupia s ię na efektywnoś ci (ROI) przeprowadzania s amej analizy przyczynowej.

Defekty s pecyficzne dla dziedziny

Ta kateg oria może być dzielona na mniejs ze, aby pokryć s zeroki zakres as pektów zależnych od dziedziny produktu.

Defekty związane ze s tandardami

Każdy defekt ws kazujący niezg odnoś ć ze s tos owanymi/wymag anymi s tandardami jes t kandydatem do analizy przyczynowej.

Defekty związane z retes tami

Wybór defektów z tej kateg orii może ujawnić problemy związane z org anizacją tworzenia oprog ramowania lub niezg odnoś ci w konfig uracji infras truktury dla tes towania (np. związane z g eneracją danych tes towych, ś rodowis kiem tes towym, zautomatyzowanymi s kryptami).

Defekty odrzucone przez prog ramis tów

Analiza tych defektów może ujawnić problemy związane z opis ywaniem defektów zg łas zanych przez tes terów lub z inną interpretacją wymag ań przez tes terów i deweloperów.

Defekty wielokrotnie poprawiane

Wybór tych defektów do analizy przyczynowej może ujawnić przyczyny źródłowe podobne do tych z poprzedniej kateg orii. Ponadto mog ą zos tać odkryte problemy z komunikacją między członkami zes połu.

możemy wykorzystać typologię jako swoistą listę kontrolną na użytek inspekcji lub spotkania retrospektywnego dla projektu. W tabeli 33.1 podajemy za Bathem i van Veenendaalem [219] przykładowe kategoryzacje defektów. Analiza statystyczna. Istnieje wiele różnych metod statystycznych pomagających w wyborze defektów do analizy przyczynowej. Informacja będąca rezultatem analizy statystycznej może przyjąć formę wyników testów statystycznych, porównania parametrów, wykresów X–Y, rozkładów prawdopodobieństw i innych. Kilka przykładów analizy statystycznej przedstawiamy dalej. Identyfikacja grup defektów. Grupy gromadzących się defektów można rozumieć w dwóch wymiarach: przestrzennym i czasowym. Do identyfikacji grup przestrzennych może służyć zwykły histogram opisujący liczność defektów ze względu na miejsce ich

Rysunek 33.2. Przestrzenna i czasowa identyfikacja grup defektów wystąpienia. Miejscem tym może być poziom testów, faza testów, konkretny moduł itp. Sytuację taką przedstawia górny wykres na rysunku 33.2, zbudowany dla systemu ELROJ z Dodatku A. Widzimy z niego, że największa gęstość defektów występuje dla modułów getDisplayString oraz getNextBusTime. Do identyfikacji grup w wymiarze czasowym można wykorzystać wykres przebiegu, który przedstawia zmianę metryki w czasie. Na dolnym wykresie rysunku 33.2 przedstawiono liczbę defektów odrzuconych przez programistów

w poszczególnych tygodniach projektu. Widzimy, że od 19. tygodnia liczba ta znacząco wzrosła. Defekty odrzucone w tygodniach 19–25 (lub ich losowo wybrana próba) są naturalnymi kandydatami do analizy przyczynowej. Wykorzystanie testów statystycznych. Testy statystyczne można przeprowadzać, aby wykazać np. istotność statystyczną różnic w parametrach poszczególnych grup. Rozważmy organizację mającą dwa zespoły wsparcia technicznego, które zajmują się usuwaniem defektów. Załóżmy, że dysponujemy średnim czasem usuwania defektów (wyrażonym w dniach) dla obu tych zespołów na przestrzeni 14 miesięcy. Chcemy sprawdzić, czy szybkość usuwania defektów w obu zespołach jest taka sama, czy też jeden zespół usuwa je szybciej niż drugi. W listingu 33.1 przedstawiono fragment sesji z programu R Statistical Pod zmienne ZespolA i ZespolB przypisaliśmy wektory liczb

package.

określających średnie czasy usuwania defektów w poszczególnych miesiącach dla obu zespołów, a następnie wywołaliśmy funkcję t.test, która przeprowadza test statystyczny t-Studenta. Test ten sprawdza statystyczną istotność różnicy średnich dla obu próbek.

> ZespolA ZespolB t.test(ZespolA, ZespolB, paired=FALSE) Welch Two Sample t-test data: ZespolA and ZespolB t = 3.1953, df = 25.457, p-value = 0.003706 alternative hypothesis: true difference in means is not equal to 0 95 percent confidence interval: 0.5279466 2.4377677 sample estimates: mean of x mean of y

5.072857

3.590000

Listing 33.1. Test statystyczny t-Studenta dla czasu naprawy defektów Z analizy wynika, że średni czas usuwania defektu w zespole A wynosi 5,07 dnia, natomiast w zespole B tylko 3,59. Aby sprawdzić, czy różnica ta jest istotna i nie jest np. wynikiem przypadku, przeprowadziliśmy test, w którym wartość p jest znacznie mniejsza niż 0,051. Oznacza to, że odrzucamy hipotezę (na poziomie ufności 1 – 0,05 = 95%) mówiącą o równym czasie naprawy defektów i wnioskujemy, że zespół B pracuje efektywniej niż zespół A. Do analizy przyczynowej możemy wybrać losową próbę defektów zgłoszonych zarówno do A, jak i do B i przeanalizować procesy w obu zespołach. Zespół A może pracować mniej efektywnie np. ze względu na trudniejszy projekt, ale problem może też być natury organizacyjnej lub personalnej (np. mniejsze doświadczenie pracowników zespołu A). Wykorzystanie rozkładów prawdopodobieństwa. Technika ta służy do wyboru defektów do analizy przyczynowej na podstawie wartości odstających. Załóżmy, że czas naprawy defektu ma rozkład normalny o średniej µ i odchyleniu standardowym σ2 (patrz rys. 33.3, zaadaptowany z www.wikipedia.com).

Rysunek 33.3. Rozkład normalny Pole pod wykresem krzywej to 100%. W przedziale od µ – σ do µ + σ znajduje się około 68,2% pola powierzchni krzywej, co oznacza, że ok. 68,2% defektów zostanie

naprawionych w czasie µ ± σ. Analizując wykres widzimy, że jedynie 2,2% defektów ma czas naprawy większy niż µ + 2σ. Możemy uznać, że jest to zbyt długi czas i wybrać te właśnie defekty do analizy przyczynowej. Wykresy X–Y. Wykresy X–Y, zwane też wykresami punktowymi lub rozproszonymi obrazują zależność między dwoma czynnikami. Służą najczęściej do wizualnej reprezentacji korelacji, ale można je również wykorzystać do identyfikacji i wyboru defektów do analizy przyczynowej. Rozważmy przykład organizacji wykorzystującej reużywalne komponenty. Na wykresie 33.4 przedstawiono związek częstości defektów dla dwóch projektów: X oraz jego nowszej wersji Y, w którym wykorzystano ponownie część komponentów starego systemu X.

Rysunek 33.4. Wykres X–Y dla identyfikacji defektów do analizy przyczynowej

Każdy punkt na wykresie odpowiada jednemu modułowi. Współrzędna x opisuje gęstość defektu modułu w systemie X, a współrzędna y – gęstość defektów tego samego modułu, użytego powtórnie i testowanego w systemie Y. Testy dla systemu X wykryły większość błędów, dlatego skala gęstości defektów dla systemu Y jest mniejsza. Kreskowane linie – pozioma i pionowa – oznaczają medianę wartości gęstości defektów dla obu tych systemów i dzielą wszystkie reużywalne moduły na cztery części, oznaczone na rysunku 33.4 literami A, B, C i D. Taki podział pozwala na zastosowanie różnych strategii analiz i udoskonaleń dla poszczególnych grup komponentów: grupa A to moduły o niewielkiej gęstości błędów w starym projekcie i znacznie wyższej w nowym projekcie. Defekty te mogą mieć związek z modyfikacjami wprowadzanymi do nowego systemu Y; grupa B to moduły chronicznych problemów; ich pozostawanie w obszarze wysokiej gęstości w obu wersjach systemu sugeruje ich wybór do analizy przyczynowej w pierwszej kolejności; grupa C to moduły, w których po ponownym użyciu w nowej wersji gęstość defektów jest relatywnie niższa niż w systemie X; można je badać pod kątem wykorzystania zastosowanych w nich rozwiązań w bardziej problematycznych modułach; grupa D to moduły o niskiej gęstości defektów w obu wersjach systemu; rozwiązania w nich stosowane mogą być wzorem do naśladowania, a badania nad strukturą projektową tych modułów mogą prowadzić do otrzymania wzorca projektowego. Kombinacje defektów. Podczas wyboru defektów do analizy przyczynowej można wykorzystywać więcej niż jeden parametr kategoryzacji. Na przykład, można wybierać defekty na podstawie wysokiej dotkliwości, ale znajdujące się tylko w 20% najczęściej spotykanych typów defektów. Spotkania retrospektywne projektu. Wybór i ocena defektów do dalszej analizy przyczynowej może być przeprowadzona jako część spotkania retrospektywnego. Jeśli sam wybór defektów został dokonany wcześniej, to na spotkaniu retrospektywnym można dokonać weryfikacji wybranych defektów pod kątem kosztów i zysków z przeprowadzenia analizy przyczyny źródłowej dla tych konkretnych problemów. Jeśli nie dokonano wcześniej wyboru, można to

zrobić na spotkaniu retrospektywnym, posługując się np. listą kontrolną dla kategoryzacji defektów (np. taką jak ta z tab. 33.1).

33.2.3. Zebranie i zorganizowanie informacji Po wybraniu defektów/problemów do analizy przyczynowej, przechodzimy do drugiego kroku: zebrania i zorganizowania informacji dotyczących wybranych elementów. Na rysunku 33.5 przedstawiono przykładowe metody, które można wykorzystać do zbierania oraz organizacji danych.

Rysunek 33.5. Techniki zbierania i organizacji informacji Większość tych metod omówiliśmy dokładnie w rozdziale 29. Ważne jest, aby niezależnie od wybranej techniki spotkanie było prowadzone przez doświadczonego moderatora. Jego rolą jest dobranie odpowiednich interesariuszy (zarówno specjalistów, jak i osób mających ogólny pogląd na dany projekt) tak, aby spotkanie było jak najbardziej efektywne. Ponadto, musi on czuwać nad przebiegiem spotkań. Faza zbierania i organizowania informacji nie służy jeszcze do samej identyfikacji przyczyny źródłowej, a uczestnicy sesji mają naturalną tendencję do równoległego zbierania informacji oraz analizy. Moderator musi pilnować, aby na tym etapie dyskusja nie schodziła na kwestie analityczne. Takie analizy są zwykle przedwczesne i nie pozwalają na całościowe spojrzenie na

problem. Zebrana informacja może być niekompletna. Dokonanie przedwczesnej analizy stwarza naturalną tendencję szybkiego przejścia do wniosków. Wnioski te mogą być błędne, jeśli wyciągnięte zostały na podstawie analizy opartej na niepełnych, nie do końca przeanalizowanych informacjach. Na przykład, zebranie i organizacja informacji za pomocą burzy mózgów i diagramu Ishikawy może mieć postać taką, jak na rysunku 29.11. W kolejnym kroku (patrz p. 33.2.4) diagram ten będzie rozszerzany w celu dokonania analizy i będzie miał postać podobną do tej z rysunku 33.6.

33.2.4. Identyfikacja przyczyny źródłowej przez analizę zebranych informacji Rozważmy analizę informacji przy użyciu diagramu rybiej ości (diagramu Ishikawy). Sylabus [327] proponuje następujące kroki tego procesu: 1. Wypisać skutek z prawej strony diagramu. Nie może on być zbyt ogólny (np. zła jakość produktu). Powinien dotyczyć konkretnego problemu (np. zbyt długi czas na naprawienie defektu). 2. Dodać „ości” do diagramu i poetykietować je zgodnie z przeznaczeniem. 3. Przypomnieć grupie zasady prowadzenia burzy mózgów (brak krytycyzmu, kreatywność, pozyskiwanie dużej liczby pomysłów, zapisywanie wszystkich pomysłów, nie ocenianie pomysłów na etapie samej burzy mózgów). 4. Wykorzystać metodę burzy mózgów w celu dodawania kolejnych przyczyn (w postaci nowych ości) dla istniejących na diagramie skutków. Dla każdej głównej przyczyny jest wskazane wykorzystanie list kontrolnych dla identyfikacji przyczyn źródłowych, które mogą znajdować się w innej części diagramu. 5. Pozwolić „dojrzeć” ideom i pomysłom, zostawiając analizę na pewien czas. 6. Przeanalizować diagram w celu odnalezienia klastrów przyczyn i symptomów. Wykorzystać analizę Pareto, identyfikując przyczyny do rozwiązania. Przykład. Przykładowa analiza dla skutku „wysoka gęstość defektów w kodzie” jest pokazana na rysunku 33.6. Efektem przeprowadzenia kroków 1–5 jest „goły” diagram Ishikawy, zawierający jedynie podpisane ości (czarne strzałki). W jego tworzeniu mogą pomóc nam listy kontrolne. Krok 6 to pogłębiona

analiza problemu: do diagramu dodajemy różnego rodzaju informacje, mające pomóc nam w znalezieniu przyczyny źródłowej problemu. Ciemnoszare prostokąty oznaczają, że przyczyny w nich umieszczone mają wysokie prawdopodobieństwo wystąpienia. Jasnoszare prostokąty oznaczają znikome prawdopodobieństwo. Jak widać, duże prawdopodobieństwo koncentruje się w obrębie przyczyn związanych z metodami i ludźmi, dlatego skupiamy się na tej części diagramu (zaznaczonej prostokątem o przerywanych bokach). Na tym etapie możemy uszczegółowić ten obszar diagramu Ishikawy, jeśli to konieczne. Ponadto, opisujemy zależności, jakie mogą wystąpić między przyczynami leżącymi na odległych od siebie ościach. Na przykład źle prowadzone inspekcje (kategoria: metody) są powodem braku ekspertów od testów i metod statycznych (kategoria: ludzie). Zależności te są oznaczone na diagramie szarymi strzałkami. Z tak przeprowadzonej analizy wyłania się nam ciąg głównych przyczyn prowadzących do wysokiej gęstości defektów w kodzie: jest ona powodowana źle prowadzonymi, nieefektywnymi inspekcjami. To z kolei jest spowodowane brakiem ekspertów od testów, na co wpływ ma nieefektywny proces pozyskiwania kandydatów przez dział HR. Przyczyną tego faktu jest brak współpracy zespołu testowego z pracownikami HR. Wykorzystując analizę Pareto i uwzględniając prawdopodobieństwa poszczególnych przyczyn, wybieramy ten łańcuch jako główny ciąg przyczynowo-skutkowy prowadzący do przyczyny źródłowej: braku współpracy z działem personalnym.

Rysunek 33.6. Rozszerzenie diagramu Ishikawy w celu przeprowadzenia analizy

33.2.5. Wyciągnięcie wniosków Po odnalezieniu przyczyny źródłowej przechodzimy do fazy wyciągania wniosków z przeprowadzonej analizy. Przede wszystkim, należy zastanowić się, czy analiza ta pozwala rzeczywiście na wyciągnięcie sensownych wniosków. Może się zdarzyć, że będzie potrzebnych więcej szczegółowych informacji albo że problem jest zbyt skomplikowany, aby mógł być rozwiązany w ramach zespołu testowego. Jeśli grupa jest przekonana, że przyczyna źródłowa może być określona z dużą pewnością, to grupa musi rozważyć następujące pytania [219]: czy przyczyna jest jednorazowym zdarzeniem? czy wykryliśmy jakieś nieprawidłowości w procesie, które mogą prowadzić do podobnych problemów w przyszłości? czy należy zasugerować zmiany w innych procesach? czy możemy zidentyfikować generyczne przyczyny?

Odnieśmy się do naszego przykładu z poprzedniego rozdziału. Z analizy wynika, że działania naprawcze powinny przede wszystkim dotyczyć współpracy z działem HR, która obecnie w zasadzie nie występuje. Przyczyna ta nie jest jednorazowym zdarzeniem, gdyż organizacja zatrudnia wiele osób i za każdym razem współpracy z HR nie było. Ważne jest, aby rozumieć, że jednorazowa przyczyna nie zawsze jest zdarzeniem nieistotnym – może być ona powodem innych poważnych problemów, jeśli nie wprowadzimy działań naprawczych. Jeśli współpraca z HR będzie układać się dobrze, to problem nie powinien wystąpić w przyszłości. Zatrudniając doświadczonych testerów, trzeba będzie przeprowadzić zmiany w procesie inspekcji, ponieważ do tej pory są one prowadzone nieefektywnie. Generyczną przyczyną naszego problemu może być np. zła atmosfera w firmie i brak zaufania między poszczególnymi pracownikami. Jeśli rzeczywiście tak jest, to problem jest poważniejszy (być może systemowy), i należy zaangażować w jego rozwiązanie kierownictwo wyższego szczebla. Po wyciągnięciu wniosków z analizy przyczyny źródłowej należy sprawdzić, czy inne, podobne problemy pojawiały się w przeszłości. Możemy uaktualnić listy kontrolne przyczyn, jeśli problem jest zupełnie nowy. Warto również klasyfikować zidentyfikowane przyczyny (defekty) na podstawie klasyfikacji defektów, takich jak IEEE 1044 czy Orthogonal Defect Classification. Zebrane w ten sposób dane pozwalają identyfikować obszary wymagające doskonalenia oraz monitorować postęp procesu doskonalenia. Aby jednak działania te były efektywne, proces zbierania i klasyfikacji tego typu danych musi być wdrożony w całej organizacji. Jeśli nie istnieje, to jego wdrożenie może być traktowane jako osobny proces doskonalenia.

33.3. Podejście GQM (Goal–Question–Metric) 33.3.1. Opis metody GQM, czyli Goal–Question–Metric (pol. Cel–Pytanie–Metryka) to znana i często stosowana technika pomiaru oprogramowania opracowana przez prof. Basiliego [328]. Informacje zebrane za pomocą GQM mogą być pomocne w doskonaleniu procesu testowego. Pomiar to zebranie i analiza danych o procesie bądź produkcie w celu zidentyfikowania empirycznych charakterystyk badanego

obiektu lub relacji między jego elementami. Ten rozdział został napisany na podstawie [329]. Czytelnik zainteresowany metodą GQM może również sięgnąć do wielu innych podręczników na ten temat, np. [330]. GQM, Cel–Pytanie–Metryka (ang. Goal–Question–Metric) – podejście do mierzenia oprogramowania przy użyciu trzypoziomowego modelu: poziom konceptualny (cel), poziom operacyjny (pytanie) oraz poziom ilościowy (metryka)

33.3.2. Fazy GQM Metoda GQM składa się z 4 faz: 1.

Faza

planowania,

podczas

której

jest

wybierany,

definiowany,

charakteryzowany i planowany projekt do pomiaru. 2. Faza definicji, podczas której jest definiowany i dokumentowany program pomiarów (cel, pytania, metryki oraz hipotezy). 3. Faza zbierania danych, podczas której następuje fizyczne zbieranie niezbędnych informacji. 4. Faza interpretacji, podczas której zebrane dane są przetwarzane według zdefiniowanych metryk w wyniki pomiarów, które dostarczają odpowiedzi na postawione pytania, w wyniku czego można ocenić stopień osiągnięcia celu. Schematycznie model GQM jest przedstawiony na rysunku 33.7. Cztery szare prostokąty symbolizują cztery kolejne etapy procesu.

Rysunek 33.7. GQM – schemat modelu Faza planowania służy spełnieniu wszystkich podstawowych wymagań, będących warunkiem koniecznym sukcesu metody. W szczególności obejmuje: organizację odpowiednich szkoleń, pozyskanie zaangażowania kierownictwa oraz planowanie projektu. Faza definicji polega na opracowaniu wszystkich koniecznych elementów dostawy (ang. deliverables), głównie na podstawie ustrukturalizowanych wywiadów bądź innych technik pozyskiwania wiedzy. Definiowane są: cel, pytania pomocne w osiągnięciu tego celu, odpowiadające im metryki oraz oczekiwania (hipotezy) wobec pomiarów. Faza zbierania danych to przeprowadzenie tych pomiarów. Dane są zbierane i składowane w bazie danych pomiarów. Gdy zbieranie danych się zakończy, można przystąpić do głównej fazy: interpretacji. W fazie tej pomiary są wykorzystywane do odpowiedzi na pytania, a te z kolei służą ocenie spełnienia postawionych celów.

33.3.3. Dwa paradygmaty metody GQM Metoda GQM jest oparta na dwóch paradygmatach. Dotyczą one koncepcji pomiaru (paradygmat GQM) oraz koncepcji udoskonalania (paradygmat QIP, ang. Quality Improvement Paradigm). Paradygmat GQM. Główna zasada GQM mówi, że pomiary powinny być zorientowane na cele. Aby więc udoskonalić proces, należy określić cele, uszczegółowić je przez zdefiniowanie pytań z nimi związanych oraz zdefiniować metryki, których pomiary dadzą odpowiedzi na postawione pytania. Pomiary definiują cele operacyjnie (tzn. określają dokładną procedurę oceny ich

osiągnięcia). Definicja metryk odbywa się zatem w podejściu top-down, natomiast interpretacja wyników i ocena stopnia osiągnięcia celów – w podejściu bottom-up. Cała struktura celów, pytań i metryk powinna być budowana na podstawie wiedzy eksperckiej odpowiednich interesariuszy: deweloperów, testerów, kierowników itd.

Rysunek 33.8. Paradygmat GQM – koncepcja pomiaru Paradygmat QIP. QIP [331] jest podejściem jakościowym kładącym nacisk na ciągłe udoskonalanie procesu przez wykorzystanie doświadczenia z poprzednich projektów. Doświadczenie to płynie z eksperymentowania i stosowania pomiarów. QIP jest oparty na generycznym modelu Deminga PDCA opisanym w punkcie 31.4.1. Jest schematycznie przedstawiony na rysunku 33.9.

Rysunek 33.9. Paradygmat QIP – koncepcja udoskonalania Zakłada 6 następujących kroków: 1. Scharakteryzuj – poznanie środowiska na podstawie dostępnych modeli, danych, intuicji itd. Ustalenie poziomu odniesienia (ang. baseline)

charakterystyk istniejących procesów. 2. Ustal cele – na podstawie charakterystyk, poziomów odniesienia oraz innych czynników strategicznych dla organizacji ustal ilościowe cele dla wydajności i udoskonalenia procesu bądź organizacji. 3. Wybierz proces – na podstawie charakterystyk oraz ustalonych celów wybierz odpowiednie procesy, które będą podlegać usprawnianiu, a także metody i narzędzia pomocne w przeprowadzeniu czynności doskonalących. 4. Wykonaj – dokonaj ulepszeń i zbierz informację zwrotną na podstawie zebranych danych dotyczących osiągania celu. 5. Analizuj – pod koniec danego projektu analizuj zebrane informacje, aby ocenić stosowane praktyki, rozpoznać ewentualne problemy, zarchiwizować poczynione odkrycia oraz zdefiniować zalecenia dla przyszłych projektów. 6. Wyciągnij wnioski – zbierz nabytą w tym i poprzednich projektach wiedzę i zachowaj ją jako źródło doświadczenia, które będzie wykorzystane w przyszłych projektach.

33.3.4. Wzorzec definiowania celu Cel definiuje zakres programu zdefiniowany cel musi być bezpośrednio odzwierciedlać osobiste. Aby opisywać cele

pomiarów w określonym przedziale czasu. Dobrze osiągalny i mierzalny. Cele pomiarowe mogą cele biznesowe, ale również cele projektowe lub w ujednolicony sposób, Basili i Rombach [332]

opracowali specjalny wzorzec definiowania celu (patrz rys. 33.10).

Analizuj: obiekty: proces y, produkty lub inny model doś wiadczenia w celu: zrozumienia, kontroli, udos konalenia, przewidywania z uwzg lędnieniem: kos ztów, poprawek, us uwania defektów, zmian, niezawodnoś ci z punktu widzenia: użytkownika, klienta, kierownika, prog ramis ty, tes tera, org anizacji w kontekś cie: zes połu dewelopers kieg o, zes połu tes toweg o, firmy, g rupy zadaniowej Rysunek 33.10. GQM – wzorzec definiowania celu

33.3.5. Siedem pytań Czasami kierownik czy osoba doskonaląca proces bardzo dobrze wie, jak w danej sytuacji należy zdefiniować cel, jednak często nie jest to rzeczą oczywistą. Można wtedy zastosować burzę mózgów oraz wybór celów przy użyciu technik wielokryterialnego wyboru, ale można też zdefiniować i wybrać cele pomiarowe na spotkaniu. W procesie tym pomocne może być wykorzystanie siedmiu pytań [219]: 1. Jakie są cele strategiczne organizacji? 2. Jakie czynniki mają wpływ na cele strategiczne? 3. Jak można poprawić efektywność? 4. Jakie są najważniejsze, główne obawy i problemy? 5. Jakie są cele udoskonalania? 6. Jak można osiągnąć cele udoskonalania? 7. Jakie są możliwe cele pomiarowe oraz ich priorytety?

33.3.6. Przykład Opiszemy teraz prosty przykład wykorzystania metody GQM w doskonaleniu procesu testowego. Cel będzie dotyczyć efektywności usuwania defektów w fazie testów systemowych3 (w skrócie DRE – ang. Defect Removal Efficiency). Cel: analiza procesu testów systemowych w celu oceny ze względu na efektywność usuwania defektów z punktu widzenia organizacji. Pytania: pytanie 1: jaka jest DRE fazy testów systemowych w projektach benchmarkowych? pytanie 2: jaka jest DRE fazy testów systemowych w bieżącym projekcie?

Projekty benchmarkowe (porównawcze) zostały dobrane tak, aby odpowiadały mniej więcej parametrom bieżącego projektu. Dla pytania 1 zdefiniowane metryki to: – liczba defektów znaleziona podczas testów systemowych w projektach benchmarkowych 1, …, n; – liczba wszystkich defektów znalezionych w fazie testów systemowych i późniejszych w projektach benchmarkowych 1, …, n. Dla pytania 2 zdefiniowane metryki to: PS – liczba defektów znalezionych w fazie testów systemowych bieżącego projektu PT – liczba defektów znalezionych w fazie testów systemowych i późniejszych w bieżącym projekcie. Na podstawie tych surowych metryk definiujemy metryki pochodne: dla i = 1, …, n – DRE w fazie testów systemowych dla projektów benchmarkowych 1, …, n; – średnia DRE w fazie testów systemowych dla projektów benchmarkowych; EP = PS /PT – DRE w fazie testów systemowych dla projektu bieżącego;

QP = EP /ES – iloraz DRE w fazie testów systemowych dla bieżącego projektu oraz projektów benchmarkowych.

Interpretacja. Jeśli QP > 1, oznacza to, że DRE w fazie testów systemowych bieżącego projektu jest lepsza niż średnia dla projektów benchmarkowych. W takim przypadku należy przeanalizować bieżący proces testów systemowych w celu identyfikacji dobrych praktyk oraz być może wzorców organizacyjnych. Jeśli QP = 1 lub QP ≈ 1 (równość EP i ES można zweryfikować za pomocą testów statystycznych, np. testu t-Studenta) oznacza to, że metoda jest porównywalna z historycznymi. Jeśli jej koszt jest niższy, to należy wdrożyć metody stosowane w tym procesie w całej organizacji. Jeśli wyższy, to należy wykorzystywać techniki z projektów historycznych. Jeśli QP < 1, oznacza to, że bieżący projekt ma gorsze DRE dla testów systemowych niż projekty historyczne. Należy udoskonalić

fazę testów systemowych, biorąc np. za wzór praktyki stosowane w testach systemowych projektów historycznych.

33.4. Miary, metryki i wskaźniki Pomiar metryk jest istotny z punktu widzenia doskonalenia procesu testowego z następujących powodów: pomiary pozwalają określić bieżący poziom procesu; pomiary pozwalają pokazać zyski (lub ich brak) z doskonalenia procesu; pomiary pozwalają porównać nasz proces/organizację z innymi; dane historyczne pozwalają zbudować modele predykcyjne. Istnieje bardzo wiele metryk dotyczących zarówno procesu rozwoju, jak i testowania oprogramowania. Dokładne ich omówienie, wraz z podstawami teorii pomiarów, Czytelnik znajdzie w części siódmej.

1 Wartość p w tym konkretnym przypadku to prawdopodobieństwo, że zmienna losowa o rozkładzie t-Studenta przyjmie wartość większą, niż różnica średnich badanych populacji. Jeśli p jest mniejsze niż z góry założona liczba (najczęściej 0,05), hipoteza zerowa jest odrzucana jako zbyt mało prawdopodobna. 2 Więcej szczegółów o rozkładach prawdopodobieństwa Czytelnik znajdzie w Dodatku C. 3 Efektywność usuwania defektów w fazie to stosunek defektów wykrytych w tej fazie do sumy tych defektów oraz defektów znalezionych w fazach późniejszych. Więcej informacji na temat tego typu metryk Czytelnik znajdzie w części siódmej.

34. Wybór metody usprawniania

Wybór konkretnego podejścia do usprawniania procesu testowego zależy od bardzo wielu czynników. Dalej są podane wskazówki mogące pomóc w podjęciu decyzji. Modele referencyjne procesu (np. TPI Next, TMMi) są zwykle najlepszym wyborem, gdy: proces testowy już istnieje (chociaż metody te mogą również pomagać w ustanowieniu procesu testowego); jest wymagane porównanie między podobnymi procesami; jest wymagana zgodność z wytwarzania oprogramowania; polityka organizacji dojrzałości;

zakłada

modelami osiągnięcie

usprawniania

procesu

określonego

stopnia

jest pożądany dobrze zdefiniowany punkt startowy oraz dobrze określona kolejność wykonywania działań; pomiar dojrzałości procesu testowego jest ważny np. z punktu marketingowego; modele referencyjne procesu są w organizacji znane, stosowane i cieszą się powodzeniem. Modele referencyjne zawartości (np. STEP, CTP) są zwykle najlepszym wyborem, gdy: istnieje potrzeba ustanowienia procesu testowego; jest wymagane dokonanie szacowania kosztów i ryzyk związanych z obecnie wykorzystywanym procesem testowym;

udoskonalenia nie muszą być implementowane w określonej kolejności, lecz zgodnie z potrzebami biznesowymi; jest wymagane dopasowanie procesu testowego do aktualnych potrzeb organizacji; pożądane lub konieczne są nieciągłe, gwałtowne zmiany w istniejącym procesie. Podejście analityczne zwykle jest najlepszym wyborem, gdy: chcemy skupić się na konkretnych problemach w procesie; są dostępne metryki i miary lub jest możliwość ustanowienia w organizacji programu pomiarów; jest wymagane uzasadnienie potrzeby procesu testowego; jest potrzebna zgoda co do powodów zmian; przyczyna źródłowa problemu nie leży w obrębie kontroli lub wpływu właściciela procesu testowego; jest wymagana ocena rozwiązania w małej skali lub rozwiązanie pilotażowe; jest wymagane wdrożenie pilotażowe, aby sprawdzić, czy potrzeba zastosować usprawnienia na większą skalę; chcemy testować hipotezy i zbierać dowody związane z przyczynami, symptomami i skutkami problemów oraz proponowanych rozwiązań; organizacja ceni sobie bardziej wewnętrzne analizy oparte na lokalnych pomiarach niż na zewnętrznych modelach; interesariusze z wielu obszarów są zaangażowani osobiście w analizę (np. w sesje burzy mózgów); zespół ma kontrolę nad analizami. Można wykorzystywać również modele hybrydowe (mieszane), np. podejście analityczne w ramach usprawniania opartego na modelu, np.: wykorzystanie analizy przyczynowej podczas programu udoskonalania testów TMMi; wykorzystanie metryk podczas programu udoskonalania testów STEP. Przede wszystkim jednak, należy pamiętać, że modele są jedynie wskazówkami, które mogą nam pomóc w doborze odpowiednich narzędzi. Nie

zwalnia nas to z myślenia. Nie można stać się „niewolnikiem modelu” i ślepo wykonywać zawarte w nim zalecenia. Takie podejście nigdy nie zakończy się sukcesem. Jeśli okaże się, że np. jeden z obszarów procesowych wymaganych do uzyskania kolejnego poziomu dojrzałości jest bardzo trudny do usprawnienia, to można wykorzystać metody zaczerpnięte z innych modeli lub swoje własne pomysły, aby przezwyciężyć tę trudność. W końcu naszym celem jest doskonalenie naszego procesu, a nie to, aby postępować idealnie według jakiegoś modelu.

35. Proces udoskonalania

35.1. Wprowadzenie Modele opisane w rozdziale 32 dostarczają zbiór dobrych praktyk, a niektóre z nich dodatkowo definiują różne stopnie dojrzałości procesu testowego. Nie podają jednak szczegółowo sposobu wdrożenia programu doskonalenia. Aby wypełnić tę lukę, Software Engineering Institute opublikowało obszerny raport [303]

opisujący schemat takiego wdrożenia zgodnie z modelem IDEAL omówionym w punkcie 31.4.2. Przypomnijmy, że model IDEAL składa się z pięciu faz: 1. Inicjalizacja (ang. Initiating). 2. Diagnozowanie (ang. Diagnosing). 3. Ustanowienie (ang. Establishing). 4. Działanie (ang. Acting). 5. Uczenie się (ang. Learning, Leveraging). W kolejnych podrozdziałach, odpowiadającym poszczególnym fazom, zaadaptujemy metodę IDEAL do doskonalenia procesu testowego. Jeśli organizacja chce wdrażać program doskonalenia procesu testowego, powinna dysponować wcześniej zdefiniowaną polityką testów [305]. Dokument

ten określa podejście organizacji do testowania, a więc wdrożenie powinno przebiegać zgodnie z zasadami w nim zawartymi. Ponadto, polityka testów zwykle odnosi się do innych wysokopoziomowych dokumentów obowiązujących na poziomie całej organizacji, takich jak np. misja czy strategia firmy. Postępowanie zgodnie z polityką testów gwarantuje nam zatem, że cele, jakie

osiągniemy podczas doskonalenia procesu testowego, będą zbieżne z celami strategicznymi organizacji. Plany dotyczące usprawniania testowania muszą być zgodne z polityką testów.

35.2. Rozpoczęcie procesu doskonalenia Faza

rozpoczęcia

jest jednym

z najważniejszych etapów całego

procesu

doskonalenia. Zła inicjalizacja może bowiem skutkować wieloma problemami: oporem ludzi, konfliktami, opóźnieniami, niską efektywnością przeprowadzanych zmian. Osoba odpowiedzialna za doskonalenie testów musi rozważyć następujące czynniki ludzkie, które mają zasadniczy wpływ na powodzenie całego projektu: poziom wiedzy – ludzie niemający wystarczającej wiedzy na temat testowania, zarządzania czy przeprowadzania zmian, będą nastawieni negatywnie i oporni wobec tych zmian; kultura organizacyjna (ang. organizational culture) – brak odpowiedniej kultury organizacyjnej może wręcz uniemożliwić wdrożenie zmian; np. jeśli firma nie ma dobrze zdefiniowanych procesów, próba wdrożenia zaawansowanego, dojrzałego procesu testowego skończy się niepowodzeniem; kultura pracowników (ang. people culture) – brak otwartości, wysoki poziom nieufności, zwyczaj obwiniania innych za popełnione błędy, nieumiejętność dyskutowania, ciągłe konflikty w zespołach – to czynniki, które bardzo utrudnią doskonalenie procesu testowego; aby go doskonalić, musi on najpierw w ogóle działać, a to jest niemożliwe, jeśli ludzie nie potrafią ze sobą współpracować; otwartość na zmiany i doświadczenie w tym zakresie – dobrze, jeśli w organizacji przeprowadzano wcześniej skutecznie jakieś zmiany, np. udoskonalanie procesu wytwarzania oprogramowania; jeśli poprzednie zmiany kończyły się porażką, ludzie będą negatywnie nastawieni do kolejnych takich projektów. Czynniki ludzkie są bardzo istotne. Jeśli okaże się, że istnieją poważne problemy w tym obszarze, to należy rozważyć zawieszenie procesu doskonalenia do momentu usunięcia tych problemów (np. przez organizację szkoleń,

warsztatów umiejętności miękkich i pracy w grupie). Aby projekt doskonalenia testowania się powiódł, musi być przeprowadzany w dojrzałym, otwartym na zmiany środowisku. Faza Rozpoczęcia składa się z następujących zadań: określenie powodu, dla którego chcemy doskonalić proces testowy; ustanowienie celów dla doskonalenia testowania; określenie kontekstu; pozyskanie wsparcia; stworzenie infrastruktury dla procesu udoskonalania.

35.2.1. Określenie powodu doskonalenia (stymulacja do zmiany) Dlaczego chcemy doskonalić proces testowy? Zanim zaczniemy wdrażać ulepszenia, musimy mieć świadomość, po co to wszystko robimy. Może istnieć ku temu wiele powodów, np.: niska efektywność bieżącego procesu (np.: chroniczne opóźnienia, mała efektywność usuwania defektów, niezadowalający poziom efektywności testerów, zbyt wysokie koszty testowania, niskie ROI procesu testowego); wystąpienie nieoczekiwanych problemów (np. poważne awarie w produkcji); zmieniające się okoliczności (np.: zmiany organizacyjne, realizowanie zupełnie odmiennych typów projektów, zmiana w metodologii wytwarzania oprogramowania, konieczność outsourcingu części zadań testowych); powody formalne lub marketingowe (np. wymaganie klienta bądź kierownictwa dotyczące osiągnięcia odpowiedniego poziomu dojrzałości procesu testowego); konkurencyjność (np. gdy badania lub benchmarking wykażą, że poziom naszego procesu jest zbyt niski sam w sobie lub w porównaniu z innymi organizacjami). Powody rozpoczęcia całego procesu powinny być określone przez interesariuszy (wywiady, ankiety, spotkania) oraz jako wnioski z pomiarów. Proponowane zmiany muszą współgrać ze strategicznymi celami organizacji.

Czasami powody do zmian są widoczne gołym okiem (np. wysoka liczba defektów polowych lub znajdowanych w późniejszych fazach procesu), a czasami mogą być bardziej subtelne i pochodzić od kierownictwa, użytkowników, klientów, programistów, testerów, działu utrzymania, działu pomocy technicznej, działu marketingu i sprzedaży itp. Dobrze jest nie ograniczać się w przeprowadzaniu wywiadów i ankiet do kierownictwa i działu testowego, ale zebrać możliwie jak najwięcej informacji od wszystkich interesariuszy w całej organizacji. W ten sposób uzyskamy szerokie spojrzenie na cały proces testowy i lepiej dostrzeżemy jego słabości.

35.2.2. Ustanowienie celów dla doskonalenia testowania Główne cele doskonalenia procesu testowego zawsze powinny odnosić się do jakości, kosztu, czasu i wartości biznesowej. Na ich ustanowienie składają się trzy zasadnicze kroki: 1. Określenie ogólnej wizji przyszłości. Jak według klienta oraz zespołu powinien wyglądać „idealny” proces testowy? Interesariusze muszą być przekonani, że nowy proces będzie cechował się lepszym zwrotem z inwestycji (ROI) niż proces obecny. 2. Określenie celów specyficznych. Umożliwiają one podjęcie odpowiednich akcji oraz możliwość pomiaru postępu w usprawnianiu. Cele mogą być ilościowe (metryki, podejście GQM), jakościowe (mierzalne np. przez ankiety zadowolenia) lub wyrażone w terminach poziomu dojrzałości procesu (wtedy jest zwykle wymagane wykorzystanie procesu opartego na modelu). 3. Dopasowanie procesu usprawniania do ogranizacji. Udoskonalenia procesu testowego muszą być zgodne z celami biznesowymi organizacji, z procesem doskonalenia produkcji oprogramowania, strukturą organizacyjną itp. W celu ułatwienia powiązania celów doskonalenia procesu testowego z celami doskonalenia określonymi na poziomie całej organizacji można wykorzystać znane z teorii zarządzania narzędzia, takie jak Korporacyjna tablica rozdzielcza czy zrównoważona karta wyników. Korporacyjna tablica rozdzielcza to czytelna, łatwa w interpretacji graficzna reprezentacja bieżącego stanu i trendów historycznych dla KPI, tzn. kluczowych

wskaźników efektywności (ang. Key Performance Indicators, KPI) oraz związków między nimi. Umożliwiają kierownictwu kontrolę oraz badanie wpływu różnych procesów na stan organizacji i podejmowanie decyzji. Przykładowa Korporacyjna Tablica Rozdzielcza, zaczerpnięta ze strony www.bluemarlinsys.com/bi/demo/dash.php jest pokazana na rysunku 35.1.

Rysunek 35.1. Korporacyjna tablica rozdzielcza – przykład Stosowanie Korporacyjnej tablicy rozdzielczej ma wiele zalet [219]: umożliwia graficzną wizualizację kluczowych danych i wskaźników; pozwala na szybką identyfikację negatywnych trendów; pozwala na podejmowanie bardziej świadomych, opartych na faktach i danych decyzji; ułatwia dopasowanie strategii do celów organizacji; oszczędza czas na studiowanie wielu różnych raportów;

umożliwia spojrzenie na stan organizacji „z lotu ptaka”. Korporacyjna tablica rozdzielcza (ang. corporate dashboard) – reprezentacja za pomocą tablicy rozdzielczej statusu organizacji i kondycji przedsiębiorstwa; patrz też: Zrównoważona karta wyników Korporacyjna tablica rozdzielcza jest szczególnym przypadkiem tablicy rozdzielczej, zwanej też niekiedy kokpitem menedżerskim. Tablica rozdzielcza jest tworzona na podstawie czterech podstawowych zasad: ergonomiczna prezentacja – sposób prezentacji danych jest oparty na naukach kognitywnych i ma zapewniać – przez wykorzystanie odpowiedniej oprawy graficznej poznawczej1 (ang. cognitive bias);



uniknięcie

tendencyjności

wykorzystanie operacyjnych KPI; podejście strukturalne; automatyzacja – dane dla tablicy rozdzielczej powinny być zbierane, przetwarzane i opracowywane automatycznie, z jak najmniejszym udziałem człowieka w tym procesie. tablica rozdzielcza (ang. dashboard) – przedstawienie dynamicznych miar wydajności operacyjnej organizacji lub pewnego działania, przy użyciu metaforycznych metryk, takich jak wizualne „zegary”, „liczniki” i inne wskaźniki podobne do tych na desce rozdzielczej samochodu tak, aby skutki zdarzeń i działań były zrozumiałe i mogły być w prosty sposób powiązane z celami operacyjnymi organizacji; patrz rozdzielcza, karta wyników

także: Korporacyjna

tablica

Zrównoważona karta wyników. Znana również pod nazwą Strategicznej karty wyników. Jest to metoda [333] zarządzania strategicznego, a więc ustanowienia oraz pomiaru długookresowych celów organizacji. Została stworzona jako odpowiedź na tradycyjne systemy zarządzania, które sukces organizacji mierzyły wyłącznie w aspekcie finansowym. Zrównoważona karta wyników zakłada, że sukces organizacji zależy od czterech zasadniczych czynników:

wyniki finansowe – jak powinniśmy być udziałowców, aby osiągnąć sukces finansowy?;

postrzegani

przez

klient – jak powinniśmy być postrzegani przez klienta, aby zrealizować wizję?; procesy wewnętrzne – jakie procesy zadowolić udziałowców i klientów?;

musimy

doskonalić, aby

infrastruktura, nauka i rozwój – jak możemy utrzymać naszą zdolność do zmiany i udoskonalania, aby zrealizować wizję? Zrównoważona karta wyników przeszła ewolucję od prostej techniki przedstawiającej zagregowane wyniki organizacji do narzędzia zarządzania strategicznego. Model Zrównoważonej karty wyników jest pokazany na rysunku 35.2. Zrównoważona karta wyników (ang. balanced scorecard) – instrument strategicznego zarządzania działaniem firmy, który umożliwia mierzenie, czy operacyjna działalność organizacji jest zgodna z jej celami określonymi w biznesowej wizji i strategii; patrz także: Korporacyjna tablica rozdzielcza

Rysunek 35.2. Zrównoważona karta wyników – przykład Zrównoważona karta wyników bierze pod uwagę cztery wspomniane wymiary działania organizacji, wskazując, co organizacja powinna mierzyć, aby je „zrównoważyć”, w szczególności, aby zbilansować perspektywę finansową. Jeśli Zrównoważona karta wyników jest wdrożona w organizacji, to, aby doskonalić proces testowy, możemy posiłkować się nią w celu doboru odpowiednich metryk, zwłaszcza, jeśli będziemy stosować podejście analityczne do usprawniania. Zrównoważona karta wyników jest szczególnym przypadkiem karty wyników. karta wyników (ang. scorecard) – reprezentacja zagregowanego pomiaru wydajności, przedstawiająca postępy w dążeniu do długofalowych celów; karta wyników dostarcza statycznych pomiarów wydajności z całości lub na koniec zdefiniowanego okresu

35.2.3. Określenie kontekstu Organizacja jest systemem naczyń połączonych, dlatego doskonalenie procesu testowego nie odbywa się w próżni. Przed rozpoczęciem tego projektu należy dobrze określić kontekst, w jakim będziemy działać, gdyż wpływa on zasadniczo na możliwości oraz kierunek naszych działań. Obszary, które należy rozważyć, obejmują: Zakres innych procesów niż proces testowy – np. zarządzanie projektem, wymaganiami czy konfiguracją. Ważne jest, by uświadomić sobie, czy założone cele możemy osiągnąć przez doskonalenie procesu testowego, czy też wymaga to usprawnienia innych procesów, np. związanych z usługami IT [321]. Zakres procesu testowego – czy możemy tak zorganizować projekt doskonalenia, aby skupiać swoje działania w danym czasie tylko na pewnych obszarach procesu testowego? Jest to podejście o wiele korzystniejsze pod względem kosztów niż rozważanie procesu testowego jako całości. Należy jednak pamiętać o zależnościach między poszczególnymi podprocesami. Ich nieuwzględnienie i skupienie się wyłącznie na jednym, izolowanym fragmencie procesu, może prowadzić do rozwiązań suboptymalnych.

Zakres projektu – program doskonalenia testów może być zorganizowany na podstawie programu/projektu lub na podstawie organizacji testowej. W tym pierwszym przypadku udoskonalenia są skoncentrowane na poszczególnych projektach lub programach (czyli grupach projektów). Mogą przynieść szybkie zyski, ale niekoniecznie na poziomie całej organizacji. Program oparty na organizacji skupiony jest na zespole/departamencie testowym. Poza doskonaleniem poszczególnych projektów skupia się także na całej organizacji.

35.2.4. Pozyskanie wsparcia Uzyskanie wsparcia odpowiednich menedżerów i kierownictwa wyższego szczebla jest kluczowe dla powodzenia każdego projektu, a zwłaszcza projektu dotyczącego doskonalenia procesów. Należy pozyskać wsparcie wszystkich osób związanych w jakikolwiek sposób z usprawnianym procesem. Jest ono szczególnie potrzebne w kontekście ryzyka związanego ze zmianami, jakie będą następowały podczas doskonalenia testów, zwłaszcza na początku projektu. Wsparcie powinno objawiać się m.in. przez: przydzielanie odpowiednich zasobów; dostarczanie zespołowi doskonalącemu niezbędnych dla jego prac informacji; uczestniczenie w kluczowych spotkaniach; promowanie projektu w organizacji; bronienie projektu oraz zespołu projektowego przed kierownictwem wyższego szczebla.

35.2.5. Stworzenie infrastruktury dla procesu udoskonalania Ostatnią czynnością fazy Rozpoczęcia jest określenie sposobu, w jaki proces udoskonalania będzie przeprowadzony. Oznacza to utworzenie dla niego odpowiedniej infrastruktury. Każdy element infrastruktury musi mieć dokładnie określony zakres obowiązków i odpowiedzialności. W kontekście udoskonalania procesu testowego najczęściej spotykanym elementem infrastruktury jest tzw. Grupa procesu testowego, złożona ze specjalistów, których zadaniem jest fizyczne przeprowadzenie ulepszeń. W dużych organizacjach można spotkać ciała zwane

Radą wykonawczą bądź Radą projektu. Są to ciała, w skład których wchodzą sponsorzy oraz menedżerowie, a także zewnętrzni konsultanci. Rada formalnie odpowiada za proces usprawniania, zatwierdza plany, kamienie milowe i rezultaty końcowe. Jest uprawniona do przydzielania zasobów i eskalowania problemów (zwykle na poziom wyższego kierownictwa, np. zarządu). GPT, Grupa procesu testowego, grupa zajmująca się procesem testowym (ang. Test Process Group, TPG) – zespół specjalistów (od testów), którzy ułatwiają definiowanie, utrzymanie i doskonalenie procesu testowego używanego w organizacji [197]

35.3. Diagnozowanie aktualnej sytuacji Faza Diagnozowania służy odpowiedzeniu sobie na pytanie: w jakim miejscu się aktualnie znajdujemy? Jaki jest nasz punkt wyjścia dla projektu doskonalenia? Faza ta składa się z dwóch zasadniczych działań: scharakteryzowanie obecnego i pożądanego stanu procesu oraz bieżących praktyk; projekt rekomendacji. Produktem tej fazy jest raport oceny procesu testowego w organizacji.

35.3.1. Scharakteryzowanie obecnego oraz pożądanego stanu procesu Czynności związane z charakteryzowaniem obecnego stanu procesu są istotnie zależne od tego, czy proces będzie oparty na modelu, czy na technikach analitycznych. W pierwszym przypadku jest przeprowadzana ocena procesu (ang. process assessment) opisana w samym modelu, w której pomiary są porównywane z określonym standardem, np. konkretnym poziomem dojrzałości procesu. Ocena taka jest zwykle przeprowadzana przez certyfikowanych audytorów i jest oparta na listach kontrolnych oraz wywiadach. W podejściu analitycznym są wykorzystywane różnego rodzaju metryki, miary i wskaźniki. Rekomendowane minimum, jeśli chodzi o wyniki tej fazy, to: ocena bieżącego poziomu dojrzałości procesu;

opis bieżących procesów (tzw. mapa procesów); aktualne wartości metryk służących do oceny procesu. mapa procesów2 (ang. process mapping) – dokument opisujący strukturę procesów w organizacji lub jej części oraz zależności między nimi; może zawierać klasyfikację procesów (np. na zarządcze, główne i pomocnicze) oraz szczegółowo definiować poszczególne procesy; definicja taka powinna obejmować: początek i koniec procesu, jego strukturę, wejścia i wyjścia, opis dostawców i klientów, właściciela procesu, kryteria oceny, wpływ działania procesu na wynik całej organizacji, mierniki, narzędzia pomiaru i oceny, kryteria i metody sterowania, w tym sprzężenie zwrotne, zasoby i odstępstwa oraz dokumentacje procesu i jego przebiegu [334] Ocena bieżącego poziomu dojrzałości procesu oraz aktualne wartości metryk posłużą do oceny, czy projekt doskonalenia idzie w dobrym kierunku. Opis bieżących procesów stanowi podstawę do projektowania zmian i ulepszeń w istniejących procesach. Ocena istniejących procesów składa się z następujących kroków: planowanie i harmonogramowanie przygotowanie (analiza wstępna, przygotowanie materiałów do wywiadów, np. list kontrolnych, zebranie odpowiednich testaliów, np. planów i specyfikacji testów); zaplanowanie w procesie

wywiadów testowym:

z osobami testerami,

pełniącymi różne role kierownikami testów,

deweloperami, kierownikami projektów, właścicielami procesów, analitykami biznesowymi, osobami odpowiedzialnymi za zarządzanie zmianą, konfiguracją, wydaniami, incydentami itp.; zaplanowanie pokrycia odpowiednich obszarów procesów podczas poszczególnych wywiadów; przygotowanie oceny (analiza dokumentów w celu zrozumienia obecnych procesów testowych, na podstawie której zostaną przygotowane pytania do wywiadów oraz zostaną przeprowadzone formalne etapy oceny niewymagające dyskusji, np. weryfikacja kompletności dokumentów lub zgodności ze standardami);

przeprowadzenie wywiadów (wymaga stworzenia bezpiecznego środowiska, w którym osoba udzielająca informacji będzie czuła się swobodnie;

wywiady

nie

powinny

odbywać

się

w

obecności

przełożonego osoby, z którą jest przeprowadzany wywiad); wstępna informacja zwrotna po przeprowadzeniu wywiadów (np. w formie prezentacji lub spotkania; ma na celu upewnienie się, czy wszystkie poruszone kwestie zostały właściwie zrozumiane, a niejasności wyjaśnione); analiza wyników (w przypadku podejść analitycznych można wykorzystać techniki takie, jak myślenie systemowe [335] lub teorię punktów przełomowych (ang. Tipping Point Theory) Malcolma Gladwella [336]; w podejściach opartych na modelu dokonuje się porównania między dojrzałością obecnego procesu oraz procesu docelowego, zdefiniowanego w fazie Rozpoczęcia); przeprowadzenie analizy rozwiązania (dostarcza możliwe rozwiązania zidentyfikowanych problemów oraz pozwala na wybór między różnymi rozwiązaniami; w procesie tym priorytetyzuje się problemy i przyczyny źródłowe, przeprowadza analizę zysków i kosztów potencjalnych rozwiązań, identyfikuje ograniczenia, analizuje sprzężenia zwrotne, zarówno dodatnie, jak i ujemne; podczas przeprowadzania analizy rozwiązania często stajemy przed problemem wyboru jednego z wielu potencjalnych rozwiązań, przy czym ocena każdego z nich jest dokonywana na podstawie wielu kryteriów – w takiej sytuacji w podrozdziale 29.4.

można

posłużyć

się

technikami

opisanymi

35.3.2. Rekomendacje akcji naprawczych Rekomendacje są zalecanymi sposobami postępowania podczas udoskonalania procesu testowego. Mogą dotyczyć następujących kwestii: w jakiej kolejności zajmować się poszczególnymi procesami testowymi? które części procesów testowych mają podlegać udoskonaleniu i w jaki sposób?

Każda rekomendacja powinna być zdefiniowana jako wymaganie doskonalenia procesu (np. „dostarczyć wsparcie narzędziowe dla systemu śledzenia defektów”) i powinna zawierać następujące informacje [296]: unikalny identyfikator (w celu umożliwienia śledzenia powiązań); wpływ rekomendacji na jeden lub więcej celów (jeśli można, to należy użyć skali wskazującej stopień wpływu, np.: minimalny, częściowy, pełny, lub skali procentowej); szacunkowe koszty i zyski; szacunkowy czas długookresowe itp.);

implementacji

(działania

krótkookresowe,

ryzyka implementacji (np. ryzyko dużego oporu przed wprowadzeniem zmiany); zależności i założenia (np. założenie, że dokonano wcześniej innego ulepszenia, niezbędnego dla wdrożenia danej rekomendacji).

35.4. Ustanowienie planu doskonalenia procesu testowego Faza ustanowienia planu doskonalenia procesu testowego składa się z trzech kroków: ustanowienie priorytetów dla wdrożenia planu doskonalenia; opracowanie podejścia do wdrożenia; zaplanowanie działań związanych z wdrożeniem. Wynikiem tej fazy jest zwykle plan doskonalenia testów.

35.4.1. Ustanowienie priorytetów dla wdrażania planu doskonalenia Rekomendacje z fazy Diagnozowania są priorytetyzowane według określonych kryteriów, które powinny obejmować co najmniej: czas trwania usprawnienia – należy znaleźć „złoty środek” między udoskonaleniami krótko- i długookresowymi. Te krótkookresowe dają tzw. szybkie korzyści (ang. quick win) i mogą być silnym czynnikiem motywującym do dalszej pracy. Usprawnienia długookresowe dotyczą zwykle fundamentalnych kwestii związanych z procesem testowym i są

trudniejsze do przeprowadzenia. Łatwiej tu o zniechęcenie udziałowców czy inne ryzyka związane zwykle z przeprowadzaniem długotrwałych, skomplikowanych projektów; ryzyko wdrożenia – niektóre usprawnienia wymagają całkowitej zmiany w podejściu do określonych praktyk stosowanych w organizacji. Podczas analizy wpływu tych ryzyk powinno się rozważyć następujące kwestie: zdolność do bezproblemowego powrotu do sytuacji sprzed usprawnienia, jeśli zostanie ono porzucone lub zakończy się porażką, ogólny wpływ na cały program udoskonalania (np. czy przeprowadzenie innych usprawnień zależy od poprawnego wdrożenia bieżącego usprawnienia?), realna możliwość implementacji udoskonalenia wystarczające zasoby, odpowiednich ludzi,

(czy mamy motywację,

infrastrukturę? czy ludzie będą mogli poświęcić odpowiednią ilość czasu na zadania związane z usprawnieniem?), koszty i zyski z proponowanego usprawnienia (najlepiej wyrażone w terminach ROI, czyli zwrotu z inwestycji); powiązanie z celami – czy istnieje wyraźny związek między implementacją wdrożenia a osiągnięciem konkretnego, uprzednio zdefiniowanego celu? wpływ – jaki wpływ będzie miało dane usprawnienie na konkretne cele?

35.4.2. Opracowanie podejścia do wdrożenia Na podstawie spriorytetyzowanych rekomendacji podejmuje się decyzję o wyborze podejścia do udoskonalania procesu testowego. Pod uwagę należy wziąć zarówno czynniki techniczne, jak i nietechniczne. Dwa zasadnicze podejścia to: podejście „od ogółu do szczegółu” (ang. top-down); podejście „od szczegółu do ogółu” (ang. bottom-up). Porównanie różnych aspektów obu tych podejść jest przedstawione w tabeli 35.1.

Tabela 35.1. Porównanie podejść do wdrożenia doskonalenia procesu testowego

Aspekt

Podejście top-down

Podejście bottom-up

Zakres

Globalny – zwykle obejmuje wiele projektów lub całą org anizację

Lokalny – zwykle s kupia s ię na wybranym jednym lub dwóch projektach bądź ich wybranych obs zarach.

Właś ciciel

Zwykle s pecjalnie wyznaczony zes pół

Zwykle zes pół projektowy

Zwykle bardziej formalne, Formalizm oparte na planie udos konalania tes tów

Podejś cie

Wymag ana s zczeg ółowa analiza w celu identyfikacji dobrych i złych praktyk oraz porównania ich w różnych projektach. Krytyczne czynniki s ukces u to: efektywne zarządzanie ludźmi, uzys kanie ws parcia i zarządzanie oczekiwaniami (ang . expectations)

Zwykle mniej formalne Zwykle podejś cie analityczne, z elementami prototypowania w celu nabycia doś wiadczenia i zbudowania ws parcia. S tos owane częś ciej, g dy budżet na udos konalanie jes t og raniczony

35.4.3. Zaplanowanie działań związanych z wdrożeniem Gdy mamy wybrane podejście do usprawniania, możemy zaplanować działania w ramach tego procesu. Na podstawie wszystkich zebranych dotąd informacji jest tworzony plan doskonalenia testów. plan doskonalenia testów (ang. test improvement plan) – plan osiągnięcia celów doskonalenia procesu testowego oparty na zrozumieniu aktualnych mocnych

i słabych stron procesów testowych w organizacji i aktywów procesu testowego [197] Plan doskonalenia testów powinien zawierać wszystkie typowe dla planów cechy, a więc: zadania do wykonania, ich harmonogram, określenie kamieni milowych, punktów decyzyjnych, zasobów określenie odpowiedzialności, opis niezbędnych pomiarów, jakie należy przeprowadzić, mechanizmy śledzenia i weryfikacji, analizę ryzyka i strategię wdrożenia. Do najważniejszych zadań czynności planowania wdrożenia należą: osiągnięcie zgody co do wyboru miar wywiedzionych na podstawie rekomendacji; zdefiniowanie wymaganych wskaźników wydajności; priorytetyzacja oraz łączenie w grupy związanych ze sobą czynności doskonalących; powiązanie zadań usprawniania z rekomendacjami oraz wymaganymi wskaźnikami efektywności dla późniejszego pomiaru stopnia ich wdrożenia; formalne potwierdzenie wyboru podejścia do usprawniania; harmonogramowanie zmian; ustanowienie grup lub zespołów wdrażających usprawnienia; przydział zadań i odpowiedzialności; udokumentowanie testowego.

powyższych

w

planie

doskonalenia

procesu

Model IDEAL opisuje dwa możliwe plany działań: strategiczny (długofalowy, rozpisany zwykle na 3 do 5 lat i obejmujący całą organizację) oraz taktyczny (krótkofalowy, rozpisany zwykle na rok lub krócej i skupiający się na szczegółowym planowaniu działań Grupy Procesu Testowego). Jest to jednak podział formalny i nie należy się nim zbytnio przejmować.

35.5. Działanie w celu wdrożenia udoskonaleń Faza Działania składa się z czterech zasadniczych czynności: stworzenie rozwiązania;

rozwiązania pilotażowe/testowe; doprecyzowanie rozwiązania; zaimplementowanie rozwiązania. Jest to faza fizycznego wykonania planu, tzn. faza, w której (w końcu!) uprzednio zaplanowane czynności są realizowane. Jest ona najbardziej pracochłonna spośród wszystkich faz modelu IDEAL. W literaturze przedmiotu (np. [337]) można znaleźć opinie mówiące, iż planowanie zajmuje około 30% wysiłku, a implementacja rozwiązania aż 70%.

35.5.1. Stworzenie rozwiązania Faza Działania rozpoczyna się opracowaniem rozwiązania dla istniejących problemów. Rozwiązania te powinny pomóc w osiągnięciu docelowego, pożądanego stanu procesu. Rozwiązanie może zawierać procesy, wzorce, narzędzia, wiedzę, szkolenia, informację oraz wsparcie. Duże, skomplikowane rozwiązania są zwykle opracowywane przez zespoły doskonalące i według niektórych autorów jest to podejście bardzo efektywne [338]. Bath i van Veenendaal zalecają, aby już we wczesnych fazach projektu doskonalenia precyzyjnie zdefiniować strukturę dokumentacji i zapewnić jej spójność [219]. Brak spójności może być spowodowany np. przez fakt tworzenia różnych części dokumentacji przez różne zespoły, które nie komunikują się ze sobą. Brak spójności utrudnia i opóźnia przeprowadzenie projektu, ponieważ dokumentacja musi zostać poprawiona.

35.5.2. Rozwiązania pilotażowe/testowe Zgodnie z radą Toma Gilba, „jeśli nie wiesz, co robisz, nie rób tego na dużą skalę”, stworzone przez nas rozwiązanie dla doskonalenia procesu powinniśmy najpierw przetestować w jednym lub kilku projektach pilotażowych. Czasami efektywność naszego podejścia może być bowiem zweryfikowana wyłącznie na drodze eksperymentalnej. Projekty pilotażowe są dobrą metodą na znaczną redukcję ryzyka (np. w przypadku niepowodzenia projektu w skali całej organizacji). Wybór projektu pilotażowego powinien uwzględniać następujące czynniki: realizm – czy pilot jest reprezentatywny dla „rzeczywistego świata”? Nie należy ulegać pokusie stosowania pilota dającego szybkie korzyści

lub łatwą implementację, ale kompletnie nierealistycznego; skalowalność rozwiązania – czy wyniki z projektu pilotażowego mogą być użyte we wszystkich koniecznych kontekstach? Jeśli pilot nie jest reprezentatywny dla złożoności lub wielkości rzeczywistych projektów, to istnieje ryzyko, że będzie nieskalowalny, a więc nieefektywny; wpływ na bieżący projekt – pilot nie powinien być przeprowadzany na bieżącym, aktualnym projekcie, chyba że jego wpływ na ten projekt jest przez nas akceptowalny. Należy uważać z pilotami, które zastępują istniejące procesy czy praktyki. Jeśli to możliwe (zwłaszcza w kontekście dostępności zasobów ludzkich i budżetu), lepiej uruchomić projekt pilotażowy równolegle z istniejącymi procesami; ryzyko porażki – chociaż stosowanie pilota ma redukować ryzyko, projekt pilotażowy sam w sobie jest obarczony ryzykiem niepowodzenia, które musi być właściwie ocenione.

35.5.3. Doprecyzowanie rozwiązania Dzięki przeprowadzeniu projektu pilotażowego, zebraniu danych oraz wyciągnięciu wniosków mamy możliwość doprecyzowania czy ulepszenia zaproponowanego rozwiązania. Czasem efektywne doprecyzowanie wymaga kilku iteracji projektu pilotażowego. Jeśli wnioski dotyczące przydatności pilota są pozytywne, to można rozważyć wdrożenie analogicznych udoskonaleń w innych częściach organizacji.

35.5.4. Zaimplementowanie rozwiązania Gdy jesteśmy już przekonani (np. przez przeprowadzenie projektów pilotażowych) o tym, że rozwiązanie będzie właściwe i korzystne, można je wdrożyć w całej organizacji. Istnieje kilka sposobów przeprowadzenia tego procesu: big-bang – wszystkie zmiany są wdrażane w tym samym czasie; projekt po projekcie – w każdym projekcie zmiana jest implementowana w określonym momencie czasu; w trakcie wykonania (ang. just in time) – zmiana jest implementowana, gdy proces jest wykonywany.

Wybór podejścia zależy od natury samych czynności doskonalących oraz od różnych okoliczności istniejących w organizacji. W trakcie implementacji rozwiązania należy cały czas zbierać i analizować zdefiniowane wcześniej miary i wskaźniki, aby na bieżąco oceniać rzeczywisty wpływ zmian na stan procesu.

35.6. Wyciąganie wniosków z projektu doskonalenia testów Faza wyciągania wniosków (ang. lessons learned) to ważna i – niestety – często pomijana faza we wszelkiego rodzaju projektach. W kontekście udoskonalania procesu testowego składa się z dwóch zasadniczych czynności: analiza i weryfikacja przeprowadzonych działań; propozycje przyszłych rozwiązań. Ostatni krok cyklu IDEAL ma na celu ciągłe doskonalenie procesu ulepszania tak, aby każdy kolejny projekt usprawniania był efektywniejszy od poprzedniego. W fazie tej „uczymy się na własnych błędach”, a uświadamiając je sobie, możemy uniknąć ich w przyszłości lub znacznie zminimalizować ryzyko ich powtórzenia.

35.6.1. Analiza i weryfikacja przeprowadzonych działań Analiza i weryfikacja przeprowadzonych działań najczęściej jest przeprowadzana na spotkaniu retrospektywnym, z udziałem możliwie szerokiej grupy interesariuszy projektu doskonalenia. Podczas sesji spotkania retrospektywnego uczestnicy odpowiadają na kilka pytań: jak przebiegał proces doskonalenia? co osiągnęliśmy? jakie cele zrealizowaliśmy? czy początkowe cele zostały zrealizowane? co poszło dobrze, a z czym mieliśmy problemy? z czego te problemy wynikały? co mogliśmy zrobić szybciej, lepiej, efektywniej? Odpowiedzi na te i podobne pytania są archiwizowane i stanowią bazę wiedzy dla przyszłych projektów. Są cennymi wskazówkami podpowiadającymi, jak efektywnie organizować prace i na co zwracać szczególną uwagę. Są również przydatne do tworzenia (bądź rozbudowy) list kontrolnych na użytek różnego

rodzaju inspekcji czy analiz ryzyka. Faza analizy i weryfikacji może zakończyć się opracowaniem tzw. raportu z oceny. raport z oceny (ang. assessment report) – dokument podsumowujący wyniki oceny, np. wnioski, zalecenia oraz ustalenia; patrz także: ocena procesu

35.6.2. Propozycje przyszłych rozwiązań Opierając się na wynikach spotkania retrospektywnego, uczestnicy mogą sformułować rekomendacje pozwalające na ulepszenie kolejnych procesów doskonalenia. Rekomendacje te powinny zostać przesłane do kierownictwa wyższego szczebla. Kierownictwo, po przeprowadzeniu ich analizy, może zadecydować o włączeniu ich w kolejne cykle doskonalenia procesu testowego.

1 Istnienie tendencyjności poznawczej w tym kontekście powoduje podejmowanie (często błędnych) decyzji na podstawie sugerowania się sposobem prezentacji danych, a nie faktycznymi danymi. Dokładne omówienie tego zjawiska opisujemy w rozdziale 50. 2 Niektórzy autorzy mówią o „mapie procesu” (jednego). Taki schemat opisuje jednak zadania, a nie procesy. Stanowi schemat blokowy procesu, a nie mapę wszystkich procesów.

36. Organizacja, role i umiejętności

Aby efektywnie przeprowadzić projekt doskonalenia procesu testowego, należy mieć odpowiednią infrastrukturę, a osoby odpowiedzialne za jego fizyczną realizację powinny posiadać wiele umiejętności miękkich oraz analitycznych. Omówimy teraz obie te kwestie.

36.1. Organizacja Doskonalenie procesu testowego przeprowadza się zazwyczaj przez stworzenie Grupy procesu testowego. Zespół ten może mieć różne inne nazwy, np. Grupa doskonalenia procesu (ang. Process Improvement Group, PIG)1, Grupa procesu inżynierii testów (ang. Test Engineering Process Group, TEPG) lub inne. My będziemy wykorzystywać nazwę Grupa procesu testowego (GPT).

36.1.1. Zakres działań GPT GPT powołuje się w celu wspierania rozwoju infrastruktury procesu testowego i planowania oraz implementacji prac związanych z doskonaleniem tego procesu. Grupa powinna skupiać się na czynnościach związanych z procesami (definiowanie, analiza, ocena, planowanie procesu). Zazwyczaj jest ona również odpowiedzialna za identyfikowanie komponentów zdatnych do powtórnego użycia w innych projektach doskonalących oraz udostępnianie ich całej organizacji. Dobrą praktyką jest, aby GPT była zespołem permanentnym, a nie powoływanym wyłącznie na okres pojedynczych działań doskonalących. Wymóg ten może być np. zapisany w strategii testowej. Permanentność grupy zapewnia lepszą efektywność jej działań, zarówno w krótko-, jak i długookresowej

perspektywy. Stabilna GPT może na przestrzeni dłuższego czasu wypracować spójne podejście do usprawniania. Grupie takiej łatwiej jest również kontrolować implementację programu pomiarów dla doskonalenia procesu testowego. GPT może zostać ustanowiona właścicielem procesu testowego (ang. process owner) i stanowić kanał komunikacyjny dla wszystkich interesariuszy tego procesu. Większość podejść opartych na modelu (np. TMMi) wymaga na wyższych poziomach dojrzałości ustanowienia permanentnej GPT. Członkami GPT powinny być osoby reprezentujące wiele umiejętności, wiedzy, ról oraz spojrzeń na proces testowy. Powinni to być praktycy mający w organizacji autorytet jako specjaliści w swoich dziedzinach. Dobrze dobrany skład GPT ułatwia także uzyskanie w organizacji szerokiej akceptacji dla działań doskonalących.

36.1.2. Organizacja Grupy procesu testowego Model IDEAL [303] proponuje trzypoziomową strukturę organizacyjną Grupy procesu testowego (patrz rys. 36.1).

Rysunek 36.1. Struktura Grupy procesu testowego dla dużej organizacji Rada wykonawcza jest ciałem spotykanym raczej w większych organizacjach. Odpowiada za strategię oraz wytyczanie celów i kierunków działań. Grupa sterująca składa się z kierowników wyższego szczebla. Odpowiada za ustanawianie celów, kryteriów powodzenia oraz priorytetów. Nadzoruje przeprowadzanie usprawnień w organizacji i dostarcza niezbędnych do tego celu zasobów. Grupa sterująca może powoływać doraźne Techniczne grupy robocze dla wykonania określonych zadań w ramach procesu doskonalenia. Techniczne

grupy robocze proponują Grupie sterującej rozwiązania i przeprowadzają czynności związane z prototypowaniem bądź wdrożeniami pilotażowymi. Modyfikują plany taktyczne na podstawie wniosków wyciągniętych z tych pilotów. Po zakończeniu działań, do przeprowadzenia których została powołana, Techniczna grupa robocza zostaje rozwiązana. W projekcie doskonalenia procesu testowego nie można zapomnieć o jeszcze jednej, ważnej kwestii organizacyjnej – nieustannym szkoleniu i podnoszeniu kwalifikacji poszczególnych osób zaangażowanych w projekt. Ciągłe doskonalenie procesu może być bowiem osiągnięte tylko wtedy, gdy ludzie odpowiedzialni za przeprowadzenie go sami nieustannie się rozwijają.

36.1.3. Właściwości Grupy procesu testowego Efektywna GPT jako zbiór ludzi powinna charakteryzować się następującymi cechami: Umiejętności społeczne (miękkie): doradztwo – GPT jest w stanie kompetentnie odpowiadać na pytania i doradzać w zakresie doskonalenia procesu testowego; rozwiązywanie konfliktów; umiejętność negocjowania; entuzjazm i siła przekonywania; uczciwość i otwartość; umiejętność przyjmowania konstruktywnej krytyki; cierpliwość. Umiejętności techniczne: znajomość zasad zorganizowanego testowania oraz procesu testowego; znajomość zagadnień oprogramowania;

udoskonalania

procesu

znajomość modeli doskonalenia procesu testowego; znajomość podejść analitycznych; znajomość procesów testowych; znajomość narzędzi testowych; doskonała znajomość procesów testowych w organizacji.

tworzenia

36.2. Role i umiejętności W procesie doskonalenia testowania można wyróżnić dwie główne role oraz jedną rolę pomocniczą: doskonalący proces testowy; główny oceniający; oceniający (rola pomocnicza).

36.2.1. Doskonalący proces testowy Główne zadania i oczekiwania wobec doskonalącego proces testowy to [219]: kierowanie programami doskonalenia w organizacji lub poszczególnych projektach;

procesów

testowych

podejmowanie decyzji o podejściu do doskonalenia procesu testowego; ocena procesu testowego, proponowanie kolejnych (ang. step-by-step) ulepszeń oraz wiązanie ich z celami biznesowymi; ustanowienie strategii dla doskonalenia procesu testowego oraz jej wdrożenie; analiza problemów związanych z doskonaleniem oraz proponowanie efektywnych rozwiązań; stworzenie planu doskonalenia testów zgodnego z celami biznesowymi; opracowanie koncepcji usprawniania procesu testowego na poziomie organizacyjnym zawierającej opis ról, umiejętności oraz strukturę organizacyjną; ustanowienie standardowego procesu dla implementacji ulepszeń; zarządzanie wprowadzaniem zmian do procesu testowego, włączając w to współpracę ze sponsorami projektu oraz identyfikacje kluczowych czynników sukcesu; koordynacja działań grup zaangażowanych w doskonalenie, np. Technicznych grup roboczych; rozumienie i efektywne zarządzanie ludźmi w zakresie oceny procesu i implementacji ulepszeń; pełnienie roli „punktu kontaktowego” dla wszystkich interesariuszy zaangażowanych w proces doskonalenia;

wypełnianie obowiązków głównego oceniającego lub oceniającego, jeśli to konieczne. doskonalący proces testowy (ang. test process improver) – osoba wprowadzająca udoskonalenia w procesie testowym zgodnie z planem doskonalenia testów Doskonalący proces testowy powinien charakteryzować się następującymi umiejętnościami i kwalifikacjami: umiejętność definiowania i wdrażania procesów testowych; zdolności menedżerskie; umiejętności w zakresie szkolenia, konsultingu i prezentacji; umiejętność zarządzania zmianą; umiejętności miękkie (zwłaszcza dotyczące komunikacji i prowadzenia spotkań); umiejętności oceny; znajomość modeli doskonalenia procesu testowego; (opcjonalnie) certyfikacja, np. ISTQB Expert Level – Improving the Testing Process, pożądana pełna certyfikacja ISTQB na poziomie zaawansowanym; dla TMMi istnieje osobny certyfikat, TMMi Professional (www.tmmi.org/?q=node/133). Doskonalący proces testowy powinien mieć doświadczenie jako projektant testów bądź analityk testów zdobyte w kilku projektach. Powinien również mieć kilkuletnie doświadczenie jako kierownik testów.

36.2.2. Główny oceniający Zadania i oczekiwania wobec głównego oceniającego to: planowanie oceny procesu testowego; przeprowadzanie wywiadów i analiz zgodnie z założonym podejściem oceny (np. opartym na danym modelu); tworzenie raportów z oceny; proponowanie kolejnych usprawnień oraz ich powiązań z celami biznesowymi;

prezentowanie rekomendacji;

interesariuszom

wyników

ocen,

wniosków

oraz

przeprowadzanie obowiązków oceniającego, jeśli to konieczne. główny oceniający, asesor (ang. lead assessor) – osoba kierująca procesem oceny; w niektórych przypadkach, np. w CMMI oraz TMMi, gdy jest dokonywana formalna ocena, główny oceniający musi być akredytowany i formalnie przeszkolony Główny

oceniający

powinien

charakteryzować

się

następującymi

umiejętnościami i kwalifikacjami: umiejętności miękkie, zwłaszcza związane przeprowadzaniem wywiadów i słuchaniem;

z

komunikacją,

umiejętność tworzenia raportów i prezentacji; umiejętności planowania i zarządzania (zwłaszcza w większych projektach); szeroka wiedza dotycząca wybranego podejścia do doskonalenia; (pożądane) certyfikacja ISTQB Advanced Level – Test Manager; dla formalnych ocen według TMMi – akredytacja jako główny asesor TMMi. Główny oceniający powinien mieć doświadczenie w przeprowadzaniu oceny jako oceniający według wybranych podejść. Powinien mieć doświadczenie jako tester lub analityk testów, kilkuletnie doświadczenie jako kierownik testów oraz doświadczenie w stosowaniu cykli życia oprogramowania.

36.2.3. Oceniający Oceniający jest pomocnikiem głównego oceniającego, np. w sytuacji, gdy główny oceniający musi dokonywać wielu czynności naraz (słuchanie, notowanie, zadawanie pytań, kierowanie dyskusją). Zadania i oczekiwania wobec oceniającego to: notowanie podczas wywiadów; wyrażanie swojej opinii dla głównego oceniającego (jako drugiej), gdy dane zagadnienie jest niejasne;

kontrola pokrycia określonych obszarów i tematów, np. poruszanych podczas wywiadów, oraz przypominanie o tym głównemu oceniającemu, jeśli je pominął. oceniający (ang. assessor) – osoba przeprowadzająca ocenę; członek zespołu oceniającego Oceniający

powinien mieć

umiejętności

miękkie (zwłaszcza

dotyczące

notowania) i wiedzę na temat przyjętej metody oceny. Pożądane jest posiadanie certyfikatu ISTQB Foundation Level. Oceniający powinien mieć doświadczenie jako tester lub analityk testowy w kilku projektach.

36.2.4. Umiejętności doskonalącego proces testowy Ilustrację pożądanych cech, jakie powinien mieć doskonalący proces testowy przedstawiono na rysunku 36.2. Część z nich omówiliśmy we wcześniejszych rozdziałach (np. większość umiejętności dotyczących przeprowadzania wywiadów).

Rysunek 36.2. Umiejętności doskonalącego proces testowy

Omówimy teraz krótko kilka spośród cech przedstawionych na rysunku. Zachowanie współzależne. Współzależność pojawia się w wielu sytuacjach, zwłaszcza związanych z koniecznością uzyskania kompromisu wobec jakichś działań. W kontekście doskonalenia procesu może się to zdarzyć, gdy doskonalący proces testowy proponuje, zgadza się lub priorytetyzuje ulepszenia procesu. zachowanie

współzależne

(ang.

codependence

behavior)



nadmierna

emocjonalna lub psychologiczna zależność od innej osoby, szczególnie w próbach zmiany aktualnego (niepożądanego) zachowania tej osoby przy równoczesnym wspieraniu jej w kontynuowaniu tego zachowania Przykładem zachowania współzależnego może być narzekanie na opóźnione dostawy wydań i jednocześnie wyrażanie zadowolenia z postawy „heroizmu” członków zespołu, którzy zostają w pracy po godzinach, aby nadrobić opóźnienie. Taka postawa jedynie wzmacnia opóźnienia, ponieważ członkowie zespołu mogą czuć się bardziej doceniani, gdy biorą nadgodziny i pracują pod presją. Zachowanie współzależne może wystąpić również podczas przeprowadzania wywiadu. Na przykład, doskonalący proces testowy odkrywa poważne wady w procesie, za który odpowiedzialna jest osoba, z którą przeprowadza wywiad. Jeśli doskonalący proces testowy lubi tę osobę, to może (nawet podświadomie) nie chcieć sprawiać jej kłopotów związanych z tymi wadami, mówiąc np.: „nie martw się, pominę tę kwestię w raporcie” czy „to nieistotne”. Zachowanie współzależne podczas wywiadu będzie zminimalizowane, jeśli doskonalący proces testowy jest niezależny wobec zespołu testowego lub rozmówcy. Wykorzystanie inteligencji emocjonalnej. Rozwinięta inteligencja emocjonalna jest bardzo przydatną cechą podczas przeprowadzania wywiadów. Mianem tym określa się kompetencje osobiste pozwalające rozumieć nie tylko przekazywane faktyczne, konkretne informacje, lecz także stany emocjonalne własne oraz rozmówców, a także umiejętność wykorzystywania własnych emocji oraz radzenia sobie ze stanami emocjonalnymi innych osób. inteligencja emocjonalna (ang. emotional intelligence, EI) – zdolność, umiejętność i biegłość w identyfikowaniu, ocenie i zarządzaniu emocjami własnymi, innych ludzi lub grup Mayer i Salovey [339] zauważyli, że ludzie różnią się jeśli chodzi o zdolności komunikacji oraz rozumienia emocji i opracowali model opisujący 4 typy

związanych z nimi umiejętności. Model ten jest przedstawiony w tabeli 36.1. Tabela 36.1. Typy zdolności emocjonalnych według Mayera i Saloveya

Zdolność

Opis

Pos trzeg anie Zdolnoś ć do odkrywania i interpretowania emocji emocji u s iebie i u innych. Używanie emocji

Zdolnoś ć do wykorzys tania emocji do analizowania problemów.

Rozumienie emocji

Zdolnoś ć do doceniania s komplikowanych relacji między emocjami oraz teg o, jak mog ą s ię one zmieniać w czas ie.

Zarządzanie emocjami

Zdolnoś ć do reg ulowania emocji u s iebie i u innych.

Z kolei według Reuvena Bar-On na inteligencję emocjonalną składa się 5 elementów [340]: inteligencja intrapersonalna (emocjonalna samoświadomość, asertywność, samoakceptacja, szacunek dla własnej osoby, niezależność osobista); inteligencja interpersonalna (empatia, umiejętność utrzymywania więzi, odpowiedzialność społeczna); radzenie sobie ze stresem (tolerancja na stres, kontrola impulsów); zdolność adaptacji (umiejętność rozwiązywania problemów emocjonalnych oraz konfrontacji osobistych doświadczeń ze światem zewnętrznym); ogólny nastrój (poczucie szczęścia i spełnienia, optymistyczne podejście do życia i innych osób). Doskonalący proces testowy, jeśli cechuje się wysokim poziomem inteligencji emocjonalnej, jest w stanie dostrzegać emocje rozmówcy i odkrywać jego prawdziwe intencje czy poglądy, które ten stara się ukryć podczas rozmowy (np. przez zadanie uszczegóławiającego pytania w odpowiednim momencie). Potrafi,

wykorzystując emocje, uzyskać od rozmówcy informacje, opinie lub osobisty punkt widzenia na daną sprawę, także w sposób pośredni, przez analizę jego zachowania, gestów czy tonu głosu. Umie również zarządzać emocjami, aby rozmowa była efektywna (np. przeprowadzenie rozluźniającej rozmowy na początku wywiadu, aby odstresować rozmówcę). Umiejętności prezentacji i raportowania. Umiejętności te są istotne, jeśli chcemy pozyskać zaangażowanie innych ludzi do zmiany w procesie, przekonać ich do swojej koncepcji bądź komunikować interesariuszom w czytelny i zrozumiały sposób wyniki naszych prac. Podczas prezentowania informacji należy: dostosować formę i poziom abstrakcji do grupy docelowej; być selektywnym przedstawienia;



wybierać

jedynie

najważniejsze

idee

do

mówić do słuchaczy i językiem słuchaczy; skonstruować prezentację tak, aby antycypować pytania, które mogą się pojawić.

1 Nie jest to jednak popularna nazwa – jej akronim, PIG, po angielsku oznacza świnię.

37. Czynniki sukcesu

Aby projekt doskonalenia procesu testowego się powiódł (a także, aby kolejne tego typu projekty były przeprowadzane bez większych problemów), muszą wystąpić następujące czynniki sukcesu. Właściwe zarządzanie zmianą. Doskonalenie nierozerwalnie wiąże się ze zmianami w organizacji i wymaga zmiany przyzwyczajeń oraz nawyków; właściwe zarządzanie zmianą pozwala zminimalizować opór przed nią. Dobre rozpoczęcie. Omawiając pierwszą fazę modelu IDEAL, wspomnieliśmy, że jest to, obok fazy Diagnozowania, najważniejsza faza całego procesu doskonalenia. Aby dobrze rozpocząć, muszą wystąpić następujące czynniki: ustanowienie czytelnych, mierzalnych i osiągalnych celów dla procesu usprawniania; uzyskanie wsparcia ze strony kierownictwa; zorganizowanie procesu doskonalenia jako formalny projekt; odpowiednia

ilość

dostępnego

czasu

ludzi

biorących

udział

w doskonaleniu. Dobre wykonanie swojej pracy. Faza Implementacji również ma kluczowy wpływ na powodzenie całego projektu. Oto czynniki sukcesu z nią związane: zdefiniowanie czytelnych przedziałów czasowych dla poszczególnych czynności doskonalących oraz dla cykli informacji zwrotnej; nie mogą być zbyt duże, aby można było utrzymać tempo procesu (ang. momentum); czytelne, mierzalne i osiągalne cele procesu doskonalenia dla każdego cyklu; identyfikacja i organizacja własności procesu (ang. process ownership);

kontrola i nadzór wszystkich kroków w procesie zarządzania zmianą; zaangażowanie ekspertów implementacji ulepszeń;

w

zakresie

testowania

podczas

zaangażowanie innych interesariuszy, gdy problem wykracza poza obszar testowania (np. jakość specyfikacji i projektów architektury, zarządzanie konfiguracją, zarządzanie wydaniami); zarządzanie

oporem

przed

zmianą

i

stosowanie

zabiegów

marketingowych w celu zdobycia zaangażowania i poparcia innych ludzi dla procesu doskonalenia; wykorzystywanie istniejących praktyk (nie zmieniać czegoś tylko dla samej zmiany); wykorzystanie narzędzi wspomagających; wykorzystanie zewnętrznych konsultantów, jeśli to konieczne; świadomość istnienia zewnętrznych standardów i regulacji, jeśli takie istnieją, zwłaszcza jeśli są obowiązujące; czytelne raportowanie postępów; przestrzeganie procesów wewnętrznego akceptowania poszczególnych działań przez podmioty do tego uprawnione; utrzymanie zgodności z innymi procesami usprawniającymi, jeśli są obecne w organizacji. Ustanowienie kultury dla usprawniania. uwzględniać czynniki kulturowe, takie jak:

Proces

doskonalenia

musi

kultura zarządzania (rozkazuj i nadzoruj (ang. command and control), konsultatywna, zorientowana na zespół itp.) – wpływa na akceptację proponowanych podejść do usprawniania; lokalizacja geograficzna organizacji (poziom akceptacji dla różnych modeli i podejść może się różnić w zależności od obszaru geograficznego. W Europie, USA czy Azji popularność danej metody może być zupełnie inna); cele, polityka, strategia i postawa wobec ulepszania (np. czy dane podejście jest używane gdzieś indziej i czy odniosło sukces); relacje między poszczególnymi departamentami organizacji (np. w przypadku połączenia dwóch firm może pojawić się opór przed zmianami traktowanymi jako narzucone przez „inną” organizację);

używany model cyklu życia – wpływa na częstotliwość zmian procesowych; podejście do testowania (automatyczne, ręczne, eksploracyjne, ad hoc itd.) – wpływa na stopień akceptacji poszczególnych typów sugerowanych zmian. Przykładem podejścia do ustanowienia kultury dla usprawniania jest manifest doskonalenia procesu testowego omówiony w podrozdziale 31.1.

Część VII Jakość oprogramowania

Twoi klienci potrafią świetnie opisać jakość produktu, ponieważ ją właśnie tak naprawdę kupują. Nie kupują produktu. Kupują zapewnienie, że produkt spełni ich oczekiwania John Guaspari

Ostatnia część książki jest poświęcona zagadnieniom jakości oprogramowania. Można powiedzieć, że testowanie jest podzbiorem inżynierii jakości – stanowi jedno z narzędzi zapewniania jakości oprogramowania. Rozdział 38 stanowi wprowadzenie w dyscyplinę inżynierii jakości oprogramowania. Omówimy w nim samo pojęcie jakości oraz dojrzałość procesu i istniejące standardy jakości. Aby oceniać i ulepszać jakość, musimy potrafić ją mierzyć. Rozdział 39 stanowi wprowadzenie w teorię pomiaru. Przedstawimy w nim różne typy skal pomiarowych i metryk, a także bardzo ważne pojęcia spójności i odpowiedniości pomiaru. Opiszemy ponadto podstawowe zasady analizy danych – w szczególności rozważymy istotne pojęcie

przyczynowości. Rozdział 40 to katalog narzędzi wykorzystywanych w inżynierii jakości. Omówimy w nim dokładnie dwa typy narzędzi: do statystycznej kontroli procesu oraz analizy i zapobiegania przyczyny źródłowej. Rozdziały 41, 42 i 43 stanowią przegląd podstawowych metryk stosowanych w inżynierii oprogramowania, służących pomiarowi: wielkości, charakterystyk jakościowych oraz złożoności oprogramowania. Większość z tych metryk będzie wykorzystywana w modelach opisanych w późniejszych rozdziałach. Rozdział 44 omawia pierwszy ważny typ modeli jakościowych – modele wysiłku. Służą one do przewidywania nakładu pracy, czasu i kosztu projektu, w szczególności projektu testowego. Przedstawimy wiele typów modeli wysiłków, w szczególności ważny i często wykorzystywany parametryczny model COCOMO II. Rozdział 45 dotyczy metryk i modeli dla defektów. Modele te służą do opisu przepływu defektów przez cykl życia oprogramowania. Pozwalają m.in. na przewidywanie liczby defektów rezydualnych, tzn. pozostających w kodzie po wypuszczeniu oprogramowania. Są to potężne narzędzia dla kontroli i predykcji jakości oprogramowania. Nieco więcej miejsca poświęcimy na omówienie modelu Rayleigha oraz pokażemy praktyczny przykład jego wykorzystania. Rozdziały 46 i 47 skupiają się na metrykach oraz modelach niezawodności oraz związanej z nią dostępności. Modele przyrostu niezawodności służą do modelowania

zmian średniego czasu między kolejnymi awariami lub liczby awarii na jednostkę czasu. Tak jak modele defektów mogą być wykorzystane do szacowania defektów rezydualnych. Rozdział 48 stanowi przegląd kilkunastu metryk opisujących proces testowy. Pokażemy w nim praktyczne wykorzystanie tych metryk z punktu widzenia kierownika testów (lub kierownika działu jakości). Zobaczymy, w jaki sposób, rozpoczynając nowy projekt, oszacować wiele jego parametrów, których prawdziwe wartości znane będą tak naprawdę dopiero pod koniec projektu testowego. Rozdział 49 jest poświęcony metrykom zadowolenia klienta, które mają zupełnie odmienną specyfikę od wszystkich wcześniej omówionych metryk. Rozdział 50 zawiera zbiór praktycznych porad dotyczących sposobu prezentowania danych i wyników modelowania przed innymi członkami zespołu oraz kierownictwem.

38. Czym jest jakość oprogramowania?

38.1. Testowanie oprogramowania a jakość oprogramowania W najwęższym sensie przez zapewnianie jakości oprogramowania rozumie się testowanie, którym zajmowaliśmy się w pierwszych sześciu częściach książki. Jest tak dlatego, gdyż testowanie jest najpopularniejszą techniką służącą do uzyskania zadowalającego poziomu jakości. Zapewnianie jakości jest jednak dyscypliną znacznie szerszą. Obejmuje swoim działaniem wszystkie fazy cyklu życia. Poza testowaniem należy do niej między innymi pomiar oprogramowania oraz procesów, a także budowa modeli jakości. Modele pozwalają na ocenę aktualnego poziomu jakości oraz na przewidywanie tego poziomu w przyszłości. Modele jakości mogą być także wykorzystane do przewidywania parametrów projektu, takich jak koszty, czas czy zasoby. W rozdziale 2 podaliśmy definicję jakości jako stopień spełnienia wymagań i oczekiwań klienta, jednak jest to tylko jedno z możliwych ujęć jakości. W podrozdziale 31.3 przedstawiliśmy 5 spojrzeń na jakość, opartych na: produkcie, użytkowniku, wytwarzaniu, wartości oraz tzw. ujęciu transcendentnym jakości. W tej części książki na jakość będziemy patrzeć z pierwszych trzech perspektyw: jakość oparta na produkcie – dotyczyć będzie cech produktu; jej pomiar i predykcja odbywają się za pomocą miar charakteryzujących oprogramowanie (np. jego wielkość fizyczna, wyrażona w punktach funkcyjnych) oraz wykryte defekty; jakość oparta na wytwarzaniu – dotyczyć będzie procesu wytwórczego; jej pomiar i predykcja odbywają się za pomocą tzw. wewnątrzprocesowych metryk jakości dotyczących niezawodności czy efektywności faz rozwoju w wykrywaniu defektów;

jakość oparta na użytkowniku – dotyczyć będzie poziomu zadowolenia użytkownika z dostarczonego mu produktu; spośród trzech wymienionych rodzajów ta jakość jest najtrudniejsza do ilościowego pomiaru; zwykle odbywa się on za pomocą ankiet, wywiadów i kwestionariuszy. jakość oprogramowania (ang. software quality) – ogół funkcjonalności i cech oprogramowania, które charakteryzują zdolność zaspokajania stwierdzonych lub przewidywanych potrzeb [3]

w

Aby zapewnić wysoką jakość organizacji proces zarządzania

oprogramowania, należy ustanowić jakością. W jego wdrażaniu oraz

przeprowadzeniu pomagają normy jakościowe, takie jak rodzina norm ISO 9000 [1]. zarządzanie jakością (ang. quality management) – ogół skoordynowanych czynności mających na celu kierowanie organizacją i kontrolowanie jej pod kątem jakości; zwykle obejmuje czynności takie, jak: zdefiniowanie polityki jakościowej i celów jakościowych, planowanie jakości, kontrolowanie jakości, zapewnienie jakości i poprawę jakości [1]

38.2. Model Kano Związek między zadowoleniem użytkownika a jakością produktu opisuje tzw. model Kano, autorstwa Noriaki Kano [341]. Model ten wyróżnia 5 kategorii preferencji użytkownika wobec produktu. Są to: Jakość konieczna (ang. must-be quality) – dotyczy atrybutów koniecznych do użytkowania produktu, ale niewpływających na zwiększenie satysfakcji użytkownika. Przykładem może być możliwość uruchomienia oprogramowania w określonym systemie operacyjnym: jeśli programu nie da się uruchomić, to użytkownik nie będzie go używał, ale z samego faktu możliwości uruchomienia go satysfakcja użytkownika się nie zwiększy.

Jakość jednowymiarowa (ang. one-dimensional quality) – dotyczy atrybutów, których spełnienie daje użytkownikowi satysfakcję, a niespełnienie – rozczarowanie (ang. dissatisfaction). Przykładem może być szybkość działania programu. Jeśli użytkownik ma do wyboru dwa analogiczne produkty, to będzie bardziej zadowolony z użytkowania tego, który działa wydajniej. Jeśli użytkownik będzie niezadowolony.

jego

wydajność

spadnie,

to

Jakość atrakcyjności (ang. attractive quality) – dotyczy atrybutów, które dają użytkownikowi satysfakcję, jeśli są całkowicie spełnione, ale ich niespełnienie nie powoduje rozczarowania. Atrybuty te decydują o „pozytywnym zaskoczeniu” klienta. Na przykład, jeśli klient spodziewa się, że zakupiony produkt będzie współpracował tylko z plikami csv (i jest to wszystko, co klient potrzebuje), a okazuje się, że potrafi pracować również z plikami xls, dbf, xml, a także konwertować automatycznie dane do baz danych różnego typu, to klient otrzyma „ekstra satysfakcję” z tego powodu. Jakość obojętna (ang. indifferent quality) – dotyczy atrybutów, które nie są ani dobre, ani złe i nie powodują ani wzrostu, ani spadku satysfakcji klienta. Przykładem może być forma elementów graficznych GUI (np. pola prostokątne albo w zaokrąglonych prostokątach).

Rysunek 38.1. Model Kano Jakość odwrotna (ang. reverse quality) – dotyczy „pozytywnych” atrybutów, których spełnienie może powodować brak satysfakcji klienta, ze względu na to, że różni użytkownicy lubią różne rzeczy. Na przykład, program z bogatą liczbą zaawansowanych opcji będzie powodował brak satysfakcji klienta potrzebującego jedynie prostej funkcjonalności. atrakcyjność (ang. attractiveness) – zdolność oprogramowania do bycia atrakcyjnym dla użytkownika [3] Poziom satysfakcji klienta zależy od stopnia spełnienia określonych atrybutów produktu. Jednak oczekiwania klienta zmieniają się w czasie: to, co dzisiaj świadczy o „ekstra jakości” (jakość atrakcyjności), jutro może stać się cechą absolutnie wymaganą w ramach jakości podstawowej (jakość konieczna). Schematycznie zależność ta jest pokazana na rysunku 38.1. Lewy górny obszar rysunku to obszar satysfakcji. Prawy dolny obszar to region podstawowych, koniecznych potrzeb. Szare strzałki reprezentują fakt, iż innowacyjne rozwiązania dające dodatkową satysfakcję z biegiem czasu stają się standardowymi, a następnie podstawowymi, koniecznymi wymaganiami.

38.3. Dojrzałość procesu i standardy jakości Powodzenie projektu software’owego w dużej mierze zależy od poziomu dojrzałości organizacji oraz jej procesów. Omawiając kwestie związane z tymi działaniami, wspomnieliśmy w rozdziale 32 o kilku modelach pozwalających na przeprowadzenie formalnej oceny dojrzałości procesu: ISO 9000, CMMI (dla procesu wytwarzania oprogramowania) czy TMMi. Opiszemy teraz jeszcze dwie inne, ważne metody oceny dojrzałości: ocenę SPR dla procesów związanych z rozwojem oprogramowania oraz ocenę Malcolma Baldridge’a (dotyczącą szeroko pojętej jakości zarządzania).

38.3.1. SPR

SPR (ang. Software Productivity Research Inc.), czyli Instytut Badań Produktywności Oprogramowania opracował metodę oceny procesu rozwoju oprogramowania, który – w odróżnieniu od powstałego mniej więcej w tym samym czasie modelu CMMI – poza kwestiami dotyczącymi architektury oprogramowania oraz procesu produkcji bierze pod uwagę również elementy generalnej strategii organizacji wpływające na jakość, produktywność i zadowolenie użytkownika. Kwestionariusz do oceny SPR składa się z ok. 400 pytań, przy czym część z nich jest pytaniami wielokrotnego wyboru z zastosowaniem pięciostopniowej skali Likerta, w odróżnieniu od kwestionariusza CMMI, w którym odpowiedź na każde pytanie może brzmieć wyłącznie „tak” lub „nie”. Końcowa ocena SPR procesu jest wyrażona również w skali Likerta o następujących stopniach: 1) proces doskonały; 2) proces dobry; 3) proces średni; 4) proces poniżej średniej; 5) proces słaby. W odniesieniu do metryk i modeli oprogramowania SPR obejmuje następujące elementy [200]: jakość i produktywność; doświadczenie programistów w usuwaniu defektów przed testami i podczas testów; zamierzone cele jakości i niezawodności oprogramowania; usuwanie defektów na poziomie projektu przed testami. Stosując ocenę SPR, można sprawdzić, jak nasze projekty wypadają na tle innych organizacji. Według badań Capersa Jonesa [342] przeprowadzonych na około 600 projektach w 350 firmach i 50 organizacjach rządowych USA, w 2000 roku rozkład ocen SPR kształtował się następująco: 3% projektów otrzymało ocenę doskonałą; 18% projektów otrzymało ocenę dobrą;

54% projektów otrzymało ocenę średnią; 22% projektów otrzymało ocenę poniżej średniej; 3% projektów otrzymało ocenę słabą.

38.3.2. Ocena Malcolma Baldridge’a Narodowa Nagroda Jakości im. Malcolma Baldridge’a (MBNQA) to najbardziej prestiżowe amerykańskie wyróżnienie w dziedzinie przeprowadzana jest przez jury i obejmuje 7 kategorii:

jakości.

Ocena

przywództwo; informacja i analiza; strategiczne planowanie jakości; korzystanie z zasobów ludzkich; zapewnianie jakości produktów i usług; uzyskana jakość; zadowolenie klienta. Wiele organizacji korzysta z metodologii oceny Malcolma Baldridge’a jako swoistego standardu doskonalenia jakości.

38.4. Co mierzyć, jak mierzyć i po co mierzyć? Oprogramowanie oraz proces jego produkcji ma mnóstwo potencjalnych charakterystyk, które można mierzyć. Nie sposób mierzyć ich wszystkich, dlatego zanim rozpoczniemy nasz program pomiarów, musimy odpowiedzieć sobie na następujące pytania: jakie są cele strategiczne i biznesowe wobec tworzonego produktu? co chcemy osiągnąć na końcu procesu wytwarzania oprogramowania? kto będzie wykorzystywał zebrane przez nas pomiary? wykorzystanie jakich metryk najlepiej pozwoli nam zweryfikować osiągnięcie (bądź nie) założonych celów? Typowe podejścia do tego zagadnienia to GQM omówione w podrozdziale 33.3 lub wykorzystanie standardowych, powszechnie stosowanych metryk. Za minimalny zbiór możemy uznać metryki pokrywające następujące obszary:

rozmiar systemu/produktu; czas trwania projektu; wysiłek; defekty; produktywność. Pomiar oprogramowania oraz procesu jego tworzenia to niezwykle cenne narzędzie, umożliwiające m.in.: zrozumienie bieżącego procesu; zwiększenie kontroli nad procesem; zwiększenie efektywności procesu; dokonywanie dokładnych szacunków dotyczących kosztów, czasu i wysiłku; obiektywną weryfikację i porównanie skuteczności stosowanych technik, metod i narzędzi; zmniejszenie kosztów produkcji oraz skrócenie cyklu wytwórczego przez zwiększenie produktywności i wydajności; zwiększenie satysfakcji klienta przez dostarczenie produktu o wysokiej jakości; budowę modeli przewidujących przyszłą jakość produktu (w tym jakość końcową) czy szacunkową liczbę defektów; budowę modeli pozwalających na badanie efektywności poszczególnych faz cyklu życia w usuwaniu defektów.

39. Podstawy teorii pomiarów

39.1. Metryka, miara, wskaźnik, pomiar Lord Kelvin wypowiedział kiedyś dwie bardzo trafne opinie o pomiarach: „mierzyć znaczy wiedzieć” oraz „jeśli nie możesz czegoś zmierzyć, nie możesz tego usprawnić”. Przeprowadzanie pomiarów jest kluczowe dla zdobycia wiedzy o procesie testowym, weryfikowania stawianych hipotez o procesie oraz w celu jego ulepszenia. Przykładowa hipoteza może brzmieć następująco: „stosowanie inspekcji formalnych w fazie projektowania przyczynia się do redukcji błędów polowych”. Hipotezę tę możemy zweryfikować tylko wtedy, gdy określimy niezbędne metryki, wykonamy pomiary oraz przeprowadzimy odpowiednie testy statystyczne. Przedstawimy teraz podstawowe pojęcia stosowane w teorii pomiarów. Ich hierarchia jest przedstawiona na rysunku 39.1. Punktem wyjścia do przeprowadzania pomiarów jest teoria. Teoria składa się z jednej lub kilku tez opisujących zależności między pojęciami, którymi teoria ta operuje. Pojęcia są formalizowane jako definicje. Z każdej tezy można wyprowadzić jedną lub kilka hipotez empirycznych, tzn. stwierdzeń, których prawdziwość chcemy potwierdzić w sposób naukowy. Do tego celu potrzebne nam będą definicje operacyjne, tzn. takie, które mówią, w jaki sposób można mierzyć określone elementy teorii. Definicja operacyjna skutkuje utworzeniem metryki, czyli skali pomiaru oraz sposobu jej stosowania. Hipotezę empiryczną weryfikuje się przez zebranie i analizę danych. Od strony praktycznej oznacza to przeprowadzenie pomiarów, czyli obliczenie miar – liczbowych wartości metryk. Analiza danych jest zwykle związana z przeprowadzeniem określonych badań statystycznych, które potwierdzają bądź odrzucają postawioną hipotezę, a więc decydują o prawdziwości (lub nie) danej teorii. Jeśli teoria jest słuszna, to można

ją wykorzystać do stworzenia modelu pozwalającego na szacowanie i predykcję elementów wchodzących w skład teorii. Od strony praktycznej oznacza to wykorzystanie wskaźników.

Rysunek 39.1. Hierarchia abstrakcji pojęć w teorii pomiaru pomiar (ang. measurement) – proces przypisania wartości liczbowej lub kategorii do obiektu mający na celu opisanie określonej właściwości obiektu [343] wskaźnik (ang. indicator) – w teorii pomiarów miara wykorzystywana do oszacowania lub przewidywania innej miary [23] Przykład. Rozważmy wspomnianą wcześniej teorię mówiącą, że wdrożenie przeglądów formalnych wpływa korzystnie na poziom jakości końcowego produktu. Teoria ta może zawierać następującą tezę: „wdrożenie przeglądów w fazie projektu architektury zmniejsza końcową liczbę defektów”. Aby zweryfikować tę tezę, potrzebujemy wprowadzić definicje przeglądu formalnego, projektu architektury oraz defektu. Hipoteza empiryczna może mieć następującą postać: „w fazie projektu architektury zawierającej przeglądy formalne wszystkich kluczowych elementów tej architektury poziom gęstości defektów przechodzących do kolejnych faz jest istotnie niższy niż w analogicznych fazach

projektu architektury niezawierających przeglądów formalnych”. Dla skonkretyzowania kolejnych pojęć potrzebujemy wprowadzić operacyjną definicję gęstości defektów. Mając tak zdefiniowane pojęcia oraz hipotezę empiryczną, przeprowadzamy eksperyment, np. analizujemy pewną liczbę projektów, z których jedna część stosuje przeglądy podczas fazy projektu architektury, a druga – nie. W każdym z tych projektów mierzymy liczbę defektów na jednostkę objętości kodu (według definicji operacyjnej gęstości defektów), a następnie przeprowadzamy porównanie wartości w obu tych grupach, stosując np. test tStudenta. Jeśli okaże się, że różnica jest istotna statystycznie, to skonstruowaliśmy poprawną teorię mówiącą o korzystnym wpływie przeglądów na poziom gęstości defektów. Na podstawie zebranych danych możemy również skonstruować model, który będzie przewidywał, jak bardzo zmniejsza się gęstość defektów w wyniku zastosowania przeglądów. W modelu tym możemy uwzględnić jeszcze inne, nierozważane do tej pory czynniki, takie jak wielkość lub typ projektu, poziom doświadczenia zespołu projektowego, wykorzystywana technologia itp.

39.2. Skale pomiarowe Aby przekształcić pojęcia w definicje operacyjne, musimy określić skalę pomiarową dla każdego pojęcia. Przedstawimy teraz cztery podstawowe skale pomiarowe, uporządkowane według ich „siły wyrazu”, od najprostszych (dla których np. nie da się przeprowadzić porównania czy testu statystycznego) do bardziej skomplikowanych (dla których istnieją czysto ilościowe metody statystyczne). Są to skale: nominalna; porządkowa; interwałowa; stosunkowa. skala pomiaru (ang. measurement scale) – skala ograniczająca typ analiz, które mogą być wykonywane na danych [23] Czasami specyfika pojęcia wymusza przyjęcie określonej skali pomiarowej, jeśli jednak można wykorzystać więcej niż jedną skalę, to ze względów praktycznych dobrze jest przyjąć tę o możliwie największej „sile wyrazu”.

39.2.1. Skala nominalna Skala

nominalna

służy

do

klasyfikacji

obiektów.

Pomiar

polega

na

przyporządkowaniu przedmiotu badania do dokładnie jednej spośród kilku zdefiniowanych kategorii. Kategorii tych nie można ze sobą porównywać. Przykładem skali nominalnej może być np. faza cyklu życia: specyfikacja wymagań, projekt, kodowanie, testowanie, utrzymanie. Nie można powiedzieć, że np. wartość „projekt” jest lepsza lub gorsza od wartości „testowanie”. Skala nominalna umożliwia natomiast agregację obiektów pomiaru według kategorii tej skali. Na przykład, po zebraniu danych o znalezionych defektach i przypisaniu każdego z nich do fazy jego wykrycia, możemy otrzymać wyniki takie jak na rysunku 39.2. Ważne jest, aby każdy znaleziony defekt można było przypisać do jednej i tylko jednej spośród dostępnych kategorii. Jeśli nie da się tego uzyskać (np. element należy do niezdefiniowanej kategorii lub można go przypisać do więcej niż jednej kategorii), oznacza to, że skala jest źle skonstruowana i należy ją poprawić. Elementy skali nominalnej (kategorie) muszą wyznaczać poprawny podział dziedziny obiektów, które są odnoszone na tej skali.

Rysunek 39.2. Przykład zastosowania skali nominalnej

39.2.2. Skala porządkowa Skala porządkowa służy do względnego porównywania elementów. Jest to skala nominalna wzbogacona o relację porównania, np. relację > („większe niż”). Przykładem skali porządkowej może być stopień zadowolenia (satysfakcja) klienta: bardzo niezadowolony; niezadowolony; neutralny (ani zadowolony, ani niezadowolony); zadowolony; bardzo zadowolony. Elementy te możemy uporządkować: bardzo zadowolony > zadowolony > neutralny > niezadowolony > bardzo niezadowolony. Możemy zatem powiedzieć, że „zadowolony” to coś lepszego niż „neutralny”, ale nie możemy powiedzieć, że różnica między „niezadowolony” a „neutralny” jest taka sama jak między „neutralny” a „zadowolony”. Skala porządkowa może również być skalą liczbową. Na przykład, użytkownik może ocenić poziom swojego zadowolenia w skali od 1 (bardzo niezadowolony) do 7 (bardzo zadowolony). Mimo wykorzystywania liczb, należy pamiętać, że cały czas operujemy skalą porządkową, w związku z czym różnica między 2 a 4 niekoniecznie musi być taka sama, jak między 5 a 7. Być może najbardziej znanym przykładem skali porządkowej jest tzw. skala Likerta. Jest ona pięciostopniowa. Pierwszy przykład dotyczący poziomu zadowolenia jest właśnie przykładem skali Likerta. Służy ona najczęściej do badania postaw wobec problemów bądź opinii1.

39.2.3. Skala interwałowa Skala interwałowa to skala porządkowa wzbogacona o możliwość definiowania różnic między poszczególnymi elementami skali. Przykładem skali interwałowej jest np. temperatura w stopniach Celsjusza. Jeśli o godzinie 10:00 temperatura otoczenia wynosiła 2°C a o 11:00 wzrosła do 6°C, to mamy prawo powiedzieć, że temperatura wzrosła o 4 stopnie Celsjusza. Nie możemy jednak powiedzieć, że

o jedenastej temperatura była trzy razy większa niż o dziesiątej, ponieważ 0°C nie jest zerem bezwzględnym (absolutnym). W rzeczywistych zastosowaniach skala ilorazowa jest bardzo rzadko spotykana, ponieważ tego typu skale zwykle mają zero, tzn. punkt odniesienia, co czyni je automatycznie skalami stosunkowymi.

39.2.4. Skala stosunkowa Jeśli na skali interwałowej zdefiniujemy punkt odniesienia (tzw. zero skali), to staje się ona skalą stosunkową. Na wartościach skali stosunkowej możemy nie tylko wykonywać operacje porównywania czy odejmowania, lecz także dzielenia. Przykładem skali stosunkowej może być temperatura w stopniach Kelwina, ponieważ 0 K to zero absolutne. Innym przykładem skali stosunkowej może być np. gęstość defektów na 1000 linii kodu. Jeśli w modułach A, B, C liczących odpowiednio 1000, 2000 i 3000 linii kodu znajduje się odpowiednio 10, 4 i 60 defektów, to gęstości defektów wynoszą odpowiednio 10, 2 i 20. Zatem gęstość defektów w module A jest pięć razy większa niż w module B i dwa razy mniejsza niż w module C. Oczywiście możemy również powiedzieć, że gęstość defektów w B jest o 8 mniejsza niż w A – wszystkie właściwości skal niższego rzędu występują w skalach wyższego rzędu.

39.2.5. Podsumowanie rodzajów skal pomiarowych W tabeli 39.1 podsumowano pomiarowych.

informacje dotyczące wprowadzonych skal

Tabela 39.1. Skale pomiarowe – podsumowanie

Skala

Dozwolone operacje

Przykłady

Nominalna

=

Faza cyklu życia Rodzaj s ys temu operacyjneg o

Porządkowa , =

Poziom dojrzałoś ci CMMI S kala zadowolenia klienta

Interwałowa , =, –, +

Temperatura według s kali Cels jus za

S tos unkowa , =, –, +, /, ×

Temperatura według s kali Kelwina Liczba defektów

39.3. Typy metryk Niezależnie od przyjętej skali pomiarowej, zebrane dane są poddawane różnego rodzaju operacjom matematycznym w celu ich analizy czy porównania. Aby metryka oddawała rzeczywiście istotę hipotezy empirycznej, musi mieć określoną strukturę. W kolejnych podrozdziałach przedstawimy kilka takich struktur. Istnieją dwa zasadnicze typy metryk: bezpośrednie i pośrednie. Metryki bezpośrednie mierzą wprost wartości atrybutów, natomiast metryki pośrednie (ang. derived measures) są funkcjami dwóch lub większej liczby innych metryk. W punktach 39.3.2–39.3.7 są opisane metryki pośrednie.

39.3.1. Metryka bezpośrednia (podstawowa) Metryka bezpośrednia to najprostsza metryka, wyrażająca po prostu liczbę lub ilość mierzonych elementów lub bezpośrednią wartość atrybutu: M =A gdzie A jest wartością liczbową. Przykładem metryki liczności może być liczba defektów znalezionych w danej fazie cyklu życia, liczba linii kodu itp.

39.3.2. Suma/różnica Jest to metryka wyrażająca sumę bądź różnicę dwóch wartości tego samego typu: M =A – B M =A +B gdzie A i B to wartości liczbowe. Przykładem metryki sumy/różnicy może być przewidywana liczba pozostałych w module defektów – w takim przypadku we wzorze (tym z różnicą) A to przewidywana całkowita liczba defektów w module, a B – liczba dotychczas wykrytych i usuniętych defektów.

39.3.3. Stosunek

Stosunek to wynik dzielenia jednej wartości przez drugą, przy czym licznik i mianownik należą do dwóch rozłącznych grup, tworzących w sumie grupę większą: M = A/B Przykładem stosunku może być stosunek liczby testerów do deweloperów w zespole projektowym. Na przykład stosunek 1:10 oznacza, że na jednego testera przypada 10 deweloperów. W takim przypadku zakłada się oczywiście, że żaden tester nie jest deweloperem i na odwrót.

39.3.4. Proporcja Proporcja to miara podobna do stosunku z tą różnicą, że licznik jest częścią mianownika:

Gdy licznik i mianownik są liczbami naturalnymi, proporcję nazywa się częstością względną. Przykładem proporcji może być liczba testów zautomatyzowanych do wszystkich stworzonych testów.

39.3.5. Odsetek Odsetek to proporcja wyrażona w postaci procentowej:

Jeśli w przykładzie z punktu 39.3.4 mamy 50 testów automatycznych i 80 ręcznych, to proporcja testów automatycznych wynosi

Wyrażając ją

jako odsetek, otrzymamy wartość 0,385 ⋅ 100% = 38,5%. Oznacza to, że testy automatyczne stanowią niecałe 40% wszystkich testów.

39.3.6. Miary iloczynowe Miary iloczynowe to miary wyrażane w jednostkach będących iloczynem dwóch lub więcej jednostek, np. osobotygodni, osobogodzin, osobomiesięcy: M =A ⋅ B Przykładem miary iloczynowej może być miara produktywności, wyrażona np. w osobotygodniach. 1 osobotydzień to ilość pracy, jaką jedna osoba jest

w stanie wykonać w czasie jednego tygodnia. Praca o pracochłonności 10 osobotygodni może zostać wykonana w 10 tygodni przez jedną osobę, w 5 tygodni przez 2 osoby, w 1 tydzień przez dziesięć osób2 itd., ogólnie, pracochłonność n osobotygodni to praca, jaką a osób wykona w b tygodni, gdzie a ⋅ b = n.

39.3.7. Tempo Tempo wyraża zmianę jednej wielkości na jednostkę innej wielkości. W przeciwieństwie do wcześniej zdefiniowanych miar, które są statyczne, tempo odnosi się do dynamiki danego zjawiska, najczęściej uwzględniając zmianę pewnej wartości w czasie: M = A/B Przykładem tempa może być częstość defektów, zdefiniowana jako iloraz liczby defektów do liczby sposobności popełnienia błędu. Na przykład, jeśli jedna linia kodu oznacza sposobność do popełnienia jednego błędu, gęstość defektów jest definiowana jako liczba defektów na linię kodu. W praktyce tego typu miary mnoży się przez odpowiedni współczynnik, aby wynikowe wartości nie były zbyt dużymi lub zbyt małymi liczbami. W kontekście gęstości defektów najczęściej stosuje się iloraz liczby defektów na 1000 linii kodu.

39.4. Spójność i odpowiedniość pomiaru Spójność (ang. reliability) i odpowiedniość (ang. validity) to dwie najważniejsze cechy charakteryzujące dobre pomiary (i równocześnie metryki). Spójność dotyczy powtarzalności pomiaru. Pomiar jest spójny, jeśli za każdym razem, gdy jest dokonywany (możliwe, że przez różne osoby), otrzymujemy ten sam lub bardzo podobny wynik. Na przykład, mając operacyjną definicję złożoności cyklomatycznej, jeśli poprosimy kilka osób o jej pomiar dla zadanego programu, to powinniśmy otrzymać identyczne wyniki. Pomiar jest odpowiedni, jeśli mierzy dokładnie to, co powinien, tzn. prawdziwą wartość mierzonego atrybutu. Standard IEEE 1061 [344] opisuje odpowiedniość w terminach następujących kryteriów: korelacja (ang. correlation) – istnieje wystarczająco silna liniowa zależność między mierzonym atrybutem a metryką;

śledzenie (ang,. tracking) – metryka może śledzić zmiany w atrybucie przez cały cykl jego życia; zgodność (ang. consistency) – metryka może uszeregować zbiór produktów lub procesów na podstawie określonego atrybutu; zdolność predykcji (ang. predictability) – metryka może przewidzieć wartość atrybutu z wymaganą dokładnością; moc dyskryminacyjna (ang. discriminative power) – metryka może odseparować zbiór komponentów komponentów złej jakości.

dobrej

jakości

od

zbioru

Na rysunku 39.3 symbolicznie przedstawiono spójność i odpowiedniość pomiaru. Środek tarczy reprezentuje prawdziwą wartość mierzonego atrybutu, a każda kropka – jeden pomiar. Pomiar z lewej strony koncentruje się wokół jednej wartości, zatem jest spójny, chociaż wartość ta leży daleko od środka tarczy. Pomiar środkowy koncentruje się wokół środka tarczy (średnia leży blisko wartości prawdziwej), ale ma duży rozrzut. Pomiar po prawej stronie jest zarówno spójny, jak i odpowiedni: wyniki są skupione wokół prawdziwej wartości.

Rysunek 39.3. Graficzna reprezentacja spójności i odpowiedniości pomiaru Ze statystycznego punktu widzenia, jeśli pomiary są określone przez estymatory, to spójność odpowiada estymatorowi efektywnemu, a odpowiedniość – estymatorowi nieobciążonemu. W tradycyjnej inżynierii

jakości spójność określa się mianem precyzji pomiaru, a odpowiedniość – dokładnością pomiaru. Zazwyczaj nie da się uzyskać „idealnego” pomiaru, który zawsze będzie mierzył prawdziwą wartość atrybutu. Ma to związek z istnieniem błędów pomiarowych, które omówimy w podrozdziale 39.5. Między odpowiedniością a spójnością z natury rzeczy musi istnieć pewien kompromis: osiągnięcie wysokiej spójności wymaga dokładnego zdefiniowania pomiaru, co może powodować spadek odpowiedniości. Jednocześnie, zagwarantowanie wysokiej odpowiedniości wymaga zwykle bardzo skomplikowanych, precyzyjnych definicji, co osłabia spójność [200]. Istnieją miary statystyczne pozwalające porównywać metryki pod kątem ich różnych charakterystyk. Na przykład, do oceny spójności pomiaru można wykorzystać tzw. indeks wariancji (ang. Index of Variation, IV). Niech P = {p 1, p 2, …, p n} będzie zbiorem pomiarów pewnej cechy za pomocą metryki M. Indeks wariancji jest stosunkiem odchylenia standardowego3 do wielkości zasadniczej: IV(M) = σ(P)/μ(P) gdzie σ(P) to odchylenie standardowe z próby P, a μ(P) – wartość średnia z tej próby. Im mniejszy wskaźnik IV, tym bardziej spójny pomiar. Opisana przez nas definicja odpowiedniości dotyczy tzw. wewnętrznej odpowiedniości mertyki (ang. internal validity). Wprowadza się również pojęcie tzw. zewnętrznej odpowiedniości (ang. external validity) zwanej czasem odpowiedniością predyktywną (ang. predictive validity). Odnosi się ona do zdolności metryki do generalizowania lub przenoszenia wyników pomiaru na inne populacje. Aby metryka była odpowiednia zewnętrznie, musi być odpowiednia wewnętrznie. Przykładem zewnętrznej odpowiedniości jest sytuacja, w której metryka średniego czasu do naprawy defektu w jednym projekcie może być użyta do przewidywania średniego czasu do naprawy defektu w innych projektach.

39.5. Błędy pomiarowe Każdy pomiar jest obarczony jakimś błędem. Istnieją dwa podstawowe typy błędów pomiarowych: błąd losowy oraz błąd systematyczny. Rozważmy przykład pomiaru masy ciała przy użyciu wagi łazienkowej. Waga może być

przeskalowana o 1 kg od położenia zerowego4. Każda osoba ważąca się na tej wadze otrzyma wynik powiększony o 1 kg w stosunku do swojej prawdziwej masy. Jest to przykład błędu systematycznego. Do tego mogą wystąpić drobne odchylenia związane z różnymi czynnikami losowymi, np. związanymi z wewnętrznym mechanizmem wagi. Jest to błąd losowy. W rezultacie, pomiar końcowy będzie sumą trzech składników: Pomiar wagi = Masa ciała + 1 kg + losowe odchylenie. W ogólnym przypadku możemy to opisać równaniem P = W + εS + ε gdzie P oznacza wartość dokonanego pomiaru, W – rzeczywistą wartość, εS – błąd systematyczny, a ε – błąd losowy. Błędy systematyczne są zwykle łatwe do wyeliminowania i pomiar jest wtedy sumą dwóch składników: P =W+ε W przypadku błędu losowego zakłada się, że ma rozkład normalny ze średnią w zerze. Oznacza to, że jeśli dokonamy wielu pomiarów, to w niektórych z nich błąd losowy będzie miał wartość dodatnią, w innych ujemną, ale jeśli pomiary uśrednimy (tzn. weźmiemy wartość oczekiwaną zmiennej losowej reprezentującej pomiary), to błędy te zniosą się. Patrząc na to od strony teoretycznej, mamy E(P) = E(W) + E(ε) = W + 0 = W gdzie E(X) oznacza średnią wartość X. Uśrednienie nieskończenie wielu pomiarów daje więc nam dokładny wynik. W rzeczywistości możemy dokonać tylko skończonej liczby pomiarów, a ponadto założenie o rozkładzie normalnym błędu losowego nie zawsze musi być prawdziwe. Niemniej jednak, uśrednienie odpowiednio dużej liczby pomiarów da nam wynik zbliżony do prawdziwego. Istnienie błędu losowego wpływa jednak na spójność pomiaru. Im mniejsze odchylenie standardowe błędu losowego, tym pomiar bardziej spójny. Mamy Var(P) = Var(W + ε) = Var(W) + Var(ε) gdzie Var(X) oznacza wariancję zmiennej X, czyli kwadrat jej odchylenia standardowego. Spójność pomiaru definiuje się jako

Im ρ(P) bliższe jedynki, tym większa spójność pomiaru.

39.6. Podstawowe zasady analizy danych Wiele organizacji dokonuje różnego rodzaju pomiarów, które następnie są raportowane i dyskutowane na spotkaniach. Na ich podstawie nierzadko podejmuje się główne, strategiczne decyzje. Dlatego tak ważna jest nie tylko odpowiedniość i spójność pomiarów, lecz także umiejętność właściwej ich interpretacji. Aby dobrze zinterpretować wyniki, nie wystarczy np. operować wartościami średnimi i ich porównaniem. Podczas analizy należy wziąć pod uwagę co najmniej następujące aspekty: wartości średnie lub inne miary tendencji centralnej, jeśli wartość średnia nie jest odpowiednia; odchylenie standardowe lub inną miarę określającą rozproszenie wyników wokół średniej; korelacje między mierzonymi wartościami; kryteria przyczynowości (np. czy wartościami nie istnieje z definicji?);

korelacja

między

dwoma

trendy długookresowe; czynniki nieuwzględnione przy pomiarach, a mogące wpływać na ten pomiar; graficzną postać danych, co może ułatwić interpretację wyników; wykorzystanie testów statystycznych, aby formalnie, statystycznie potwierdzać hipotezy dotyczące wyników pomiarów.

39.6.1. Miary tendencji centralnej Miary tendencji centralnej określają tzw. centralną tendencję zbioru danych, tzn. wartość, wokół której skupiają się te dane. Istnieją trzy główne miary tendencji centralnej wykorzystywane w statystyce: średnia, mediana i moda. Średnia. Niech x1, x2 , …, xn będą pomiarami pewnego atrybutu. Średnią (średnią arytmetyczną, wartością przeciętną, wartością oczekiwaną) z tej próby nazywamy wartość

Wartość średnią często oznacza się również jako μ(X) czy Sama wartość średnia nie charakteryzuje dokładnie próby. Rozważmy dwa projekty A i B, każdy złożony z 10 modułów. W projekcie pierwszym w każdym z modułów wykryto po jednym błędzie. W projekcie drugim dziewięć modułów było bezbłędnych, a dziesiąty miał 10 błędów. Odpowiednie średnie liczby defektów na moduł wynoszą:

Mamy zatem μ(A) = μ(B) – średnie liczby defektów w obu projektach są identyczne, chociaż oba projekty różnią się znacznie pod względem rozkładu tych błędów. Średnia jest miarą bardzo podatną na wartości odstające. Na przykład jeśli w projekcie A w jednym module zamiast 1 byłoby 11 błędów, średnia podskoczyłaby z 1 do 2. Mediana. Aby uniewrażliwić się na wartości odstające, możemy zamiast średniej stosować medianę. Jest to wartość dzieląca posortowany zbiór danych dokładnie na pół. Na przykład, aby obliczyć medianę dla zbioru pomiarów 6, 11, 3, 0, 4, 4, 1, 9, 6 najpierw szeregujemy te wartości od najmniejszych do największych: 0, 1, 3, 4, 4, 6, 6, 9, 11, a następnie znajdujemy wartość leżącą pośrodku. Mamy 9 pomiarów, więc środkowym pomiarem jest pomiar piąty, którego wartość w posortowanym ciągu danych wynosi 4. Jeśli pomiarów jest parzysta liczba, to za medianę przyjmuje się średnią arytmetyczną z dwóch środkowych elementów posortowanego ciągu danych. Na przykład, dla ciągu 4, 7, 11, 20 mediana to (7 + 11)/2 = 9. Wracając do przykładu z projektem A – jeśli w każdym module był 1 błąd, to mediana wyniesie 1. Jeśli w jednym z modułów liczba błędów wzrosła do 11, to mediana nie zmieni się – wartością środkową będzie nadal wartość

Rysunek 39.4. Rozkład dobrze opisywany przez modę Moda. Moda to wartość występująca najczęściej w zbiorze danych. Modę wykorzystuje się w sytuacjach, gdy rozkład wartości danych jest silnie skośny (ang. skewed distribution), nieregularny (np. występuje kilka pików w rozkładzie) lub gdy zawiera wartości odstające. Dla ciągu danych 6, 11, 3, 0, 4, 4, 1, 9 moda wynosi 4, ponieważ wartość 4 występuje w tym ciągu najczęściej (dwa razy, natomiast każda inna wartość tylko raz). Gdyby do ciągu tego dodać jeszcze wartość 6, mielibyśmy dwie mody: 4 i 6. Rozważmy rozkład liczby defektów z rysunku 39.4. Widzimy, że znakomita liczba modułów (26) ma 1, 2 lub 3 defekty. Rozkład dla pozostałych liczb defektów jest bardzo nieregularny. W takim przypadku możemy tendencję centralną scharakteryzować przez modę, która wynosi 1, 2 i 3. Oznacza to, że najwięcej modułów ma taką właśnie liczbę defektów. Średnia ważona. Specjalną odmianą średniej arytmetycznej jest średnia ważona, w której poszczególne wartości ze zbioru danych są mnożone przez tzw. wagi. Średnia arytmetyczna jest średnią ważoną, w której każdy z n elementów ma taką samą wagę 1/n. Przykładem średniej ważonej może być obliczanie poziomu ryzyka (patrz podrozdz. 19.6), w którym elementy określają wpływ poszczególnych ryzyk, a wagami są prawdopodobieństwa wystąpienia tych ryzyk. Formalnie, średnią ważoną oblicza się ze wzoru

gdzie x1 , …, xn są wartościami pomiarów, a w 1 , …, w n – odpowiadającymi im wagami.

39.6.2. Miary rozproszenia Miary rozproszenia określają, jak bardzo wartości pomiarów odchylają się od wartości centralnej. Najczęściej wykorzystywanymi miarami rozproszenia są: odchylenie standardowe, wariancja, rozstęp i rozstęp międzykwartylowy. Wariancja. Niech X = (x1, x2 , …, xn) będzie ciągiem zmierzonych wartości. Wariancję dla danych X definiuje się jako

gdzie μ jest średnią wartością X. Jeśli obliczamy wariancję z próby, która nie jest całą populacją, to wariancję tę oznaczamy przez s 2(X) i używamy wzoru5

Na przykład, dla danych 7, 3, 0, 6 mamy μ = 4, zatem wariancja wynosi

Gdyby wartości te pochodziły z próby, wtedy estymator wariancji z próby wyniósłby

Odchylenie standardowe. Wariancja ma jedną wadę: podawana jest w jednostkach, które są kwadratem jednostek oryginalnie mierzonych danych. Jeśli dane 7, 3, 0, 6 z przykładu oznaczają liczbę defektów w poszczególnych modułach, to wariancja wynosi 7,5 defektów do kwadratu, co nie ma interpretacji fizycznej. Dlatego często zamiast wariancji wykorzystuje się odchylenie standardowe (oraz odchylenie standardowe z próby), które jest po prostu pierwiastkiem z wariancji:

Dla naszego przykładu z danymi 7, 3, 0, 6 odchylenie standardowe wynosi defektu. Rozstęp i rozstęp międzykwartylowy. Rozstęp to różnica pomiędzy wartością największą i najmniejszą w danym zbiorze danych. Na przykład, dla danych 5, 9, 3, 11, –3, 5, 5, 3, 9, 14, 5 rozstęp wynosi 14 – (–3) = 17. Łatwo zauważyć, że miara ta jest podatna na wartości odstające. Dlatego częściej stosowaną miarą jest tzw. rozstęp międzykwartylowy. Jest to różnica między trzecim a pierwszym kwartylem. Pierwszy kwartyl to wartość, która odcina 25% posortowanych danych ze zbioru. Kwartyl trzeci to wartość odcinająca 75% danych (wspomniana wcześniej mediana jest drugim kwartylem, który odcina 50% danych). Jeśli posortujemy wspomniany wcześniej zbiór danych, to otrzymamy –3, 3, 3, 5, 5, 5, 5, 9, 9, 11, 13, 14. Pogrubioną czcionką zaznaczono pierwszy, drugi i trzeci kwartyl6, czyli 3, 5 i 9. Rozstęp międzykwartylowy wynosi 9 – 3 = 6.

39.6.3. Korelacja i regresja liniowa Korelacja to siła liniowego związku między dwoma zmiennymi losowymi. Jej miarą jest tzw. współczynnik korelacji, zwykle oznaczany grecką literą rho (ρ). Przyjmuje on wartości między –1 a 1. Wartość –1 oznacza, że między zmiennymi zachodzi idealna ujemna7 zależność liniowa. Wartość 0 oznacza, że korelacji nie ma, czyli brak związku między dwiema zmiennymi losowymi. Wartość 1 oznacza ścisłą dodatnią korelację między zmiennymi. Im bliżej –1 lub 1, tym korelacja silniejsza, im bliżej zera – tym słabsza. Przykłady różnych stopni korelacji pokazane są na rysunku 39.5. Współczynnik korelacji ρ między zmiennymi losowymi X i Y oblicza się ze wzoru

Rysunek 39.5. Różne stopnie korelacji Przykładem korelacji może być związek między liczbą punktów funkcyjnych modułu a liczbą defektów wykrytych podczas testów. Korelacja między tak zdefiniowanymi zmiennymi powinna być dodatnia – im więcej punktów funkcyjnych ma moduł, tym więcej zwykle jest w nim defektów. Należy jednak pamiętać, że korelacja nie oznacza przyczynowości (ang. correlation is not a causation). Z samego faktu istnienia dużej korelacji nie można wnioskować, która ze zmiennych wpływa na inną. Często związki takie jest bardzo trudno

udowodnić. Niezależnie od tego, jeśli wiemy, że między dwiema zmiennymi istnieje silna korelacja, to możemy zbudować model regresji liniowej, dzięki któremu na podstawie wartości jednej z tych zmiennych da się przewidzieć drugą. Wszystkie pakiety statystyczne mają możliwość automatycznego budowania takich modeli na podstawie istniejących danych. Przykład. Załóżmy, że zebraliśmy dane o defektach i punktach funkcyjnych8 dla 9 modułów. Są one przedstawione w tabeli 39.2. Tabela 39.2. Dane o punktach funkcyjnych i liczbie defektów dla modelu regresji

Moduł

1

2

3

4

5

6

7

8

9

Liczba PF

6

20

14

44

36

12

4

38

11

Liczba defektów

5

14

17

41

25

9

4

29

11

Na listingu 39.1 przedstawiono skrypt w pakiecie statystycznym R, w którym jest budowany model opisujący relację między liczbą punktów funkcyjnych modułu a liczbą defektów. W liniach 1 i 2 są definiowane dwa wektory zawierające dane z tabeli 39.2. W linii 3 każemy pakietowi R obliczyć korelację między tymi dwiema zmiennymi. Jak widać, jest ona dosyć duża – współczynnik ρ wynosi ponad 0,96. W linii 5., za pomocą polecenia lm budujemy model regresji liniowej (o nazwie m) liczby defektów w zależności od liczby punktów funkcyjnych. Równanie regresji ma postać y = ax + b, gdzie a i b są współczynnikami, y jest tzw. zmienną zależną (u nas jest to liczba defektów), a x – zmienną niezależną (u nas: liczba punktów funkcyjnych). Wyrażenie „–1” w tej instrukcji oznacza, że równanie modelu ma nie mieć wyrazu wolnego, tzn. chcemy, by współczynnik b był równy zero. Innymi słowy, prosta opisywana równaniem modelu ma przechodzić przez punkt (0,0). Jest to logiczne – dla 0 punktów funkcyjnych moduł powinien zawierać 0 defektów. Po wywołaniu w linii 6. szczegółów modelu, otrzymujemy (w liniach 8– 13) jego opis. Widzimy, że współczynnik a = 0,8229, zatem nasz model regresji ma postać DEF = 0,8229 ⋅ PF gdzie DEF to przewidywana liczba defektów w module, a PF – liczba punktów funkcyjnych modułu. Ostatnie dwa polecenia skryptu służą do narysowania na

ekranie wykresu zależności liczby defektów od punktów funkcyjnych oraz linii regresji. Wynik tych poleceń jest przedstawiony na rysunku 39.6.

Rysunek 39.6. Model liniowy zależności defektów od punktów funkcyjnych

1 2 3 4 5 6

> PF DEF cor(PF, DEF) [1] 0.9628234 > m m

7 8 Call: 9 lm(formula = DEF ~ PF - 1) 10 11 Coefficients: 12 PF 13 0.8229 14 15 > plot(PF, DEF) 16 > abline(a=0, b=0.8229) Listing 39.1. Skrypt w pakiecie R budujący model liniowy Każdy z dziewięciu punktów na rysunku oznacza współrzędne punktu odpowiadającego jednemu modułowi. Współrzędna x oznacza liczbę punktów funkcyjnych, a y – liczbę defektów. Tak więc moduł nr 1 jest reprezentowany przez punkt (6, 5), moduł nr 2 przez punkt (20, 14) i tak dalej. Widać, że istnieje silna zależność między tymi dwiema zmiennymi. Prosta na tym wykresie to równanie regresji y = 0,8229 ⋅ x. Mając model regresji, możemy przewidywać liczbę defektów w nowych modułach. Załóżmy, że jest tworzony dziesiąty moduł w rozważanym systemie. Z jego projektu wynika, że będzie miał 50 punktów funkcyjnych. Podstawiając tę wartość do modelu, otrzymamy, że przewidywana liczba defektów w tym module to y = 0,8229 ⋅ 50 = 41,145 ≈ 41. Znając tę szacunkową liczbę defektów, możemy przydzielić zawczasu odpowiednie zasoby oraz przedsięwziąć inne kroki, np. zarządzić inspekcję formalną (bo np. liczba defektów jest na tyle duża, że podejrzewamy, iż samo testowanie nie usunie ich wszystkich). Tak przewidziana wartość pozwala nam również ocenić, kiedy zakończyć proces testowania. Jeśli na przykład po 2 tygodniach testów wykryliśmy 38 defektów, a przez następne 2 dni nie wykryliśmy już żadnego, to możemy założyć, że usunęliśmy prawie wszystkie defekty i kończymy fazę testów dla tego modułu. Gdybyśmy jednak przez te pierwsze dwa tygodnie wykryli tylko 28 błędów, moglibyśmy wnioskować na podstawie modelu, że w module jest jeszcze około 13 defektów i czynności testowe nie powinny zostać w tym momencie przerwane.

Uwagi o korelacji. Współczynnik korelacji należy traktować z dużą ostrożnością. Zawsze warto najpierw przedstawić dane w formie graficznej, aby zobaczyć, jaka jest natura zależności między dwiema zmiennymi. Korelacja jest podatna na wartości odstające.

Rysunek 39.7. Podatność korelacji na wartości odstające Widać to dobrze na rysunku 39.7. Z lewej strony mamy silną dodatnią zależność między dwoma zmiennymi. Wystarczy jednak do zbioru danych dodać trzy wartości odstające (lewy górny róg prawego rysunku), aby zależność zmieniła się z dodatniej na ujemną. Jeśli w zbiorze danych zostaną wykryte wartości odstające, to należy przeanalizować możliwe powody ich pojawienia się oraz podjąć decyzję, czy powinny one pozostać w zbiorze, czy też zostać usunięte. Innym przykładem, który uzasadnia przedstawianie danych w formie graficznej przed ich analizą jest tzw. kwartet Anscombe’a [345]. Jest to zestaw czterech zbiorów danych, które – choć wyglądają zupełnie inaczej (patrz rys. 39.8) – wykazują niemal identyczne własności statystyczne. Dla każdego z tych zbiorów średnia współrzędnej x wynosi 9, wariancja 11, średnia y wynosi 7,50 (z dokładnością do 2 miejsc po przecinku), wariancja y 4,122 lub 4,127 (z dokładnością do 3 miejsc po przecinku), korelacje między x i y są równe 0,816 (z dokładnością do 3 miejsc po przecinku), a równanie regresji liniowej ma postać y = 0,5x + 3 (z dokładnością współczynników do odpowiednio 3 i 2 miejsc po przecinku).

Załóżmy, że do analizy dostaliśmy z czterech projektów takie właśnie dane, reprezentujące zależność średniej liczby defektów (y) od średniej złożoności cyklomatycznej poszczególnych klas (każdy punkt na wykresie reprezentuje jeden moduł, złożony z jednej lub więcej klas). Przykład z lewej górnej części rysunku pokazuje istnienie wyraźnej zależności, lecz nie jest to zależność liniowa. Przyjmuje ona kształt paraboli, dlatego aby ją modelować, musielibyśmy wykorzystać regresję nieliniową, dopasowując współczynniki a, b, c w równaniu postaci y = ax2 + bx + c. W prawym górnym rogu mamy zależność, którą można uznać za liniową – w tym przypadku linia regresji y = 0,5x + 3 dobrze modeluje związek między zmiennymi. Lewy dolny rysunek pokazuje prawie idealną zależność liniową, za wyjątkiem jednej informacji odstającej. Przed dalszą analizą należy tę wartość zbadać. Być może powstała ona na skutek błędów pomiarowych. Lewy dolny rysunek pokazuje, że poza jednym modułem, wszystkie mają taką samą średnią złożoność cyklomatyczną, ale rozrzut średniej liczby defektów jest zbyt duży. Wniosek z tego taki, że model opisujący liczbę defektów tylko przez rozmiar modułu jest niewystarczający i nie potrafi uchwycić czynników wpływających na liczbę defektów. Należy poszukać innych zmiennych niezależnych, które mogłyby mieć wpływ na tę wartość.

Rysunek 39.8. Kwartet Anscombe’a

39.6.4. Przyczynowość O ile w kontrolowanych eksperymentach odróżnienie przyczyny od skutku jest dosyć łatwe, o tyle podczas badania rzeczywistych zjawisk może być to wysoce nietrywialne. Niektórzy badacze proponują kryteria pozwalające na to odróżnienie, np. Babbie [346] proponuje następujący ich zestaw: 1) wyprzedzenie w czasie skutku przez przyczynę lub istnienie łatwej do zidentyfikowania zależności czasowej między nimi;

2) istnienie korelacji między badanymi zmiennymi; 3) relacja nie może być pozorna. Istnieje kilka typów zależności pozornych. W pierwszym obie analizowane zmienne x, y mogą być zupełnie ze sobą niezwiązane, ale mają wspólną przyczynę, która jest powodem pozornej zależności pomiędzy nimi. Drugi typ to istnienie zmiennej pośredniej. Zmienna x może bezpośrednio wpływać na „obcą” zmienną z, która z kolei bezpośrednio wpływa na y. W ten sposób otrzymujemy pozorną zależność bezpośrednią między x a y. Trzeci przypadek to taki, w którym obie analizowane zmienne są wskaźnikami tego samego pojęcia (jest to sytuacja podobna do typu pierwszego). W inżynierii oprogramowania być może najsłynniejszym przykładem zależności pozornej jest wzór Halsteada na długość programu: N = n1log n1 + n2log n2, gdzie n1 i n2 to odpowiednio liczba różnych operatorów oraz różnych argumentów wykorzystanych w programie, a N jest długością programu. Zaobserwowano bardzo wysoką korelację między właściwą długością kodu a tą ze wzoru Halsteada. Jednak, jak wykazali Card i Agresti [347], zarówno N ze wzoru, jak i właściwa długość programu są funkcjami n1 i n2, zatem korelacja ta istnieje z definicji. Zależność ze wzoru Halsteada nie jest więc przyczynowo-skutkowa.

1 Niektórzy badacze wykorzystują skalę Likerta, wykorzystując wartości liczbowe (np. od 1 do 5) i stosują wskaźniki będące sumami tych wartości pochodzących z odpowiedzi na większą liczbę pytań. Choć można np. porównywać ze sobą dwie średnie z takich zestawów odpowiedzi, postępowanie to – z teoretycznego punktu widzenia – jest metodologicznie niepoprawne. 2 Należy bardzo uważać ze stosowaniem tego typu miar. W praktyce zwykle bywa tak, że wzrost liczby osób wykonujących daną pracę nie jest proporcjonalny do zmniejszenia wymiaru czasu. Na przykład, jeśli 1 osoba wykona pewną pracę w 2 tygodnie, to zwykle 2 osoby tę samą pracę wykonają w więcej niż jeden tydzień. Ma to związek z wieloma różnymi czynnikami, np. wzrostem nakładu czasowego na komunikację między tymi osobami. 3 Więcej informacji dotyczących statystyki Czytelnik znajdzie w Dodatku C. 4 Przykład ten dotyczy starych typów wag, mających specjalne pokrętło pozwalające na wyskalowanie położenia początkowego wskaźnika wagi. W nowoczesnych występują.

wagach

elektronicznych

tego

typu

mechanizmy

nie

5 Różnica między wzorem na wariancję z populacji a wariancję z próby wynika stąd, że chcemy, aby wariancja z próby była estymatorem nieobciążonym. Postać wzoru wynika z prostych obliczeń i jest związana z pojęciem stopni swobody. Pomijamy jednak te rozważania, gdyż wybiegają poza zakres tej książki. Czytelnika zainteresowanego statystyką odsyłamy do znakomitego podręcznika [349]. 6 W przypadku zbiorów dyskretnych istnieje wiele metod wyliczania kwartyli. Metody te mogą dać różne wyniki. My przyjęliśmy jedną z metod, w której wartości kwartyli muszą należeć do rozważanego zbioru. 7 Zależność ujemna oznacza w tym kontekście, że wraz ze wzrostem wartości jednej zmiennej wartości drugiej maleją. Zależność dodatnia oznacza, że wzrost wartości jednej zmiennej pociąga za sobą wzrost wartości drugiej zmiennej. 8 Więcej o punktach funkcyjnych i sposobie ich pomiaru Czytelnik znajdzie w punkcie 41.2.1.

40. Narzędzia kontroli jakości

40.1. Klasyfikacja narzędzi Tak, jak mechanik ma swój „pas z narzędziami” czy też „pudełko z narzędziami” (ang. toolbox, tool belt), tak inżynier jakości powinien mieć analogiczny pas czy pudełko z narzędziami służącymi mu do przeprowadzania analiz w zakresie jakości oprogramowania. W rozdziale tym dokonamy przeglądu takich narzędzi. Część z nich pojawiła się już wcześniej na kartach tej książki – w takim przypadku nie będziemy dokładnie omawiać danego narzędzia, a jedynie podamy odsyłacz do rozdziału, w którym jest przedstawione jego wykorzystanie. Narzędzia kontroli jakości można pogrupować w kategorie odpowiadające czynnościom wykonywanym w ramach procesu zarządzania jakością: narzędzia planowania i wdrażania projektu; narzędzia wspomagające kreatywność; narzędzia analizy procesu; narzędzia do zbierania i analizy danych; narzędzia analizy przyczyn źródłowych; narzędzia do oceny i podejmowania decyzji. Istnieje bardzo wiele narzędzi, ale można wyróżnić kilka ich generycznych typów (np. generycznymi typami pojazdów mogą być: pojazdy lądowe, statki i statki powietrzne). Generyczne typy narzędzi inżynierii jakości to: arkusze i listy kontrolne (ang. check sheets, checklists); diagramy przepływu (ang. flowcharts); wykresy (ang. graphs);

diagramy macierzowe (ang. matrix diagrams); tabele (ang. tables); diagramy drzewiaste (ang. tree diagrams); mapy dwuwymiarowe (ang. two-dimensional charts). Niektóre narzędzia są przydatne tylko w wybranych krokach procesu poprawy jakości. Możemy wyróżnić 10 takich kroków: 1. Tworzenie planów. 2. Potrzeby klienta. 3. Bieżący stan. 4. Możliwości. 5. Przyczyna źródłowa. 6. Zmiany. 7. Wykonanie. 8. Kontrola. 9. Standaryzacja. 10. Wyciąganie wniosków.

40.2. Rodzaje narzędzi oraz obszary ich zastosowań W tabeli 40.1 podajemy pełniejszy zestaw narzędzi kontroli jakości, w postaci tzw. macierzy narzędzi. Wiersze zawierają poszczególne narzędzia pogrupowane według typów, a kolumny odpowiadają wymienionym dziesięciu krokom procesu poprawy jakości. Znak „X” na przecięciu wiersza i kolumny oznacza, że narzędzie może być wykorzystane w danym kroku. Niektóre narzędzia wymienione w tabeli się powtarzają, ponieważ mogą przynależeć do różnych typów. Niektóre z narzędzi były już omówione w książce – w takim wypadku w tabeli podano odsyłacz do rozdziału, w którym jest przedstawione jego wykorzystanie. Tabela 40.1 została opracowana na podstawie macierzy narzędzi pochodzącej z książki Nancy Tague The Quality Toolbox [348]. Podręcznik ten dokładnie omawia wszystkie wymienione narzędzia. Opiszemy teraz najważniejsze narzędzia przydatne inżynierowi jakości oprogramowania, które nie były omówione we wcześniejszych rozdziałach.

40.3. Statystyczna kontrola procesu Narzędzia statystycznej kontroli procesu umożliwiają, przez pomiary parametrów procesu w czasie, zaobserwowanie anomalii w tym procesie. Przykładem może być pojawienie się obserwacji o podejrzanie wysokiej bądź niskiej wartości, zwiększona wariancja (rozrzut) mierzonych wartości, zbyt długie sekwencje rosnących bądź malejących wartości itp. Każda anomalia tego typu może wskazywać na rozregulowanie procesu. Narzędzia statystycznej kontroli procesu umożliwiają szybką identyfikację problemu. Tabela 40.1. Narzędzia kontroli jakości

Narzędzie (nazwa angielska)

T worzenie Potrzeby Bieżący Przyczyn Możliwości planów klienta stan źródłowa

NARZĘDZIA PLANOW ANIA I W DRAŻANIA PROCESU Analiza akcjonarius zy (stakeholder analysis) 19.5.1

X

Analiza pola s ił (force-field analysis) 29.5.1 Analiza możliwych problemów (potential problem analysis) Ciąg łoś ć celów zes połu X

X

(continuum of team goals) Cykl Deming a (Plan-do-studyact cycle) 31.4.1

X

X

X

X

X

Definicje operacyjne (operational definitions) 39.1

X

X

X

X

X

Diag ram drzewias ty (tree diagram) 4.2, X 6.2.9, 8.7, 22.2.3, 32.5.1

X

X

X

X

Diag ram 2D (twodimensional diagram) 8.9, 12.2, 19.9.4

X

X

X

X

Diag ram Gantta (Gantt chart) X 21.2.2 Diag ram kontyng encji (contingency diagram) 21.2.6, 23.2.3 Diag ram macierzowy

(matrix diagram) X 8.1.6, 12.2, 19.6.3, 19.9.4 Diag ram przepływu (flowchart) 3.2, 4.1, 5.4.3, 9.15.3, 10.1, 19.9.4

X

X

X

Diag ram relacji (relations X diagram) 3.1.3, 4.3.2, 8.1.6, 10.6

X

X

X

X

X

Diag ram s trzałkowy X (arrow diagram) 4.1.2 Karta planwyniki (planresults chart) Karta projektu (project charter)

X

X

X

Korekta językowa (wordsmithing)

X

X

X

Lis ta kontrolna (checklist) 10.4, 20.3, 27.2.2

X

X

X

X

Lis ta kontrolna karty projektu (project charter checklist)

X

Mapa myś li (mind map) 29.3.3

X

X

X

X

X

Ocena s potkania (meeting evaluation)

X

X

X

X

X

Prezentacja (presentation)

X

X

X

S cenopis obrazkowy (storyboard)

X

X

X

X

X

Tabela (table) 6.1.6, 8.3, 8.8.5

X

X

X

X

X

Tes t ACORN (ACORN test)

X

X

Przes zkody i korzyś ci (barriers and benefits exercise)

Wykres decyzji w proces ie (process

decision program chart) Zrównoważona Karta Wyników X (balanced scorecard) 35.2.2

X

X

NARZĘDZIA W SPOMAGAJĄCE KREAT YW NOŚĆ 5W2H (5W2H)

X

6-3-5 (brainwriting)

X

Benchmarking (benchmaring) 19.9.11

X

Burza mózg ów (brainstorming) 29.2.1

X

X

Diag ram podobieńs twa (affinity diagram) 29.3.1

X

X

Diag ram relacji (relations X diagram) 3.1.3, 4.3.2, 8.1.6, 10.6

X

Diag ram rybiej oś ci (desired-

X

X

X

X

X

X

X

X

X

X

X

X

result fishbone) 29.5.3, 33.2.4 Mapa myś li (mind map) 29.3.3

X

X

NGT (Nominal Group Technique) X 29.2.2

X

S cenopis obrazkowy (storyboard)

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

NARZĘDZIA ANALIZY PROCESU 5W2H (5W2H) Analiza CTQ (critical-toquality analysis)

X

Analiza wartoś ci dodanej (valueadded analysis ) Benchmarking (benchmarking) 19.9.11 CoQ – Kos zt jakoś ci (cost-of-

X

X

X

X

X

poor-quality analysis) 24.2 Diag ram drzewias ty (tree diagram) 4.2, 6.2.9, 8.7, 22.2.3, 32.5.1 Diag ram macierzowy (matrix diagram) 8.1.6, 12.2, 19.6.3, 19.9.4 Diag ram przepływu (flowchart) 3.2, 4.1, 5.4.3, 9.15.3, 10.1, 19.9.4

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

Diag ram przepływu prac (work flow diagram) Diag ram relacji (relations X diagram) 3.1.3, 4.3.2, 8.1.6, 10.6

X

X

Diag ram S IPOC (SIPOC diagram)

X

X

Dom jakoś ci, QFD (house of quality) 19.9.10

X

X

X

Drzewo CTQ (critical-toquality tree)

X

X

X

Drzewo wymag ań i miar (requirementsand-measures tree)

X

X

X

FMEA (Failure Modes and Effects Analysis) 19.9.8, 21.2.2

X

Macierz przyczynowos kutkowa X (cause-and-effect matrix) 8.4

X

S cenopis obrazkowy (storyboard)

X

X

Tabela wymag ań (requirements table)

X

X

X

X

Zapobieg anie błędom (mistake proofing) NARZĘDZIA DO ZBIERANIA I ANALIZY DANYCH Analiza korelacji (correlation analysis) 39.6.3

X

X

Analiza reg res ji (regression analysis) 39.6.3

X

X

Analiza ważnoś ciwydajnoś ci (importanceperformance analysis)

X

X

X

Ankieta (survey) X 16.4, 49.1.5

X

X

X

X

X

X

X

Arkus z kontrolny (check sheet) Benchmarking (benchmarking) 19.9.11

X

X

Definicje operacyjne (operational definitions) 39.1 Głos klienta (voice of the customer table) 19.9.10

X

X

X

X

His tog ram (histogram) 3.1.6, 23.2.2, 23.2.4, 27.2.3, 33.2.2

X

X

X

X

X

Projektowanie eks perymentów (design of experiments) 8.8.5

X

Próbkowanie (sampling) 8.13

X

Rozkład normalny (normal probability plot) 32.2.4, 33.2.2 S tratyfikacja (stratification) 5.2, 8.6.2, 10.2, 33.2.2

X

X

X

X

X

X

X

X

X

X

X

S tudium możliwoś ci proces u (process capability study)

X

X

S tudium powtarzalnoś ci (repeatability and reproducibility study)

X

X

X

X

X

X

Tablica kontyng encji (contingency table) 21.2.6, 23.2.3

X

X

X

Tes towanie hipotez (hypothesis testing) 33.2.2

X

X

X

X

X

X

X

Tabela (table) 6.1.6, 8.3, 8.8.5

X

Ws kaźnik PGCV (PGCV index) Ws kaźnik wydajnoś ci (performance index)

X

X

X

Wykres (graph) 16.6.1, 19.8.3

X

X

X

X

Wykres czas u cyklu (cycle time chart) 40.4

X

X

X

Wykres kontrolny (control chart) 40.3

X

X

X

X

X

X

Wykres przebieg u (run chart) 16.2.4, 23.2.1, 33.2.2

X

X

X

Wykres pudełko (boxplot)

X

X

X

Wykres radarowy1 (radar chart)

X

X

Wykres Pareto (Pareto chart) 19.6.5, 33.2.2

X

X

Wykres X-Y (scatter plot) 19.1, 19.9.4, 33.2.2 Zrównoważona Karta Wyników

X

X

X

X

X

(balanced scorecard) 35.2.2 NARZĘDZIA ANALIZY PRZYCZYN ŹRÓDŁOW YCH Analiza drzewa us terek (fault tree analysis) 19.9.2

X

Analiza pola s ił (force-field analysis) 29.5.1

X

Diag ram dlaczeg odlaczeg o (whywhy diagram) 40.5.1

X

Diag ram drzewias ty (tree diagram) 4.2, 6.2.9, 8.7, 22.2.3, 32.5.1

X

Diag ram kontyng encji (contingency diagram) 21.2.6, 23.2.3

X

Diag ram macierzowy (matrix diagram)

X

8.1.6, 12.2, 19.6.3, 19.9.4 Diag ram relacji (relations diagram) 3.1.3, 4.3.2, 8.1.6, 10.6

X

Diag ram rybiej oś ci (fishbone diagram) 29.5.3, 33.2.4

X

FMEA (Failure Modes and Effects Analysis) 19.9.8, 21.2.2

X

Macierz jes t-nie jes t (is-is not matrix) 40.5.2

X

S tratyfikacja (stratification) 5.2, 8.6.2, 10.2, 33.2.2

X

Wykres Pareto (Pareto chart) 19.6.5, 33.2.2

X

Wykres X-Y (scatter plot) 19.1, 19.9.4, 33.2.2

X

NARZĘDZIA DLA OCENY I PODEJMOW ANIA DECYZJI Diag ram macierzowy (matrix diagram) X 8.1.6, 12.2, 19.6.3, 19.9.4

X

Drzewo decyzyjne (decision tree) 6.2.8 Filtrowanie kryteriów (criteria filtering)

X

X

X

Macierz decyzji (decision matrix) X 8.15, 28.2.4 Macierz priorytetyzacji (prioritetization matrix) 19.1, 29.3.2

X

X

X

X

X

X

Odwrócony diag ram rybiej oś ci (reverse fishbone diagram) PMI2

X

X

Porównanie parami (paired comparisons) 29.4.4

X

X

X

Redukcja lis ty (list reduction) 29.2

X

X

X

Wielokrotne g łos owanie (multivoting) 22.2.4, 29.4.2, 29.4.3

X

X

X

Wykres drzewa (tree diagram) 4.2, 6.2.9, 8.7, 32.5.1

X

X

X

X

Wykres 2D (twodimensional X chart) 8.9, 12.2, 19.9.4

X

X

X

Wykres efektywneos iąg alne X (effectiveachievable chart)

X

Inżynier jakości musi pamiętać o jednej podstawowej zasadzie: w analizie procesu ważne są nie tylko średnie czy bieżące wartości jego parametrów, lecz także zmienność procesu oraz jej źródła. W przypadku niektórych narzędzi teoria, która za nimi stoi zakłada, że istnieje jedno źródło zmienności procesu.

W praktyce prawie nigdy nie jest to prawdą. Czynników powodujących zmiany czy przesunięcia wartości parametrów procesu jest zazwyczaj bardzo wiele. Należy je identyfikować i dążyć do zminimalizowania zmienności tak, aby proces był możliwie jak najbardziej ustabilizowany. W wielu firmach decyzje biznesowe podejmuje się na podstawie raportów, które zawierają zwykle: wartość średnią procesu z danego miesiąca, analogiczną wartość sprzed roku oraz wartość z poprzedniego miesiąca. Analizy na podstawie tego typu danych są bardzo niebezpieczne – jeśli nie rozumiemy zmienności, to nie bierzemy pod uwagę trendów długookresowych, nie staramy się zidentyfikować wszystkich możliwych czynników wpływających na cały proces (nie tylko na jego bieżącą wartość), ryzykujemy podejmowaniem złych decyzji. Techniki statystycznej kontroli procesu pomagają w ustabilizowaniu procesu i zrozumieniu przyczyn jego ewentualnych zmian.

40.3.1. Wykres przebiegu Wykres przebiegu to po prostu graficzne przedstawienie kolejnych zmierzonych wartości parametru procesu w funkcji czasu. Przykładowy wykres przebiegu jest pokazany na rysunku 40.1.

Rysunek 40.1. Wykres przebiegu dla dostępności systemu

Przedstawia on dostępność systemu (w %) w 19 kolejnych tygodniach jego użytkowania. Podczas analizy wykresu przebiegu należy zwracać uwagę na następujące aspekty: powtarzające się wzorce; nagłe przesunięcie wartości w górę lub w dół wykresu; długotrwały trend (rosnący lub malejący); wartości odstające. Na rysunku 40.1 widzimy, że do 12 tygodnia proces jest w miarę stabilny – dostępność systemu oscyluje na poziomie ok. 96%. Od tygodnia 13 nastąpił jednak gwałtowny spadek dostępności (średnio ok. 87%). Jest to zjawisko, którego powody należy wyjaśnić i wyeliminować. Odmianą wykresu przebiegu jest tzw. S-kształtny wykres przebiegu (ang. Scurve run chart). Śledzi on skumulowaną wartość parametru procesu w czasie. Zwykle wykres ten ma kształt litery S, stąd jego nazwa. Przykładowy wykres Skształtny jest pokazany na rysunku 40.2. Przedstawia on skumulowaną liczbę zaimplementowanych testów w ciągu pierwszych 16 tygodni projektu testowego.

Rysunek 40.2. S-kształtny (skumulowany) wykres przebiegu Często przebieg wykresu S-kształtnego jest porównywany z wartościami przewidywanymi bądź historycznymi tak, aby analiza mogła być

przeprowadzona w odpowiednim kontekście (np. czy nasz projekt opóźnia się w stosunku do założonego planu lub innych, wcześniejszych projektów).

40.3.2. Karty kontrolne Karty kontrolne są podobne do wykresów przebiegu. Różnią się jednak dwiema zasadniczymi cechami: po pierwsze, każdy punkt na karcie kontrolnej reprezentuje średnią wartość z kilku pomiarów. Po drugie, karta kontrolna ma zdefiniowaną linię centralną CL (ang. Centerline) oraz granice kontrolne, w ramach których proces powinien się mieścić: górną UCL (ang. Upper Control Limit) oraz dolną LCL (ang. Lower Control Limit). Wszystkie te trzy parametry są wyliczane z odpowiednich wzorów matematycznych na podstawie początkowego przebiegu procesu. Karta kontrolna pozwala monitorować, czy przebieg procesu mieści się w założonych granicach. karta kontrolna, karta Shewharta (ang. control chart, Shewhart chart) – narzędzie zarządzania procesem, używane do monitorowania procesu i określania, czy jest statystycznie kontrolowany; pokazuje graficznie wartość średnią oraz górną i dolną granicę kontrolną procesu Aby wyznaczyć wartości CL, UCL i LCL, stosuje się najpierw procedurę wstępną, taką samą dla wszystkich typów kart kontrolnych. Na podstawie danych historycznych wyznacza się próbne granice kontrolne, usuwa dane, które się w nich nie mieszczą, a następnie granice kontrolne są obliczane ponownie i stosowane do przyszłych danych. Przykładowa karta kontrolna jest pokazana na rysunku 40.3.

Rysunek 40.3. Karta kontrolna i przykłady anomalii Na karcie możemy wyróżnić następujące elementy: linię centralną (CL), górny i dolny limit kontrolny (UCL, LCL), a także osiem obszarów oddzielonych poziomymi liniami. Obszar oddalony o jedno odchylenie standardowe od CL nazywać będziemy strefą A, obszar znajdujący się między jednym a dwoma odchyleniami standardowymi – strefą B, a obszar między dwoma a trzema odchyleniami standardowymi – strefą C. Ustabilizowany proces powinien mieć wartości zawierające się między LCL a UCL, czyli wpadające w strefy A, B i C. Nie powinien również zawierać anomalii. Ich wystąpienie – w przypadku stabilnego procesu – jest bardzo mało prawdopodobne, dlatego jeśli się pojawi, wymaga zbadania. Wyróżnia się pięć rodzajów anomalii dla wykresu na karcie kontrolnej (na rysunku oznaczone jako A1–A5): A1: dwa z trzech kolejnych punktów leżą poza obszarem B, tzn. w odległości większej niż 2σ od CL; A2: wartość odstająca (poza obszarem C, czyli w odległości większej niż 3σ od CL; A3: osiem kolejnych punktów po tej samej stronie CL; A4: cztery z pięciu kolejnych punktów leży poza obszarem A, tzn. w odległości większej niż 1σ od CL; A5: osiem kolejnych punktów formuje trend wznoszący lub opadający.

Istnieją również inne zestawy takich reguł, świadczących o nieustabilizowanym procesie. Przykładem mogą być tzw. kryteria Nelsona [349]: 1. Jedna obserwacja poza 3 odchyleniami standardowymi. 2. 9 kolejnych punktów po jednej stronie CL. 3. 6 kolejnych punktów regularnie wznoszących się lub opadających. 4. 14 kolejnych punktów zmieniających się na przemian w górę i w dół. 5. 2 z 3 kolejnych punktów poza 2σ. 6. 4 z 5 kolejnych punktów poza 1σ. 7. 15 kolejnych punktów w pasie ±1σ linii centralnej. 8. 8 kolejnych punktów po obu stronach linii centralnej, wszystkie poza ±1σ. Wykrywanie anomalii odbywa się z reguły automatycznie, przy użyciu oprogramowania lub pakietów statystycznych. Granice kontrolne dla większości kart kontrolnych wyznacza się za pomocą rozkładu normalnego. Zgodnie z Centralnym Twierdzeniem Granicznym rozkład wartości średniej w próbie dąży do rozkładu normalnego wraz ze wzrostem rozmiaru próby. Dlatego, w przypadku zbierania danych z procesu statystyka sumaryczna (wartość średnia) będzie miała rozkład bliższy normalnemu niż w przypadku pojedynczych, niezagregowanych obserwacji. Każdy punkt na wykresie kontrolnym jest więc uśrednioną wartością z kilku pomiarów. Z własności rozkładu normalnego wiemy, że prawdopodobieństwo odchylenia się od średniej o więcej niż 3 odchylenia standardowe wynosi ok. 0,0026. Oznacza to, że na 1000 obserwacji średnio około 3 z nich znajdą się poza limitami kontrolnymi. Przy ograniczonej liczbie punktów na karcie kontrolnej zaobserwowanie nawet jednego poza tymi limitami jest wystarczającym powodem do odrzucenia hipotezy mówiącej, że proces jest ustabilizowany na rzecz hipotezy mówiącej o nieustabilizowanym procesie. Opiszemy teraz różne rodzaje kart kontrolnych. Przez xi oznaczać będziemy zbiór wartości w i-tej próbie, to średnia z i-tej próby, to średnia z tych średnich, a k – liczba prób. We wzorach na górne i dolne limity będziemy wykorzystywać różne stałe, podane w tabeli 40.2. Tabela 40.2. Parametry kart kontrolnych

Do estymacji σ

Dla karty

Dla karty R

Dla karty s

n

c4

d2

A2

A3

D3

D4

B3

B4

2

0,7979

1,128

1,880

2,659

0

3,267

0

3,267

3

0,8862

1,693

1,023

1,954

0

2,575

0

2,568

4

0,9213

2,059

0,729

1,628

0

2,282

0

2,266

5

0,9400

2,326

0,577

1,427

0

2,115

0

2,089

6

0,9515

2,534

0,483

1,287

0

2,004

0,030

1,970

7

0,9594

2,704

0,419

1,182

0,076

1,924

0,118

1,882

8

0,9650

2,847

0,373

1,099

0,136

1,864

0,185

1,815

9

0,9693

2,970

0,337

1,032

0,184

1,816

0,239

1,761

10 0,9727

3,078

0,308

0,975

0,223

1,777

0,284

1,716

15 0,9823

3,472

0,223

0,789

0,348

1,652

0,428

1,572

20 0,9869

3,735

0,180

0,680

0,414

1,586

0,510

1,490

25 0,9896

3,931

0,153

0,606

0,459

1,541

0,565

1,435

Karta kontrolna

Pozwala na wykrycie przesunięć wartości średniej

procesu. Do skonstruowania karty

potrzebujemy estymatorów wartości

parametrów ze wzoru określającego limity kontrolne:

gdzie μ jest prawdziwą średnią procesu, a σ jego prawdziwym odchyleniem. Dobrym (zgodnym i nieobciążonym) estymatorem średniej jest średnia z wszystkich prób. Estymatorem odchylenia może być odchylenie z próby, ale jest ono dobre tylko dla dużych prób, prób, n > 10. W odniesieniu do mniejszych prób zamiast odchylenia z próby rozważa się uśrednienie zakresów wartości w każdej próbie. Ostatecznie, elementy karty kontrolnej

wyglądają następująco:

gdy rozmiar próby n ≤ 10, gdy rozmiar próby n > 10, gdzie

jest średnią odchyleń standardowych wszystkich grup, A 2 i c4 są stałymi z tabeli 40.2, a Ri to zakres wartości w i-tej próbie, czyli różnica między

największą i najmniejszą wartością w tej próbie. Rozważmy następujący przykład. Przeprowadzono 8 pomiarów średniego czasu od zgłoszenia do naprawy defektu (dane w godzinach). Każda z tych 8 prób składa się z czterech zgłoszeń. Pomiary są pokazane w tabeli 40.3. W trzech jej ostatnich kolumnach znajdują się: wartość średnia dla pomiaru (czyli kolejne pomiary odnoszone na Tabela 40.3. Pomiary dla karty kontrolnej

Pomiar Pomiar Pomiar Pomiar Próba 1 2 3 4

Średnia Odch. std. Ri si

x1

3

7,00

x2

10

6

2

3

5,25

3,594

8

x3

4

6

6

7

5,75

1,258

3

x4

6

3

8

3

5,00

2,449

5

x5

5

5

3

5

4,50

1,000

2

x6

4

7

11

9

7,75

2,986

7

x7

6

5

6

7

6,00

0,816

2

x8

8

3

5

7

5,75

2,217

5

11

6

8

3,367

8

Ś rednie: karcie kontrolnej), odchylenie standardowe oraz zakres wartości dla każdej z prób. Na przykład zakres dla próby x1 wynosi 8, ponieważ jest to różnica między największą (11) a najmniejszą (3) wartością w próbie. Średnia wartość średnich to 5,875 i jest to wartość linii centralnej. Średni zakres

Aby obliczyć górny i dolny limit kontrolny, skorzystamy z podanych wcześniej wzorów. Próba jest mała (n = 8 ≤ 10), dlatego stosujemy wzory Współczynnik A 2 odczytujemy z tabeli 40.2 dla n = 4, bo każda próba składa się z 4 pomiarów. Mamy A 2 = 0,729, zatem CL = 5,875, UCL = 5,875 + 0,729 ⋅ 5 = 9,52, LCL = 5,875 – 0,729 ⋅ 5 = 2,23.

Rysunek 40.4. Karta kontrolna Otrzymujemy kartę kontrolną przedstawioną na rysunku 40.4. Każdy z dwóch pasów: między UCL i CL oraz między CL i LCL możemy podzielić na trzy równe części, otrzymując strefy A, B i C odpowiadające obszarom o granicach odległych od CL o odpowiednio 1, 2 i 3 odchylenia standardowe.

Karty kontrolne R i s. Karta

służyła do kontroli wartości średniej procesu.

Karty R i s służą natomiast do kontroli wariancji procesu. Jeżeli proces charakteryzuje się dużą zmiennością, to znaczy, że parametry wyrobów mają szerszy niż dopuszczalny zakres i jakość produktu jest gorsza [350]. Elementy karty R:

gdzie to średnia z zakresów wszystkich grup (tak jak w karcie ). Karta s zamiast zakresów zmienności wykorzystuje odchylenia standardowe. W latach 20. XX wieku używano karty R, ponieważ łatwiej policzyć zakresy zmienności niż odchylenia standardowe. Obecnie, w dobie komputerów nie stanowi to problemu. Elementy karty s:

Karta kontrolna p. Służy do kontroli udziału wyborów wadliwych w procesie produkcyjnym. W inżynierii jakości przykładem może być liczba modułów zawierających przynajmniej jeden błąd wśród wszystkich stworzonych modułów. Niech p 1, p 2, …, p k oznaczają frakcję produktów wadliwych w partiach 1, 2, …, k. Przez

oznaczymy średnią z tych wartości. Elementy karty p:

Proces uznaje się za nieustabilizowany, jeśli frakcja przynajmniej jednej próby przekroczy wyznaczone granice kontrolne. Karta kontrolna c. Służy do kontroli liczby defektów na egzemplarz wyrobu. Na przykład, może to być liczba defektów na 1000 linii kodu. Zmienna losowa opisująca rozkład liczby błędów w określonym czasie lub na określoną liczbę okazji do ich popełnienia modeluje się rozkładem Poissona i ten rozkład jest właśnie użyty w karcie kontrolnej c. Parametry karty c:

gdzie jest estymatorem c – średniej liczby defektów na jednostkę wyrobu, obliczanym jako iloraz sumy wszystkich defektów przez liczbę wszystkich jednostek wyrobu. Karta kontrolna x. Jest to karta podobna do karty Różnica polega na tym, że tutaj proces jest kontrolowany za pomocą pomiarów pojedynczych obserwacji, a nie pomiarów uśrednionych z większej grupy. Może być bardziej praktyczna

w zastosowaniach inżynierii jakości oprogramowania, ze względu na to, iż zwykle w dziedzinie tej mamy do czynienia z niewielką liczbą pojedynczych pomiarów (w końcu produkcja oprogramowania to nie produkcja śrubek, których produkuje się tysiące w ciągu jednego dnia).W karcie x wykorzystuje się tzw. rozstęp kroczący wartości dwóch sąsiednich obserwacji w celu pomiaru zmienności procesu. Załóżmy, że zaobserwowaliśmy ciąg wartości x1, x2, …, xn. Rozstęp kroczący dla pary xi – 1, xi definiujemy jako MRi = |xi – xi – 1|. Przez oznaczamy średnią z wszystkich MRi: Parametry karty x:

Zauważmy, że 1,128 = d 2 dla n = 2. Wartość średnia może zostać zastąpiona wartością standardową lub pożądaną, jeśli jest taka potrzeba lub gdy wynika to z natury procesu. Innym sposobem skonstruowania karty x jest wykorzystanie odchylenia standardowego pomiarów. Wtedy parametry karty oblicza się ze wzorów:

gdzie

to średnia z wszystkich pomiarów, a s – odchylenie standardowe z próby.

40.4. Wykres czasu cyklu Wykres czasu cyklu (ang. cycle time chart) pozwala na określenie czasu, jaki zajmuje nam wykonanie określonych czynności. Jest on przydatny np. do określania tzw. prędkości zespołu w metodyce Scrum. Wykres ten może mieć wiele form. Często jest oparty na karcie kontrolnej jest przedstawiona na rysunku 40.5.

Inna forma wykresu czasu cyklu

Rysunek 40.5. Wykres czasu cyklu Wykres ten ma formę tzw. spiętrzonego histogramu (ang. stacked histogram). Każdy słupek histogramu reprezentuje jeden tydzień i jest złożony z czterech części, których wysokości odpowiadają czasom czterech czynności wykonywanych w danym tygodniu (wykonanie, implementacja, projekt i planowanie testów). Całkowita wysokość słupków określa czas cyklu testowania. Z rysunku widać, że czas ten w ciągu 13 tygodni zmniejszył się z ok. 39 do 26 godzin. Analizując wielkości poszczególnych części histogramu, możemy zobaczyć, które czynności miały na to największy wpływ. W naszym przykładzie widać, że najbardziej zmniejszył się czas na implementację testów, a czas na projektowanie nieznacznie wzrósł. Zmniejszył się również czas wykonania testów. Czas planowania utrzymuje się na mniej więcej stałym poziomie. Śledząc czas cyklu, możemy estymować czas przyszłych cykli, np. przez wykorzystanie średniej kroczącej (tzn. estymacja czasu przyszłego cyklu jest uśrednioną wartością n ostatnich cykli).

40.5. Narzędzia analizy i zapobiegania przyczynom źródłowym

Omawiając we wcześniejszych rozdziałach techniki pracy grupowej, zarządzanie ryzykiem oraz udoskonalanie procesu testowego przedstawiliśmy kilka narzędzi analizy przyczyn źródłowych. Były to: burza mózgów, Nominal Group Technique, diagram rybiej ości, drzewo usterek, analiza pola sił, FMEA, diagram Pareto, stratyfikacja, wykres rozproszony, diagram macierzowy, tabela kontyngencji i diagram relacji. W rozdziale opiszemy kilka innych technik służących analizie oraz zapobieganiu przyczynom źródłowym.

40.5.1. 5 pytań „dlaczego?” i diagram „why–why” Technika 5 pytań „dlaczego” to metoda podobna do diagramu rybiej ości. Zaczynamy od zdefiniowania problemu, a następnie pytamy o jego przyczynę („dlaczego problem nastąpił?”). Następie zadajemy to samo pytanie wobec przyczyny, otrzymując przyczynę przyczyny. Pytanie „dlaczego” zadajemy pięciokrotnie (lub mniej bądź więcej razy, w zależności od sytuacji). Jeśli na kolejne takie pytanie jest nam trudno odpowiedzieć, to ostatnia odpowiedź jest przyczyną źródłową. Przykładowo, niech naszym problemem będzie zbyt długi czas naprawy defektów. 1. Dlaczego czas naprawy jest zbyt długi? Bo wiele defektów nie jest poprawnie usuwanych za pierwszym razem. 2. Dlaczego wiele defektów nie jest poprawnie usuwanych za pierwszym razem? Bo specyfikacja wymagań dla obszarów dotyczących tych defektów jest niejasna lub jej brak. 3. Dlaczego specyfikacja jest niejasna lub jej brak? Bo nie wiadomo, kto ma to robić, w wyniku czego jest tworzona ad hoc. 4. Dlaczego nie wiadomo, kto ma specyfikować wymagania? Bo nie ma sformalizowanego procesu specyfikacji wymagań oraz ustanowionego właściciela tego procesu. Trudno byłoby udzielić sensownej (tzn. odkrywającej głębszą przyczynę) odpowiedzi na pytanie „dlaczego nie ma procesu specyfikacji wymagań?” (dlatego, że go nie zdefiniowaliśmy!). Dotarliśmy do przyczyny źródłowej, którą jest brak procesu specyfikacji wymagań. Zauważmy, że gdy popatrzymy na nasz podstawowy problem (zbyt długi czas usuwania defektów), nie jest to odpowiedź

trywialna. Dzięki metodzie „5 pytań dlaczego?” możemy znaleźć – często nieoczywistą – przyczynę źródłową naszych problemów. Jeśli może być więcej niż jedna odpowiedź na pytanie „dlaczego”, to odpowiedzi możemy organizować w formie tzw. diagramu why–why, czyli drzewa, w którym wszystkie dzieci danego węzła stanowią odpowiedzi na pytanie „dlaczego” odnoszące się do tego węzła. Niektóre przyczyny (węzły) mogą się powtarzać w strukturze drzewa – jest to zupełnie naturalne. Przykład takiego diagramu jest pokazany na rysunku 40.6. Wierzchołek oznaczony gwiazdką oznacza odpowiedź, która występuje już w innym miejscu drzewa. Jeśli w drzewie jeden wierzchołek występuje w wielu kopiach, może to oznaczać, że jest to bardzo istotna przyczyna wielu różnych innych zjawisk wspólnie wpływających na główny problem (umieszczony w korzeniu drzewa).

Rysunek 40.6. Diagram why–why

40.5.2. Macierz „jest–nie jest” Macierz jest–nie jest (ang. is–is not matrix) to narzędzie służące do identyfikacji wzorców w zaobserwowanych zjawiskach przez ich ustrukturalizowanie lub stratyfikację (klasyfikację). Używa się jej często jako techniki pracy zespołowej, gdy chcemy usystematyzować naszą wiedzę i pomysły związane

z dyskutowanym problemem. Przykład macierzy jest–nie jest pokazano na rysunku 40.7.

Rysunek 40.7. Macierz jest–nie jest

40.5.3. Kaizen Kaizen (patrz rys. 40.8) to złożenie dwóch japońskich słów: kai (zmiana) oraz zen (dobre), co można przełożyć jako „zmiana na lepsze”. Jest to metoda ciągłej, nieustającej poprawy procesu przez wprowadzanie drobnych udoskonaleń wszędzie tam, gdzie to można uczynić.

Rysunek 40.8. Kaizen W swych założeniach kaizen jest podobny do metody Lean (np. przez wymóg eliminowania straty) czy cyklu Deminga (ciągłe usprawnianie). Podstawą kaizen jest włączenie procesu myślowego na każdym procesie produkcji, co powoduje humanizację wytwarzania, będącą przeciwieństwem automatyzacji pozbawionej elementów myślenia i świadomego wykonywania pracy. Cele kaizen to: skracanie czasu produkcji bądź cyklu wytwórczego; dostosowywanie infrastruktury do rzeczywistych potrzeb; stworzenie kryteriów oceny i nagradzania; eliminacja zbędnych kosztów. Kaizen opiera się na dziesięciu podstawowych zasadach: 1. Pozbądź się wszystkich zastałych pomysłów na to, jak tworzyć produkt. 2. Nie wiń innych – traktuj innych tak, jak chciałbyś, by traktowano ciebie. 3. Myśl pozytywnie – nie mów, że czegoś nie da się zrobić. 4. Nie czekaj na doskonałość. Połowiczne i proste, ale szybkie udoskonalenie jest OK. 5. Naprawiaj błędy tak szybko, jak to możliwe.

6. Nie zastępuj myślenia pieniędzmi – kreatywność ponad kapitał. 7. Pytaj „dlaczego”, dopóki nie dojdziesz do przyczyny źródłowej. 8. Lepsza jest wiedza 5 ludzi, niż ekspertyza jednego. 9. Opieraj decyzje na danych, faktach i liczbach, nie na opiniach. 10. Ulepszenia nie mają końca i nie są wprowadzane w pokoju konferencyjnym, lecz w rzeczywistym środowisku pracy.

40.5.4. Poka yoke Poka yoke to technika zapobiegania defektom mogącym powstać w wyniku pomyłki. Polega na stworzeniu takich warunków, w których błąd nie może się zdarzyć lub jego popełnienie będzie natychmiast widoczne. Przykładem obiektu zaprojektowanego zgodnie z ideą poka yoke jest (przestarzała już) dyskietka, którą do stacji dysków można włożyć tylko na jeden sposób. Obiektem zaprojektowanym niezgodnie z poka yoke jest płyta CD, którą można włożyć do czytnika na dwa sposoby, z których tylko jeden jest poprawny. W inżynierii jakości metodami mniej lub bardziej wykorzystującymi poka yoke mogą być: wykorzystanie i użycie listy kontrolnej defektów na użytek inspekcji, dzięki czemu unika się przeoczenia określonych typów defektów; wykorzystanie narzędzi, które automatycznie i na bieżąco kontrolują zgodność tworzonego kodu ze standardami kodowania przyjętymi w organizacji; wykorzystanie formalnych technik projektowania testów, zapewniają dostarczenie przypadków testowych w pokrywających odpowiednie kryterium pokrycia.

które 100%

1 Wykres ten jest również znany pod nazwami: diagram Kiviata, wykres pajęczej sieci czy wykres gwiezdny. 2 PMI to akronim od słów Plus Minus Interesting, czyli plusy, minusy, rzeczy interesujące (konsekwencje, możliwe wyniki działań).

41. Metryki wielkości oprogramowania

Gdy mowa o pomiarach oprogramowania, pojęcie to najczęściej kojarzy się z mierzeniem jego wielkości, przez „fizyczny” (np. linie kodu) lub „funkcjonalny” (np. punkty funkcyjne) aspekt tej miary. Jest tak dlatego, gdyż miary wielkości oprogramowania są najczęściej używane przez inżynierów oprogramowania i informatyków. Pozostałe miary i modele (np. opisujące wzrost niezawodności czy efektywność usuwania defektów) są bardziej skomplikowane i wymagają często zaawansowanej wiedzy matematycznej i statystycznej. Pojęcie „rozmiaru oprogramowania” nie jest jednak tak oczywiste. Pytanie brzmi: jakie elementy składają się na ów rozmiar? Czy są to linie kodu źródłowego? Czy wielkość skompilowanego programu? Czy rozmiar uwzględnia wielkość dokumentacji użytkowej? Technicznej? Projektowej? Zanim rozpoczniemy program pomiarów, musimy odpowiedzieć sobie na te pytania. Zasadnicze pytanie brzmi: po co chcemy wykorzystywać miary wielkości oprogramowania. Powody mogą być różne, np.: aby móc mierzyć produktywność/szybkość zespołu (ile linii kodu zespół jest w stanie wytworzyć w ciągu określonego czasu); aby móc przewidywać jakość oprogramowania (wykorzystując zależności między wielkością oprogramowania a gęstością defektów); aby móc szacować rzeczywisty rozmiar oprogramowania, jego koszt, wysiłek oraz czas potrzebny do jego stworzenia (wykorzystując estymację metryk wielkości oprogramowania).

41.1. Metryki wolumenowe

W rozdziale tym zajmiemy się miarami definiującymi fizyczną wielkość oprogramowania i omówimy ich wady, zalety oraz zastosowania w inżynierii jakości. Miary te to: miary wielkości oprogramowania (linie kodu); miary wielkości dokumentacji.

41.1.1. LOC Metryka linii kodu określa zazwyczaj liczbę wykonywanych instrukcji. Ta definicja wynika z początków prac nad metrykami oprogramowania, gdy kod pisano w asemblerze, gdzie fizyczna linia kodu zawsze odpowiadała jednej instrukcji. Wraz z rozwojem języków drugiej, trzeciej i czwartej generacji sytuacja się skomplikowała. Obecnie, jeśli w projekcie wykorzystuje się metrykę LOC, należy bardzo precyzyjnie zdefiniować sposób jej liczenia. W szczególności należy uwzględnić następujące aspekty: czy jako jedną instrukcję liczy się fizyczną linijkę kodu, czy też instrukcję języka programowania (np. zakończoną średnikiem)? czy komentarz jest liczony jako linia kodu, czy też nie jest uwzględniany w zliczaniu? ile linii kodu stanowi komentarz złożony z kilku fizycznych linii? czy są zliczane instrukcje puste oraz puste wiersze kodu? czy są zliczane nagłówki funkcji i metod? czy są zliczane dyrektywy kompilatora1, linie oznaczające etykiety2, annotacje3 i inne niestandardowe typy poleceń? czy i w jaki sposób są zliczane nawiasy klamrowe bądź inne symbole oznaczające początek i koniec bloku instrukcji? Na podstawie metryki LOC można zdefiniować wiele innych metryk pochodnych, takich jak produktywność (LOC/jednostkę czasu), gęstość defektów (liczba błędów/LOC), dokładność dokumentacji (liczba stron dokumentacji/LOC) itp. W zastosowaniach praktycznych częściej niż LOC używa się metryki wyrażonej w tysiącach linii kodu, KLOC (ang. Kilo Lines Of Code). Metryka linii kodu ma sporo wad, na przykład:

nie istnieje jednolita, spójna definicja linii kodu; LOC nie ma związku z funkcjonalnością oprogramowania; używanie LOC łamie zasady ekonomiki oprogramowania (dyskusja na ten temat znajduje się w podrozdz. 24.3); rzeczywista wartość LOC zakończeniu kodowania.

znana

jest

dopiero

po

całkowitym

LOC ma jednak również zalety: prostota, zrozumiałość, łatwość nauki oraz łatwość dokonania pomiaru, gdy mamy określoną precyzyjnie definicję operacyjną metryki. Jednak chyba najważniejsza z nich jest związana z nieoczywistą zależnością między liczbą linii kodu a liczbą defektów. Intuicyjnie wydaje się, że im więcej linii kodu, tym więcej powinno być defektów. Badania empiryczne [351], [352] pokazują jednak, że zależność ta jest U-kształtna, tzn. liczba defektów rośnie zarówno wraz ze zmniejszaniem, jak i zwiększaniem się rozmiaru modułu (patrz rys. 1.1). Nowsze opracowania, np. [353] proponują, na podstawie danych empirycznych, następujący model dla tej zależności:

gdzie D to gęstość defektów (czyli ich liczba na KLOC), s – wielkość programu, natomiast a, b, c to stałe wyliczone na podstawie danych historycznych. Funkcja ta ma kształt taki, jak na rysunku 41.1. Minimum tej funkcji znajduje się w punkcie

dla którego (po podstawieniu s min do s) gęstość defektów przyjmuje wartość Mając taki model, dysponujemy ciekawą i cenną informacją: wiemy, jaki jest optymalny rozmiar modułu pod kątem gęstości defektów. Wiemy też, jak będzie zmieniać się gęstość defektów wraz ze zmianą rozmiaru modułu. Programiści powinni dążyć do tego, by wielkości modułów oscylowały wokół wartości s min. Jeśli z projektu jakiegoś modułu wynika, że może on znacznie odbiegać od tej liczby, to warto rozważyć podzielenie go na mniejsze części lub połączenie z innymi projektowanymi modułami o niewielkich rozmiarach.

Rysunek 41.1. Zależność gęstości defektów od rozmiaru programu Oczywiście model, a więc i optymalny rozmiar modułu, będzie zależeć od wielu różnych czynników, takich jak: użytego języka programowania; typu projektu; rodzaju środowiska produkcyjnego; umiejętności zespołu deweloperskiego. Aby wykorzystać ten model, należy zebrać empiryczne dane z możliwie jednorodnych projektów (pisanych w tym samym języku, przez ludzi o podobnych umiejętnościach, w podobnych środowiskach itp.). Wtedy powinien on dosyć dobrze przewidywać analogiczne projekty i może służyć do wyznaczenia optymalnej wielkości modułu. Skąd bierze się nieoczywista, U-kształtna zależność, zamiast intuicyjnej rosnącej funkcji liniowej? Zjawisko to można wytłumaczyć w następujący sposób. Załóżmy, że mamy rozmiar modułu o określonej gęstości defektów. Im moduł będzie większy, tym więcej będzie okazji do popełnienia błędów, zatem gęstość defektów wzrośnie. Jednocześnie, jeśli rozmiar modułu będzie coraz mniejszy, to – aby utrzymać podobną funkcjonalność – jego wewnętrzna struktura będzie coraz

bardziej skomplikowana, co również stwarza więcej okazji do popełnienia błędów.

41.1.2. Współczynnik produktywności języka Zauważyliśmy wcześniej, że metryka LOC w istotny sposób zależy od przyjętego języka programowania. Załóżmy, że mamy dobry model predykcji gęstości defektów w zależności od LOC dla programów pisanych w Javie. Tworzymy nowy projekt, podobnego typu, w tym samym zespole i w podobnym środowisku, co projekty użyte do konstrukcji modelu. Jedyna różnica jest taka, że piszemy go w języku C. Jak wykorzystać model opracowany dla Javy do predykcji gęstości defektów programu napisanego w C? W tym celu można posłużyć się tzw. tablicą produktywności języków. Podaje ona, ile (średnio) linii kodu źródłowego jest potrzebnych do oprogramowania jednego punktu funkcyjnego. O punktach funkcyjnych będziemy mówić w punkcie 41.2.1, ale tutaj tak naprawdę w ogóle nie są one nam potrzebne. Jeśli bowiem w języku X do oprogramowania 1 punktu funkcyjnego potrzeba x linii kodu, a w języku Y – y linii kodu, to stosunek produktywności języka X do Y jest jak x:y. Zatem 1 linia kodu języka X jest „równoważna” y/x liniom kodu źródłowego programu Y. Przykładowa tablica produktywności [354] dla

wybranych

języków

programowania jest przedstawiona w tabeli 41.1. Wróćmy do naszego przykładu z Javą i C. Załóżmy, że przewidujemy, iż program napisany w C będzie miał 600 linii kodu. Do predykcji gęstości defektów chcemy wykorzystać model zbudowany na podstawie programów pisanych w Javie. Musimy przeliczyć ile linii kodu liczyłby nasz program, gdyby był napisany w Javie. Produktywność C i Javy to odpowiednio 128 i 53 PF. Zatem jedna linia w C jest równoważna 53 PF/128 PF ≈ 0,41 linii w Javie. Równoważny program w Javie liczyłby więc 600 ⋅ 0,41 = 246 linii kodu. Tę wartość możemy już wstawić do naszego „Javovego” modelu i obliczyć gęstość defektów. Gęstość ta, dla programu napisanego w Javie będzie taka sama dla tego samego programu napisanego w C, ponieważ jest to miara defektów na liczbę instrukcji. Tabela 41.1. Tablica produktywności dla wybranych języków programowania

Język programowania

Średnia liczba linii kodu źródłowego na 1 PF

Język 1. g eneracji (ś rednia)

320

Język 2. g eneracji (ś rednia)

107

Język 3. g eneracji (ś rednia)

80

Język 4. g eneracji (ś rednia)

20

Język 5. g eneracji (ś rednia)

5

C

128

C++

53

FORTRAN 95

71

Has kell

38

JAV A

53

LIS P

64

Perl

21

PROLOG

64

S QL

13

41.1.3. Pomiar specyfikacji i projektu Miary wolumenowe można stosować nie tylko do kodu źródłowego, lecz także do dokumentacji projektowej. Dwoma typowymi metrykami dokumentacji są liczba stron oraz metryka liczby koniecznych wymagań (ang. shalls). Metryk wielkości dokumentacji można używać do szacowania rozmiaru projektu, choć, jak

zauważają Fenton i Pfleeger [355], korelacja taka będzie występowała w projektach o dużym stopniu zorganizowania, a więc zwykle w organizacjach o wysokim poziomie dojrzałości. Metryka liczby stron – jak sama nazwa wskazuje – zlicza liczbę stron dokumentacji. Możemy próbować korelować ją z liniami kodu napisanego na podstawie tej dokumentacji. Taka metryka nazywa się współczynnikiem ekspansji specyfikacji-do-kodu (ang. specification-to-code expansion ratio). Jeśli współczynnik korelacji jest duży, to metryki tej można używać do przewidywania rozmiaru przyszłych projektów na podstawie ich specyfikacji. Metryka liczby koniecznych wymagań zlicza te ostatnie przez wyszukiwanie w dokumencie fraz typu „system musi”. Każde zdanie rozpoczynające się od tych słów oznacza narzucenie obowiązkowego wymagania (w przeciwieństwie do wymagań typu „system może” czy „system powinien”). Metryka ta jest zwykle stosowana przez organizacje o wysokim stopniu dojrzałości (CMMI poziom 3 lub wyższy). Wymagania konieczne mogą być powiązane z projektem, kodem i przypadkami testowymi, co pozwala na kontrolę i badanie ich pokrycia. Również i tej metryki można używać do predykcji wielkości projektu czy też wysiłku (wyrażonego np. w osobomiesiącach na jedno wymaganie konieczne). Przykład. Z danych historycznych wiadomo, że jedno wymaganie konieczne zajmuje 1,3 osobomiesiąca, z odchyleniem standardowym równym 12%. Przystępujemy do tworzenia nowego projektu. Z analizy dokumentacji wynika, że metryka liczby koniecznych wymagań wynosi 140. Chcemy oszacować czas trwania projektu oraz nakład pracy potrzebny do zaimplementowania wymagań przy założeniu, że dysponujemy 6-osobowym zespołem. Zakładamy, że każdy z członków zespołu cechuje się taką samą efektywnością pracy. Jedno wymaganie średnio zajmuje 1,3 osobomiesiąca, zatem 140 wymagań wymaga nakładu 140 ⋅ 1,3 = 182 osobomiesięcy. 12% z 182 to ok. 22, zatem estymowany wysiłek to 182 ± 22 osobomiesiące. Gdybyśmy chcieli mieć większą pewność, co do tego, że prawdziwa wartość znajdzie się w podanym przez nas przedziale, możemy użyć dwóch odchyleń standardowych zamiast jednego. Wtedy oszacowana wartość to 182 ± 44 osobomiesięcy. Ponieważ mamy 6 osób w zespole, szacujemy, że projekt będzie trwał

miesięcy, a więc od 23 do 37 miesięcy.

41.2. Metryki funkcjonalności Miary wielkości oprogramowania skupiają się jedynie na fizycznej wielkości oprogramowania. Nie uwzględniają „semantycznej” strony kodu źródłowego, to znaczy funkcjonalności, jaką kod ma dostarczyć. Aby mierzyć oprogramowanie w odniesieniu do tego, co ma ono robić opracowano kilka metod, z których najpopularniejszą jest metoda punktów funkcyjnych. Pozostałe, bardzo podobne do metryki PF, to punkty cech, punkty obiektowe i rozszerzone punkty obiektowe. Istnieje również wiele innych miar funkcjonalności, takich jak liczba punktów przypadków użycia [356] czy punkty klas [357].

41.2.1. Punkty funkcyjne Punkty funkcyjne (PF, ang. Function Points, FP) zostały wprowadzone przez Albrechta w 1984 roku [358]. Obecnie jest to de facto standardowa metoda pomiaru wielkości oprogramowania. PF wykorzystuje się w innych metrykach złożonych oraz modelach, takich jak np. COCOMO II. PF mierzą wielkość oprogramowania z punktu widzenia użytkownika przez ilościowe ujęcie funkcjonalności, jaką mu ono dostarcza. Punkty funkcyjne są obliczane na podstawie projektu aplikacji, dlatego zaletą ich stosowania jest możliwość wczesnego ich użycia, jeszcze przez rozpoczęciem tworzenia kodu. Dzięki temu są one przydatne w szacowaniu czasu, kosztów i nakładu prac związanych z tworzeniem oprogramowania. Inną korzyścią jest to, że definicja PF jest ściśle określona, zatem metryka PF jest uznawana za metrykę odpowiednią i spójną4. Metoda PF jest rozwijana przez organizację International Function Point Users Group. Na stronie tej organizacji, www.ifpug.org, jest dostępnych wiele materiałów związanych z metryką punktów funkcyjnych. Procedurę obliczania PF przedstawiono na rysunku 41.2.

Rysunek 41.2. Procedura obliczania punktów funkcyjnych Na początku należy określić rodzaj obliczanych punktów funkcyjnych. Istnieją trzy rodzaje PF: dla projektu rozwoju oprogramowania (ang. development project FP count) – opisująca liczbę PF aplikacji podczas pierwszej instalacji przez użytkownika; dla projektu rozszerzania oprogramowania (ang. enhancement project FP count) – opisująca modyfikacje w stosunku do starej wersji oprogramowania, wynikłe z dodania, usunięcia bądź modyfikacji funkcjonalności; dla projektu aplikacji (ang. application project FP count) – opisująca liczbę PF w bieżącej wersji aplikacji; miara ta inicjalizowana jest po ukończeniu projektu rozwoju oprogramowania i modyfikowana przy każdym jego rozszerzeniu. Następnie określa się zakres granic systemu, aby oddzielić funkcje mierzonego oprogramowania od czynności wykonywanych przez użytkownika. Surową metrykę PF określa się na podstawie zliczania pięciu typów funkcji: dwa z nich dotyczą danych, a trzy – transakcji. Funkcje danych reprezentują funkcjonalność dostarczaną użytkownikowi w celu spełnienia zewnętrznych i wewnętrznych wymagań dotyczących danych. Funkcje transakcji reprezentują funkcjonalność dostarczaną użytkownikowi w celu przetwarzania danych. Pięć typów funkcji to: PW – pliki wewnętrzne (ang. internal logical files) – reprezentujące grupę logicznie powiązanych danych znajdujących się w ramach granic

systemu; PZ – pliki zewnętrzne (ang. external interface files) – reprezentujące grupę danych wykorzystywanych przez program, ale utrzymywanych poza nim, w ramach granic innego, zewnętrznego systemu; ZWe – zewnętrzne wejścia (ang. external inputs) – reprezentujące proces przetwarzania danych, które przychodzą do systemu z zewnątrz; podstawowy cel ZWe to utrzymywanie jednego lub kilku PW w celu kontroli zachowania systemu; ZWy – zewnętrzne wyjścia (ang. external outputs) – reprezentujące proces przesyłania danych z systemu na zewnątrz, poza jego granice; podstawowy cel ZWy to prezentowanie użytkownikowi informacji powstałej w wyniku przetwarzania danych lub modyfikującej PW, a nie tylko prostego pobierania danych z PW; ZZ – zewnętrzne zapytania (ang. external inquiry) – reprezentujące proces wysyłania danych poza granice systemu; podstawowy cel to prezentowanie użytkownikowi pobranych danych; dane te są tylko pobierane, a nie przetwarzane (np. za pomocą formuł matematycznych), tak jak zewnętrzne wyjścia. Wymienione typy funkcji są schematycznie pokazane na rysunku 41.3.

Rysunek 41.3. Punkty funkcyjne – pięć typów funkcji Każdy z czynników jest klasyfikowany do jednej z trzech grup: prosty, średni lub złożony. W zależności od złożoności jest mu przypisywana odpowiednia waga (patrz tab. 41.2).

Tabela 41.2. Wagi dla poszczególnych złożoności typów funkcji

Złożoność T yp funkcji

Niska

Średnia

Duża

Pliki wewnętrzne

7

10

15

Pliki zewnętrzne

5

7

10

Zewnętrzne wejś cia

3

4

6

Zewnętrzne wyjś cia

4

5

7

Zewnętrzne zapytania

3

4

6

Złożoność danych oblicza się na podstawie tzw. typów elementów danych DET (ang. Data Element Types) oraz typów elementów rekordów RET (ang. Record Element Types). Typ elementu danych to rozpoznawane przez użytkownika unikalne pole (np. pole tekstowe do wpisania loginu). Typ rekordu danych to rozpoznawana przez użytkownika unikalna grupa elementów danych wchodząca w skład pliku wewnętrznego lub zewnętrznego. Po obliczeniu dla każdego typu funkcji DET i RET przypisuje się mu złożoność tak, jak to przedstawiono w tabeli 41.3. Tabela 41.3. Obliczanie złożoności typu funkcji danych

DET

RET

1–19

20–50

> 50

1

nis ka

nis ka

ś rednia

2–5

nis ka

ś rednia

wys oka

>6

ś rednia

wys oka

wys oka

Złożoność transakcji oblicza się na podstawie DET oraz tzw. FTR, czyli odniesień do typów plików (ang. File Type Referenced, FTR). Odniesienie do typu pliku to plik wewnętrzny czytany lub przetwarzany przez jakąś transakcję lub plik

zewnętrzny czytany przez jakąś transakcję. W tabelach 41.4 i 41.5 przedstawiono złożoności odpowiednio plików/zapytań.

dla

wewnętrznych

plików

oraz

zewnętrznych

Tabela 41.4. Obliczanie złożoności typu transakcji dla plików wewnętrznych

DET

FTR

1–4

5–15

> 15

0–1

nis ka

nis ka

ś rednia

2

nis ka

ś rednia

wys oka

>2

ś rednia

wys oka

wys oka

Tabela 41.5. Obliczanie złożoności typu transakcji dla plików/zapytań zewnętrznych

DET

FTR

1–5

6–19

> 19

0–1

nis ka

nis ka

ś rednia

2–3

nis ka

ś rednia

wys oka

>3

ś rednia

wys oka

wys oka

Po określeniu liczby poszczególnych typów funkcji mnożymy je przez odpowiadające im wagi, otrzymując w ten sposób surową metrykę punktów funkcyjnych UFP (ang. Unadjusted Function Points):

gdzie t oznacza typ funkcji (PW, PZ, ZWe, ZWy lub ZZ), c – złożoność (niską, średnią lub dużą), ntc to liczba typów funkcji t o złożoności c, a w tc to waga odpowiadająca typowi t o złożoności c. Na przykład, jeśli mamy 4 pliki wewnętrzne o niskiej złożoności, 2 pliki wewnętrzne o dużej złożoności, 1

zewnętrzne wejście o średniej złożoności i 2 zewnętrzne zapytania o niskiej złożoności, surowa metryka punktów funkcyjnych wyniesie UFP = 4 ⋅ 7 + 2 ⋅ 15 + 1 ⋅ 4 + 2 ⋅ 3 = 28 + 30 + 4 + 6 = 68 Obliczenie czynnika korekty VAF polega na ocenieniu 14 charakterystyk systemu, które mogą wpływać na ogólną funkcjonalność aplikacji. Każda charakterystyka jest oceniania w skali od 0 do 5: 0 = brak charakterystyki lub brak wpływu; 1 = wpływ incydentalny; 2 = wpływ umiarkowany; 3 = wpływ przeciętny; 4 = wpływ znaczny; 5 = wpływ bardzo silny. Oto lista 14 charakterystyk: 1. Komunikacja danych – stopień, w jakim aplikacja komunikuje się bezpośrednio z procesorem; 0 = czyste przetwarzanie wsadowe, 5 = aplikacja jest czymś więcej niż front-endem i wspiera więcej niż jeden protokół komunikacyjny. 2. Rozproszone przetwarzanie danych – stopień, w jakim dane w aplikacji przepływają między jej komponentami. 3. Wydajność – stopień, w jakim na tworzenie aplikacji ma wpływ czas odpowiedzi i wydajność aplikacji. 4. Wykorzystanie konfiguracji – stopień, w jakim na tworzenie aplikacji mają wpływ ograniczenia zasobów. 5. Częstość transakcji – stopień, w jakim na tworzenie aplikacji ma wpływ częstość operacji biznesowych. 6. Wprowadzanie danych online – stopień, w jakim dane są wprowadzane przez interaktywne transakcje. 7. Efektywność użytkownika końcowego – stopień, w jakim w projekcie uwzględniono czynnik ludzki (użyteczność) oraz łatwość użycia aplikacji. 8. Aktualizowanie online – stopień, w jakim są aktualizowane online pliki wewnętrzne.

9. Złożone przetwarzanie – stopień, w jakim na tworzenie aplikacji ma wpływ logika przetwarzania (ang. processing logic). 10. Reużywalność – stopień, w jakim użyte do budowy oprogramowania komponenty były zaprojektowane, stworzone i wspierane w innych aplikacjach (0 = brak). 11. Łatwość instalacji. 12. Łatwość operacyjna – stopień, w jakim oprogramowanie ułatwia pracę przez eliminację konieczności ręcznego wykonywania operacji. 13. Wiele lokalizacji – stopień, w jakim aplikację zaprojektowano do pracy z wieloma środowiskami i dla wielu organizacji użytkowników. 14. Łatwość modyfikacji – opisuje łatwość wprowadzania zmian w logice aplikacji lub w strukturach danych. Wartość VAF jest wyliczana ze wzoru

gdzie w i jest wagą i-tej charakterystyki (czyli liczbą ze zbioru {0, 1, 2, 3, 4, 5}). VAF może przyjąć wartości od 0,65 do 1,35, co – jak wynika ze wzoru na ostateczną liczbę punktów funkcyjnych – pozwala zmodyfikować wartość metryki surowych punktów funkcyjnych o maksymalnie 35% w jedną lub drugą stronę. Ostateczna wartość metryki PF jest obliczana na podstawie metryki surowej FP oraz VAF według wzoru: PF = UFP ⋅ VAF Jak widać, pewną wadą metryki PF jest to, że sposób jej obliczania jest skomplikowany. Jej zrozumienie wymaga ponadto poświęcenia sporo czasu na przeczytanie dosyć obszernej dokumentacji na jej temat. Jednak dokładne przeprowadzenie (żmudnej) procedury obliczania PF może przynieść zysk w postaci dobrego przybliżenia rozmiaru tworzonego oprogramowania. analiza punktów funkcyjnych (ang. Function Point Analysis) – metoda pomiaru funkcjonalności systemu informatycznego; pomiar ten jest niezależny od technologii i może stanowić podstawę dla pomiaru produktywności, szacowania zasobów i kontroli projektu Przykład. Rozważmy system ELROJ z Dodatku A. W tabeli 41.6 są przedstawione wymagania na system, odpowiadające punktom funkcyjnym. Dla

każdego z nich określono typ funkcji oraz złożoność. Uwaga – ten przykład jest bardzo uproszczony. Wyczerpujący opis metody punktów funkcyjnych wraz z wieloma przykładami jej zastosowania Czytelnik znajdzie w [359]. Zidentyfikowaliśmy 7 zewnętrznych wejść, 1 zewnętrzne wyjście, 3 zewnętrzne zapytania, 1 plik wewnętrzny i 1 plik zewnętrzny. Wszystkie komponenty mają niską złożoność. Zatem surowa liczba punktów funkcyjnych wynosi UFP = 7 ⋅ 3 + 1 ⋅ 4 + 3 ⋅ 3 + 1 ⋅ 7 + 1 ⋅ 5 = 21 + 4 + 9 + 7 + 5 = 46 Tabela 41.6. Punkty funkcyjne dla systemu ELROJ

W ymaganie

Punkt funkcyjny

Złożoność

Zdefiniowanie lub us unięcie wyś wietlaneg o komunikatu

ZWe

nis ka

Dodanie nowej linii autobus owej

ZWe

nis ka

Dodanie g odziny kurs u dla danej linii

ZWe

nis ka

Dodanie więks zej liczby kurs ów dla danej linii

ZWe

nis ka

Us unięcie kurs u dla danej linii

ZWe

nis ka

Wyś wietlenie na ekranie informacji o kurs ach

ZWy

nis ka

Us tawienie daty i czas u

ZWe

nis ka

Wyś wietlenie lis ty zdefiniowanych linii

ZZ

nis ka

Wyś wietlenie lis ty zdefiniowanych kurs ów dla danej linii

ZZ

nis ka

Baza danych

PW

nis ka

Konfig uracja ekranów zewnętrznych

PZ

nis ka

Interfejs do obs ług i komunikacji aplikacjaekran

ZWe ZZ

nis ka nis ka

Liczbę komponentów poszczególnych typów przemnożyliśmy przez wagi odpowiadające ich złożoności (patrz tab. 41.2). Następny krok to określenie wag dla 14 charakterystyk programu ELROJ: 1. Komunikacja danych – 3. 2. Rozproszone przetwarzanie danych – 0. 3. Wydajność – 1. 4. Wykorzystanie konfiguracji – 1. 5. Częstość transakcji – 1. 6. Wprowadzanie danych online – 2. 7. Efektywność użytkownika końcowego – 3. 8. Aktualizowanie online – 2. 9. Złożone przetwarzanie – 2. 10. Reużywalność – 0. 11. Łatwość instalacji – 1. 12. Łatwość operacyjna – 3. 13. Wiele lokalizacji – 0. 14. Łatwość modyfikacji – 2. Suma tych wag to 3 + 0 + 1 + 1 + 1 + 2 + 3 + 2 + 2 + 0 + 1 + 3 + 0 + 2 = 21, zatem VAF = 0,01 ⋅ 21 + 0,65 = 0,86. Ostatecznie, liczba punktów funkcyjnych dla systemu ELROJ wynosi FP = UFP ⋅ VAF = 46 ⋅ 0,86 ≈ 40. Jeśli znamy efektywność pracy zespołu, to możemy obliczyć czasochłonność prac. Na przykład, jeśli zespół jest w stanie oprogramować 3 punkty funkcyjne tygodniowo, to całkowity czas tworzenia systemu wyniesie ok. 40/3 ≈ 13 tygodni.

41.2.2. Punkty obiektowe i rozszerzone punkty obiektowe Koncepcja punktów obiektowych jako miary wielkości oprogramowania została wprowadzona przez Bankera, Kauffmana i Kumara [360]. Metryka ta może być

stosowana dla programów zorientowanych obiektowo, a więc pisanych w językach 4. generacji (4GL). Opiera się na zliczeniu czterech typów obiektów: zbiory reguł – zestaw instrukcji pozwalający na zaimplementowanie najczęściej wymaganych funkcji; zbiory reguł mają ograniczoną elastyczność, jeśli chodzi o implementacje funkcjonalności; moduły 3GL – napisane w języku 3. generacji, zwykle stosowane dla bardziej skomplikowanych funkcji; ekrany – logiczna reprezentacja zawartości ekranu (np. formularz); raporty użytkownika. Idea metody opiera się na tym, że znajomość liczby poszczególnych typów obiektów pozwala na predykcję funkcjonalności oraz rozmiaru tworzonej aplikacji. Wprowadza się dwie metryki, ObjCount (czyli liczba obiektów) oraz OP (czyli punkty obiektowe):

gdzie: ObjInsti to liczba obiektów o typie i (reguły, moduły, ekrany, raporty), a OEF i (ang. Object Effort Weight) to średni wysiłek deweloperski związany z obiektem typu i, oparty na szacunkach kierownika projektu. Rozszerzone punkty obiektowe to modyfikacja powyższego podejścia, w którym zamiast pojedynczej, jednowymiarowej metryki wprowadza się zestaw wielowymiarowych metryk wielkości oprogramowania [361]. Powodem wprowadzenia wielu metryk jest to, że projekty różnego typu w odmienny sposób używają pojęcia wielkości oprogramowania. Na przykład, w projektach systemów typu mainframe artefaktami procesu produkcji są zwykle takie obiekty jak ekrany, okna dialogowe, programy wsadowe, iterfejsy, raporty. W projektach systemów typu klient–serwer bardziej niż na ekrany zwraca się uwagę na serwisy danych itd. Metryka rozszerzonych punktów obiektowych nie jest zbyt popularna i raczej się jej nie spotyka w rzeczywistych zastosowaniach.

41.2.3. Punkty cech Punkty funkcyjne oryginalnie zostały wprowadzone przez Software Productivity Research (www.spr.com/feature-points.html) na potrzeby szacowania

parametrów projektów typu MIS (ang. Management Information System), czyli systemów zarządzania informacją. Punkty cech (PC) zostały zaproponowane jako rozszerzenie tego podejścia dla projektów silnie opartych na algorytmice (np. systemy komunikacji, militarne itp.). Technika obliczania PC jest podobna do techniki dla FP. Polega na zliczeniu sześciu typów parametrów: liczby algorytmów (z wagą 3); liczby wejść (z wagą 4); liczby wyjść (z wagą 5); liczby zapytań (z wagą 4); liczby plików danych (z wagą 7); liczby interfejsów (z wagą 7). Z sumy liczb poszczególnych typów przemnożonych przez odpowiednie wagi otrzymujemy surowy wskaźnik punktów cech. Tak jak w przypadku punktów funkcyjnych, oblicza się dodatkowo specjalny współczynnik dostosowania związany ze złożonością projektu i na podstawie tych dwóch wyników wylicza się końcową liczbę punktów cech. Metryka punktów cech, tak jak metryka rozszerzonych punktów obiektowych, nie znalazła szerszego zastosowania w praktyce.

41.2.4. Punkty przypadków użycia Filozofia metody punktów przypadków użycia jest bardzo podobna do tej dotyczącej punktów funkcyjnych. Różnica jest taka, że w tej pierwszej technice wielkość oprogramowania szacujemy na podstawie informacji o przypadkach użycia5. Punkty przypadków użycia (ang. Use Case Points, UCP) oblicza się ze wzoru UCP = (UUCW + UAW) ⋅ TCF ⋅ ECF gdzie: UUCW to tzw. surowe wagi przypadków użycia (ang. Unadjusted Use Case Weights); UAW to tzw. surowe wagi aktorów (ang. Unadjusted Actor Weights);

TCF to tzw. czynnik złożoności technicznej (ang. Technical Complexity Factor); ECF to tzw. czynnik złożoności środowiskowej (ang. Environmental Complexity Factor). UUCW odnosi się do liczby i złożoności samych przypadków użycia. Definiuje się trzy rodzaje złożoności (ze względu na liczbę transakcji, z jakich przypadek się składa): proste (1–3 transakcji) – waga 5; średnie (4–7 transakcji) – waga 10; złożone (8 lub więcej transakcji) – waga 15. UUCW = 5nP + 10nS + 15nZ gdzie nP, nS oraz nZ to liczba odpowiednio przypadków prostych, średnich i złożonych. Drugim czynnikiem wpływającym na liczbę punktów przypadków testowych są aktorzy. Istnieją trzy typy aktorów: prosty – zewnętrzny system wchodzący w interakcję za pomocą dobrze określonego API – waga 1; średni – zewnętrzny system wchodzący w interakcję za pomocą standardowych protokołów komunikacji (np. TCP/IP, HTTP, FTP) – waga 2; złożony – człowiek wykorzystujący GUI – waga 3. UAW = 1 ⋅ a P + 2 ⋅ a S + 3 ⋅ a Z gdzie a P, a S, a Z to odpowiednio liczba aktorów prostych, średnich i złożonych. Czynnik złożoności technicznej TCF oblicza się ze wzoru TCF = 0,6 + (TF/100) gdzie TF powstaje przez przypisanie każdemu z 13 zdefiniowanych czynników technicznych wagi (od 0 do 5) i pomnożeniu jej przez wagę czynnika. Czynniki (i ich wagi) są następujące: 1. System rozproszony (waga 2).

2. Czas odpowiedzi/wydajność (waga 1). 3. Efektywność użytkownika końcowego (waga 1). 4. Złożoność przetwarzania wewnętrznego (waga 1). 5. Reużywalność kodu (waga 1). 6. Łatwość instalacji (waga 0,5). 7. Łatwość użycia (waga 0,5). 8. Przenaszalność (waga 2). 9. Pielęgnowalność (waga 1). 10. Przetwarzanie równoległe/współbieżne (waga 1). 11. Bezpieczeństwo (waga 1). 12. Dostęp dla strony trzeciej (waga 1). 13. Szkolenia użytkownika końcowego (waga 1). Czynnik złożoności środowiskowej ECF oblicza się ze wzoru ECF = 1,4 + (–0,03 ⋅ EF) gdzie EF powstaje przez przypisanie każdemu z 8 zdefiniowanych czynników środowiskowych wagi (od 0 do 5) i pomnożeniu jej przez wagę czynnika. Czynniki (i ich wagi) są następujące: 1. Znajomość wykorzystywanego procesu rozwoju oprogramowania (waga 1,5). 2. Doświadczenie w obszarze aplikacji (waga 0,5). 3. Doświadczenie zespołu w technologiach orientowanych obiektowo (waga 1). 4. Umiejętności głównego analityka (waga 0,5). 5. Motywacja zespołu (waga 1). 6. Stabilność (niezmienność) wymagań (waga 2). 7. Personel zatrudniony na część etatu (waga –1). 8. Trudność języka programowania (waga –1). Ostateczna wartość punktów przypadków użycia jest obliczana ze wzoru UCP = (UUCW + UAW) ⋅ TCF ⋅ ECF

1 Dyrektywa kompilatora to zawarte w tekście kodu źródłowego programu polecenie dla translatora. Formalnie nie zalicza się ich jako części języka, lecz jego standardu lub konkretnej implementacji. Przykładem dyrektywy kompilatora jest np. instrukcja DEFINE w języku C. 2 Oznaczają one miejsca, do których program może przeskoczyć wykonując instrukcję goto. 3 Przykładem annotacji może być polecenie @Before lub @After dla JUnita w języku Java. 4 Należy jednak podkreślić, że istnieje kilka odmian metryki punktów funkcyjnych, np. PF zdefiniowane przez IFPUG, tzw. COSMIC FP, MK II FP, NESMA FP, ObjectOriented FP, Weighted Micro Function Points. W książce odwołujemy się do tej pierwszej, zdefiniowanej przez IFPUG (www.ifpug.org). 5 Więcej informacji o przypadkach użycia Czytelnik znajdzie w podrozdziale 8.10, gdzie były one wykorzystywane do tworzenia testów techniką czarnoskrzynkową.

42. Metryki charakterystyk jakościowych

Normy ISO 9126-2, ISO 9126-3 i 9126-4 dostarczają bogaty zbiór prawie 200 metryk dla charakterystyk jakościowych, które omówiliśmy w rozdziałach 15, 16 i 17, w tym 112 metryk dla jakości zewnętrznej, 70 metryk dla jakości wewnętrznej oraz 15 metryk dla jakości w użyciu. Poniżej przedstawiamy tylko przykładowe metryki.

42.1. Metryki dla funkcjonalności Funkcjonalna odpowiedniość. Metryka zewnętrznej odpowiedniości. Mierzy odpowiedniość ocenianych funkcji. M = 1 – A/B, gdzie A to liczba funkcji, w których wykryto problemy, B to liczba wszystkich zbadanych funkcji. Obliczana na podstawie specyfikacji wymagań i raportu oceny. M ∈ [0, 1]. Im bliższa 1, tym lepiej. Poprawność implementacji funkcjonalności. Metryka zewnętrznej odpowiedniości. Mierzy kompletność implementacji funkcjonalności. M = 1 – A/B, gdzie A to liczba brakujących funkcji, a B – liczba funkcji opisanych w specyfikacji. Obliczana na podstawie specyfikacji wymagań i raportu oceny. M ∈ [0, 1]. Im bliższa 1, tym lepiej. Stabilność specyfikacji funkcjonalnej. Metryka wewnętrznej odpowiedniości. Mierzy stabilność specyfikacji funkcjonalnej podczas cyklu życia oprogramowania. M = 1 – A/B, gdzie A jest liczbą funkcji zmienionych podczas cyklu życia, a B to całkowita liczba funkcji opisanych w specyfikacji wymagań. Obliczana na podstawie specyfikacji wymagań i raportów z przeglądów. M ∈ [0, 1]. Im bliżej 1, tym lepiej, tzn. tym lepsza stabilność wymagań.

Precyzja. Metryka zewnętrznej dokładności. Mierzy, jak często użytkownik otrzymuje wyniki z niewystarczającą dokładnością. M = A/T, gdzie A to liczba zwróconych użytkownikowi wyników z niewystarczającym poziomem precyzji, a T to czas działania programu. Obliczana na podstawie specyfikacji wymagań i raportu z testów. M ≥ 0. Im bliższa 0, tym lepiej. Wymiana danych. Metryka zewnętrznego współdziałania oparta na formacie danych. Mierzy jakość implementacji interfejsów wymiany danych. M = A/B, gdzie A to liczba formatów danych, które zostały wymienione z innym oprogramowaniem lub systemem podczas testów wymiany danych, a B jest całkowitą liczbą formatów danych branych pod uwagę. Obliczana na podstawie specyfikacji wymagań (lub dokumentacji użytkowej) oraz raportu z testów. M ∈ [0, 1]. Im bliższa 1, tym lepiej. Kontrola dostępu. Metryka zewnętrznego zabezpieczenia. Mierzy poziom kontroli dostępu do systemu. M = A/B, gdzie A to liczba wykrytych niedozwolonych operacji, a B jest liczbą typów niedozwolonych operacji określonych w specyfikacji. Obliczana na podstawie specyfikacji testów, raportu z testów i raportu z operacji (tzn. z wykonywania programu). M ∈ [0, 1]. Im bliżej 1, tym lepiej. Szyfrowanie. Metryka wewnętrznego zabezpieczenia. Mierzy stopień kompletności szyfrowania danych. M = A/B, gdzie A to liczba zaimplementowanych instancji szyfrowanych elementów danych potwierdzona przez przeglądy, a B jest całkowitą liczbą elementów danych wymagających szyfrowania według specyfikacji. Obliczana na podstawie specyfikacji wymagań, projektu, kodu źródłowego oraz raportu z przeglądu. M ∈ [0, 1]. Im bliżej 1, tym lepiej.

42.2. Metryki dla niezawodności Gęstość awarii na liczbę testów. Metryka zewnętrznej dojrzałości. Mierzy liczbę awarii wykrytych w określonym czasie na liczbę wykonanych testów. M = A/B, gdzie A jest liczbą wykrytych awarii, a B liczbą wykonanych testów. Obliczana na podstawie raportu z testów, raportu z wykonania programu i raportów o incydentach. M ≥ 0. We wczesnych fazach testowania im wyższa wartość M, tym lepiej. W późnych fazach testowania i w fazie operacji im niższa wartość M, tym lepiej. Zalecane jest monitorowanie wartości metryki w czasie.

Dojrzałość testów. Metryka zewnętrznej dojrzałości. Mierzy, jak dobrze produkt jest testowany. M = A/B, gdzie A to liczba zaliczonych przypadków testowych, a B – liczba wszystkich przypadków testowych, jakie mają być wykonane w celu pokrycia wymagań. Obliczana na podstawie specyfikacji wymagań, specyfikacji testów lub dokumentacji użytkowej, raportu z testów i raportu z operacji. M ∈ [0, 1]. Im bliżej 1, tym lepiej. Wykrywanie defektów. Metryka wewnętrznej dojrzałości. Mierzy liczbę wykrytych podczas przeglądów defektów. M = A/B, gdzie A to liczba wykrytych podczas przeglądów defektów, a B to szacowana liczba defektów, które mogą być wykryte podczas przeglądów. Do szacowania wykorzystuje się dane historyczne lub odpowiedni model. Obliczana na podstawie raportów z przeglądów (wartość A) oraz z danych historycznych (wartość B). M ≥ 0. Wysokie wartości M oznaczają wyższy poziom jakości produktu, ale gdy A = 0, niekoniecznie oznacza to, że produkt jest wolny od błędów. Unikanie załamania oprogramowania (ang. breakdown avoidance). Metryka zewnętrznej odporności na błędy. Mierzy częstość, z jaką wystąpienie awarii powoduje załamanie oprogramowania. M = 1 – A/B, gdzie A to liczba załamań, a B – liczba awarii. Obliczana na podstawie raportu z testów i raportu z operacji. M ∈ [0, 1]. Im bliżej 1, tym lepiej. Dostępność. Metryka zewnętrznej odtwarzalności. Mierzy dostępność systemu w określonym przedziale czasowym. M = MTTF/(MTTF + MTTR), gdzie MTTF to średni czas do awarii, MTTR to średni czas naprawy. Obliczana na podstawie raportu z testów i raportu z operacji. M ∈ [0, 1]. Im bliżej 1, tym lepiej. Odtwarzalność. Metryka wewnętrznej odtwarzalności. Mierzy odsetek zaimplementowanych wymagań związanych z odtwarzalnością. M = A/B, gdzie A to liczba zaimplementowanych wymagań związanych z odtwarzalnością, a B to całkowita liczba wymagań związanych z odtwarzalnością występująca w specyfikacji. Obliczana na podstawie przeglądów dokumentów (wartość A) oraz wymagań lub projektu (wartość B). M ∈ [0, 1]. Im bliżej 1, tym lepsza odtwarzalność.

42.3. Metryki dla użyteczności Jawność funkcji. Metryka zewnętrznej zrozumiałości. Mierzy frakcję funkcji, jakie użytkownik może zidentyfikować podczas pracy z programem. M = A/B, gdzie A to

liczba zidentyfikowanych przez użytkownika funkcji, a B to całkowita liczba funkcji w programie. Mierzona na podstawie wywiadów/kwestionariuszy, dokumentacji użytkowej i raportów z testów bądź operacji. M ∈ [0, 1]. Im bliżej 1, tym lepiej. Zupełność opisu. Metryka wewnętrznej zrozumiałości. Mierzy odsetek funkcji opisanych w opisie produktu. M = A/B, gdzie A jest liczbą funkcji (lub typów funkcji) opisanych w opisie produktu, a B to całkowita liczba funkcji (lub typów funkcji). Obliczana na podstawie specyfikacji wymagań, projektu i raportów z przeglądów. M ∈ [0, 1]. Im bliżej 1, tym opis bardziej kompletny. Łatwość nauki funkcji. Metryka zewnętrznej łatwości nauki. Mierzy czas potrzebny użytkownikowi na naukę używania funkcji. M = T, gdzie T jest czasem nauki. Obliczana na podstawie raportu z operacji i obserwacji użytkownika. M > 0. Im mniejsza, tym lepiej. Dostępność pomocy. Metryka zewnętrznej łatwości nauki. Mierzy odsetek tematów pomocy, które użytkownik może zlokalizować. M = A/B, gdzie A jest liczbą zadań, dla których istnieje poprawny temat pomocy (online help), a B – całkowitą liczbą przetestowanych zadań. Obliczana na podstawie raportu z operacji i obserwacji użytkownika. M ∈ [0, 1]. Im bliżej 1, tym lepiej. Kompletność dokumentacji użytkowej/pomocy systemowej. Metryka wewnętrznej łatwości nauki. Mierzy proporcję funkcji, jakie są opisane w pomocy bądź dokumentacji użytkowej. M = A/B, gdzie A to liczba opisanych funkcji, a B – liczba wszystkich dostarczonych funkcji. Obliczana na podstawie specyfikacji wymagań, projektu i raportu z przeglądów. M ∈ [0, 1]. Im bliżej 1, tym dokumentacja bardziej kompletna. Poprawa błędów. Metryka zewnętrznej łatwości użycia. Mierzy czas, w jakim użytkownik potrafi poprawić popełniony przez siebie błąd. M = T1 – T2, gdzie T1 to czas ukończenia naprawy błędu, a T2 to czas rozpoczęcia naprawy. Obliczana na

podstawie raportu z operacji i obserwacji użytkownika. M > 0. Im mniejsza, tym lepiej. Sprawdzanie walidacji wejścia. Metryka wewnętrznej łatwości użycia. Mierzy proporcję elementów wejścia, dla których jest sprawdzana poprawność danych. M = A/B, gdzie A to liczba elementów wejścia, dla których poprawność wprowadzanych danych jest sprawdzana, a B to całkowita liczba elementów wejścia, dla których poprawność wprowadzanych danych mogłaby być

sprawdzana. Obliczana na podstawie specyfikacji wymagań, projektu i raportów z przeglądów. M ∈ [0, 1]. Im bliżej 1, tym lepiej. Atrakcyjność

interakcji.

Metryka

zewnętrznej

atrakcyjności.

Mierzy

atrakcyjność interfejsu dla użytkownika. Mierzona za pomocą kwestionariusza, po użyciu oprogramowania. Obliczana na podstawie kwestionariusza. Interpretacja zależy pomiarowych.

od

budowy

kwestionariusza

i

zastosowanych

skal

42.4. Metryki dla wydajności Czas odpowiedzi. Metryka zewnętrznego zachowania w czasie. Mierzy czas potrzebny do ukończenia zadania. M = T1 – T2, gdzie T1 jest czasem ukończenia zadania, a T2 czasem ukończenia wydawania polecenia wykonania zadania. Obliczana na podstawie raportu z testów oraz raportu z operacji uwzględniającego czas działania (ang. elapse time). M > 0. Im mniejsza, tym lepiej. Czas oczekiwania. Metryka zewnętrznego zachowania w czasie. Mierzy, jaka część czasu użytkowania oprogramowania jest spędzana na oczekiwaniu. M = T1/T2, gdzie T1 to całkowity czas spędzony na oczekiwaniu, a T2 – całkowity czas wykonania zadania. Obliczana na podstawie raportu z testów oraz raportu z operacji uwzględniającego czas działania. M ≥ 0. Im mniejsza, tym lepiej. Przepustowość. Metryka wewnętrznego zachowania w czasie. Mierzy ilość zadań, które mogą być wykonane w jednostce czasu. M = A, gdzie A jest liczbą wykonanych zadań w ustalonej jednostce czasu (np. 1 godzinie). Obliczana na podstawie wiedzy o systemie operacyjnym oraz szacowanym czasie wywołań systemowych. M ≥ 0. Im wyższa, tym lepiej. Maksymalne zużycie pamięci. Metryka zewnętrznego zużycia zasobów. Mierzy limit pamięci potrzebnej do wykonania funkcji. M = max(A i)/Rmax, gdzie A i jest liczbą komunikatów o błędach pamięci dla i-tej ewaluacji, a Rmax jest wymaganą maksymalną liczbą komunikatów o błędach pamięci. Obliczana przez emulację warunków maksymalnego obciążenia. Obliczana na podstawie raportu z testów i raportu z operacji uwzględniającego czas działania. M ≥ 0. Im mniejsza, tym lepiej. Zużycie pamięci. Metryka wewnętrznego zużycia zasobów. Mierzy wielkość pamięci zajmowaną przez system w celu wykonania określonego zadania. M = A, gdzie A jest rozmiarem pamięci w bajtach, obliczonym lub szacowanym.

Obliczana na podstawie informacji o zużyciu pamięci. M ≥ 0. Im mniejsza, tym lepiej.

42.5. Metryki dla pielęgnowalności Zdolność do analizy awarii. Metryka zewnętrznej możliwości analizy. Mierzy stopień, w jakim użytkownik może zidentyfikować przyczynę awarii. M = 1 – A/B, gdzie A to liczba awarii, których przyczyna jest wciąż nieznana, a B – liczba wszystkich zaobserwowanych awarii. Obliczana na podstawie raportu z rozwiązania problemu (ang. problem resolution report) i raportu z operacji. M ∈ [0, 1]. Im bliżej 1, tym lepiej. Efektywność cyklu zmiany. Metryka zewnętrznej łatwości zmian. Mierzy stopień spełnienia wymagań użytkownika dotyczących czasu naprawy zgłaszanych

problemów.

to

czas

dostarczenia

użytkownikowi naprawionej wersji na skutek i-tego zgłoszenia,

to czas ukończenia wysyłania przez użytkownika i-tego zgłoszenia problemu, a N to całkowita liczba analizowanych zgłoszeń. Obliczana na podstawie raportu z rozwiązania problemu, raportu pielęgnowalności i raportu z operacji. M > 0. Im mniejsza, tym lepiej. Wskaźnik sukcesu zmian. Metryka zewnętrznej stabilności. Mierzy stopień, w jakim użytkownik jest w stanie użytkować system po wprowadzeniu zmian. M = N/T, gdzie N jest liczbą przypadków, w których użytkownik doświadcza awarii, użytkując zmienione oprogramowanie, a T jest czasem użytkowania zmienionego oprogramowania. Można również mierzyć fluktuację zmian wskaźnika przed naprawą i po naprawie: M 2 = (Npo /Tpo )/(Nprzed /Tprzed ), gdzie Npo = N, Tpo = T,

Nprzed to liczba przypadków doświadczenia awarii podczas użytkowania oprogramowania przed wprowadzonymi zmianami, a Tprzed – czas użytkowania oprogramowania przed wprowadzeniem zmian. Obliczana na podstawie raportu z rozwiązania problemu, raportu pielęgnowalności i raportu z operacji. M, M 2 ≥ 0. Im bliżej 0, tym lepiej. Wpływ modyfikacji. Metryka wewnętrznej stabilności. Mierzy wpływ modyfikacji na oprogramowania. M = A/B, gdzie A jest liczbą zmiennych objętych wpływem na skutek zmian, potwierdzona podczas przeglądów, a B to całkowita liczba zmiennych. Obliczana na podstawie raportów z przeglądów. M ∈ [0, 1]. Im bliżej 0, tym mniejszy wpływ modyfikacji.

Efektywność retestów. Metryka zewnętrznej testowalności. Mierzy łatwość przeprowadzania testów operacyjnych przez użytkownika i osobę zajmującą się utrzymaniem systemu. gdzie Ti jest czasem spędzonym na testowaniu, czy i-ta zgłoszona awaria została rozwiązana czy nie, a N jest liczbą rozwiązanych awarii. Obliczana na podstawie raportu z rozwiązania problemu i raportu z operacji. M > 0. Im mniejsza, tym lepiej. Autonomia testowalności. Metryka

wewnętrznej testowalności.

Mierzy

stopień niezależności testowania oprogramowania. M = A/B, gdzie A jest liczbą zależności testowania od innych systemów, które są symulowane przez namiastki, a B to całkowita liczba zależności testowych od innych systemów. Obliczana na podstawie raportu z przeglądu (wartość A) oraz wymagań lub projektu (wartość B). M ∈ [0, 1]. Im bliżej 1, tym lepiej.

42.6. Metryki dla przenaszalności Przenaszalność cech przyjaznych dla użytkownika. Metryka

zewnętrznej

zdolności do adaptacji. Mierzy czas, w jakim użytkownik adaptuje oprogramowanie do nowego środowiska. M = T, gdzie T jest całkowitym czasem spędzonym przez użytkownika na adaptacji oprogramowania do nowego środowiska, gdy użytkownik próbuje zainstalować program lub zmienić jego ustawienia. Zamiast czasu może być wykorzystana miara wysiłku, np. osobogodziny. Obliczana na podstawie raportu z rozwiązania problemu i raportu z operacji. M > 0. Im mniejsza, tym lepiej. Łatwość instalacji. Metryka zewnętrznej łatwości instalacji. Mierzy łatwość, z jaką użytkownik instaluje oprogramowanie. M = A/B, gdzie A jest liczbą przypadków, w których użytkownik z powodzeniem zainstalował system, a B – całkowitą liczbą przypadków instalacji. Obliczana na podstawie raportu z rozwiązania problemu i raportu z operacji. M ∈ [0, 1]. Im bliższa 1, tym lepiej. Wysiłek instalacji. Metryka wewnętrznej łatwości instalacji. Mierzy wysiłek potrzebny do zainstalowania oprogramowania. M = A/B, gdzie A jest liczbą zautomatyzowanych kroków procesu instalacji potwierdzoną podczas przeglądu, a B to całkowita wymagana liczba kroków instalacji. Obliczana na podstawie raportu z przeglądu. M ∈ [0, 1]. Im bliżej 1, tym lepiej. Możliwość współdziałania. Metryka zewnętrznego współdziałania. Mierzy częstość, z jaką użytkownik doświadcza problemów (awarii) na skutek

użytkowania produktu równolegle z innym oprogramowaniem. M = A/T, gdzie A jest liczbą nieoczekiwanych problemów bądź awarii występujących podczas równoległego użytkowania innego oprogramowania, a T to całkowity czas, w którym użytkowano równolegle inne oprogramowanie. Obliczana na podstawie raportu z rozwiązania problemu i raportu z operacji. M ≥ 0. Im bliższa 0, tym lepiej. Zawartość funkcji (ang. function inclusiveness). Metryka zewnętrznej zastępowalności. Mierzy łatwość używania systemu po migracji lub nowego oprogramowania zastępującego stare. M = A/B, gdzie A jest liczbą funkcji zwracających podobne wyniki jak poprzednio i gdzie zmiany nie były wymagane, a B to liczba przetestowanych funkcji podobnych do mających być zamienionymi funkcji dostarczanych przez inne oprogramowanie. Obliczana na podstawie raportu z rozwiązania problemu i raportu z operacji. M ∈ [0, 1]. Im bliższa 1, tym lepiej.

43. Metryki złożoności oprogramowania

Miary złożoności opisują, jak skomplikowane jest tworzone oprogramowanie (np. jego wewnętrzna struktura). Mogą być wykorzystane do przewidywania gęstości defektów. Do miar złożoności zalicza się niekiedy także metryki LOC czy PF.

43.1. Metryki Halsteada Halstead [362] proponuje „leksykograficzne” podejście do procesu tworzenia oprogramowania (jest ono znane pod nazwą nauki o programach Halsteada). Sprowadza ten proces do wyboru i zestawienia ze sobą określonej liczby elementów składniowych rozpoznawanych i przetwarzanych przez kompilator. Jednostki te, zwane leksemami, można podzielić na dwie grupy: operatory (funkcje) i operandy (argumenty operatorów). Metoda Halsteada operuje czterema podstawowymi metrykami, na których jest zbudowana cała teoria. Są to: liczba różnych (unikatowych) operatorów w programie, n1; liczba różnych (unikatowych) operandów w programie, n2; liczba wszystkich operatorów w programie, N1; liczba wszystkich operandów w programie, N2.

Na podstawie tych czterech miar definiuje się pojęcia opisane równaniami przedstawionymi w tabeli 43.1. Metryki Halsteada są wciąż popularne w środowisku inżynierów jakości, choć cała teoria jest w swych założeniach dosyć kontrowersyjna i stała się obiektem krytyki wielu ekspertów. Obszary ataków to m.in. metodologia pomiarów i sposób wyprowadzenia równań. Jednocześnie, badania empiryczne zwykle nie potwierdzają założeń i definicji Halsteada, nie licząc oszacowań długości

programu [200]. Ponadto, model na szacowanie długości zależy wprost od tych samych czynników (operatory i operandy), co rzeczywista długość programu, mamy więc do czynienia z zależnością pozorną (i faktycznie, wielu badaczy potwierdza wysoką korelację tych dwóch miar). Co więcej, wykorzystanie tego modelu jest ograniczone, gdyż wymagana jest znajomość czynników N1 i N2, których siłą rzeczy nie możemy znać a priori. Tabela 43.1. Metryki Halsteada

Metryka

W zór

Zas ób s łownictwa n

n = n1 + n2

Dług oś ć prog ramu N

N = N1 + N2 Model: N = n1log 2n1 + n2log 2n2

Wielkoś ć prog ramu V

V = Nlog 2 (n1 + n2)

Trudnoś ć D

D = (n1 /2) ⋅ (N2 /n2)

Nakład pracy E

E=V⋅D

Czas implementacji T

T = E/18

Liczba defektów B

B = E 2/3/3000 lub B = E/3000

Przykład. Rozważmy program z listingu 43.1, napisany w języku C++. Jest to funkcja przyjmująca dwa argumenty: tablicę T liczb oraz liczbę n elementów tej tablicy. Po zakończeniu działania funkcji tablica T (a dokładniej jej pierwszych n elementów) jest posortowana. Obliczymy teraz metryki Halsteada dla tego programu.

void Sortuj(int *T, int n) { int i, j, tmp; if (n50 Podatny na błędy, bardzo złożony i kłopotliwy w utrzymaniu, nies tabilny

Bardzo wys okie

Dobrą praktyką jest utrzymywanie tworzonych modułów w złożoności nieprzekraczającej CC = 10. Każdy bardziej skomplikowany moduł powinien być poddany przeglądowi. Warto rozważyć podzielenie takiego modułu na kilka mniejszych. Złożoność cyklomatyczna jest również związana z prawdopodobieństwem złej poprawy (ang. bad fix), czyli sytuacji, w której naprawa defektu powoduje powstanie innego defektu [363]. Zależność ta jest przedstawiona w tabeli 43.3. Tabela 43.3. Złożoność cyklomatyczna a prawdopodobieństwo złej poprawki

CC

Prawdopodobieństwo złej poprawki

1–10

5%

20–30

20%

>50

40%

ok. 100

60%

Istnieją różne odmiany złożoności cyklomatycznej różniące się sposobem traktowania operatorów logicznych oraz instrukcji wielokrotnego wyboru (typu switch-case), ale tak naprawdę wszystkie mają podobną moc opisową, dlatego przez CC będziemy rozumieć złożoność zdefiniowaną tak, jak na początku tego rozdziału czy w punkcie 6.2.6. Poza różnymi odmianami złożoności cyklomatycznej istnieją dwie istotne modyfikacje samej koncepcji złożoności strukturalnej kodu – tzw. gęstość złożoności cyklomatycznej oraz ECC (Essential Cyclomatic Complexity). Omówimy je w dalszych punktach.

43.2.1. Gęstość złożoności cyklomatycznej Gęstość złożoności cyklomatycznej (ang. Cyclomatic Complexity Density, CCD) to modyfikacja metryki CC polegająca na podzieleniu złożoności cyklomatycznej

przez liczbę tysięcy linii kodu:

Gill i Kemerer [364] na podstawie przeprowadzonych przez siebie badań empirycznych1 stwierdzili, że metryka CCD jest jednym z lepszych jednowymiarowych wskaźników efektywności utrzymania (ang. maintenance productivity), czyli mniejsze wartości CCD przewidują łatwiejszą pielęgnację. Efektywność utrzymania jest definiowana jako ilość pracy nad danym systemem po jego wydaniu („work done on a software system after it becomes operational” [365]). Gill i Kemerer podają następującą zależność między efektywnością utrzymania a metryką CCD: Productivity = 28,7 – 0,134 ⋅ CDD gdzie Productivity rozumie się jako całkowitą liczbę dodanych po wydaniu linii kodu podzieloną przez czas (w godzinach) potrzebny na ich zaimplementowanie.

43.2.2. ECC (Essential Cyclomatic Complexity) Programy bez instrukcji skoku mają ustrukturyzowaną formę, tzn. gdy następuje jakieś rozgałęzienie, w pewnym momencie wszystkie ścieżki schodzą się w jednym wierzchołku. Rysunek 43.1a przedstawia ustrukturyzowane elementy CFG. Gdy do programu zostaną wprowadzone instrukcje skoku, regularność ta zostaje zaburzona – powstają elementy nieustrukturalizowane (rys. 43.1b). ECC jest miarą nieustrukturalizowania kodu źródłowego. Tak jak CC i CCD miara ta może być wykorzystana do pomiaru efektywności utrzymania kodu. Jeśli w wyniku maksymalnego redukowania CFG (przez sprowadzanie każdego ustrukturalizowanego elementu do jednego wierzchołka, czyli przez usunięcie wszystkich struktur, takich jak sekwencje instrukcji, if, case, while, repeat) otrzymamy jeden wierzchołek, to znaczy, że program jest ustrukturyzowany – w takim przypadku ECC = 1. Jeśli jednak otrzymamy większy graf, z którego nie da się już nic więcej usunąć, oznacza to, że struktura programu zawiera instrukcje zaburzające jego wewnętrzną strukturę (np. goto). Wtedy ECC wynosi tyle, ile CC dla tego zredukowanego grafu.

Rysunek 43.1. Ustrukturalizowane i nieustrukturalizowane elementy CFG

Rozważmy przykładowy CFG G z rysunku 43.2 oznaczony jako graf wyjściowy. Obliczymy jego ECC. Możemy dokonać redukcji trzech jego wierzchołków oznaczonych szarym prostokątem. Po redukcji wierzchołki te „zejdą się” do wierzchołka oznaczonego na tym grafie jako X. Na drugim diagramie przedstawiono graf G1 po tej redukcji. Następnie znowu możemy zredukować sekwencję oznaczoną w G1 na szaro do wierzchołka oznaczonego na G1 przez X. Graf G2 przedstawia G1 po tej redukcji. Możemy jeszcze zredukować dwa wierzchołki w G2 (tworzące sekwencję) do jednego, otrzymując graf G3. W G3 nie możemy już nic więcej zredukować, zatem G3 jest maksymalnie zredukowanym grafem G. Dla zredukowanego grafu obliczamy jego CC. Graf G3 ma 6 krawędzi i 4 wierzchołki2, zatem ECC(G) = CC(G3) = 6 – 4 + 2 = 4

Rysunek 43.2. Redukcja CFG w celu obliczenia ECC

43.3. Konstrukcje składniowe Metryki dla konstrukcji składniowych rozróżniają stopień złożoności instrukcji sterujących w programie. Modele wykorzystujące te metryki w bardzo silnym stopniu zależą od konkretnej organizacji, poziomu jej dojrzałości i środowiska produkcyjnego (np. rodzaju wykorzystywanego języka programowania). Jeśli jednak wiemy, że istnieje związek między składnią programu a jego jakością, to możemy tworzyć własne modele. Oto kilka przykładowych modeli występujących w literaturze przedmiotu [366]: LDO = – 2,5 + 0,003 ⋅ LOC + 0,001 ⋅ n2 (wsp. korelacji r 2 = 0,66) LDO = 0,11 ⋅ SIF – THEN + 0,03 ⋅ Lodw (wsp. korelacji r 2 = 0,88)

LDO = 0,15 + 0,23 ⋅ SDO – WHILE + 0,22 ⋅ SSELECT + 0,07 ⋅ SIF – THEN (wsp. korelacji r 2 = 0,55) gdzie LDO oznacza liczbę defektów operacyjnych, n2 to znana z metryk Halsteada liczba unikalnych operandów, Lodw to liczba odwołań, a SX oznacza liczbę struktur typu X3. Zastosowanie pierwszych dwóch równań jest raczej ograniczone, trzecie jest bardziej współczesne, choć wyprowadzone na podstawie programów pisanych w COBOLu. Badania [366] mają jeszcze inną wartość – autorzy doszli do wniosku, że większość programistów ma problemy z prawidłowym stosowaniem konstrukcji do-while, dlatego należy dążyć do zminimalizowania ich ilości w programie.

43.4. Metryki struktur Metryki struktur nie skupiają się wyłącznie na modułach jako oddzielnych, wyizolowanych elementach, tak jak to czynią wcześniej zdefiniowane metryki. Metryki struktur uwzględniają dodatkowo interakcje zachodzące wewnątrz oraz między modułami i wyrażają je w sposób ilościowy. Oto przykładowe miary struktur. Metryka złożoności wywołań McClure’a. Zdefiniowana została przez McClure’a [367]. Metryka ta skupia się na złożoności związanej ze strukturami i zmiennymi sterującymi. McClure twierdzi, że nie wszystkie predykaty mają taką samą złożoność i wiąże większą złożoność z tymi zmiennymi występującymi

w instrukcjach warunkowych, które determinują wywołanie innych procedur (modułów). Złożoność modułu P jest obliczana według wzoru IC(P) = A + B gdzie A jest złożonością dotyczącą zmiennych sterujących związanych z wywołaniem P, a B – złożonością dotyczącą zmiennych sterujących związanych z wywołaniem przez P innych modułów. Metryka obciążenia wejścia i wyjścia struktur logicznych. Metryka zaproponowana przez Henry’ego i Kafurę [368], oparta z kolei na koncepcji skojarzeń zaproponowanej przez Yourdona i Constantine’a [369] i Myersa [370]. Idea opiera się na założeniu, że moduły o dużym obciążeniu wejścia (czyli wywoływane przez wiele innych modułów) są zwykle niewielkie i o prostej strukturze. Z kolei największe, skomplikowane moduły charakteryzują się zwykle znacznie mniejszym obciążeniem wejścia. Duże obciążenie wejścia i wyjścia może zatem oznaczać problemy z jakością. Złożoność struktury według Henry’ego i Kafury oblicza się, dla zadanego modułu, ze wzoru SC = (IN ⋅ OUT)2 gdzie IN oznacza obciążenie wejścia (ang. fanin – liczba lokalnych przepływów danych do modułu plus liczba struktur danych używanych jako wejście), a OUT – obciążenie wyjścia (ang. fanout – liczba lokalnych przepływów danych z modułu plus liczba struktur danych wykorzystywanych jako wyjście). Wysoka złożoność struktury modułu może wskazywać na: brak kohezji; potencjalne wąskie gardło systemu (ze względu na zbyt duży transfer informacji); dużą złożoność funkcjonalną; konieczność uproszczenia, przetestowania modułu.

refaktoryzacji

lub

dokładnego

W badaniach, które sprawdzały przydatność tego rodzaju metryk do przewidywania złożonych modułów oraz podatności modułów na błędy [371], odkryto, że obciążenie wejścia nie jest dobrze skorelowane z przewidywanymi zmiennymi. Zaleca się więc, aby reprezentować obciążenie wejścia tylko przez liczbę struktur danych używanych jako wejście, nie uwzględniając liczby lokalnych przepływów danych do modułu [372].

43.5. Metryki złożoności systemu 43.5.1. Indeks utrzymywalności Miary złożoności zwykle stosuje się do wyszukiwania modułów, które są/będą trudne w tworzeniu oraz późniejszym utrzymaniu. W latach 90. XX wieku wspólnym wysiłkiem ludzi wywodzących się ze środowisk nauki i biznesu opracowano tzw. indeks utrzymywalności (ang. Maintainability Index, MI), który wykorzystuje metryki LOC, Halsteada oraz CC. Indeks ten może być wykorzystywany przez programistę do sprawdzenia, czy w wyniku jego prac pielęgnacyjnych nad modułem efektywność utrzymania modułu zwiększyła się, czy też zmniejszyła. Indeks utrzymywalności był z sukcesem wykorzystany w wielu systemach przemysłowych oraz systemach militarnych wielkiej skali [373]. Metrykę MI oblicza się ze wzoru

gdzie

to średnia wielkość modułu (uśredniona miara V Halstaeda dla

wszystkich komponentów analizowanego systemu),

to średnia rozszerzona

złożoność cyklomatyczna4,

to uśredniona liczba linii kodu na moduł, a PK jest średnim odsetkiem linii programu będących komentarzami. Ostatni składnik wzoru, 50 sin((2,4PK)0,5), jest opcjonalny. Można go wykorzystywać wtedy, gdy komentarze w kodzie są rzeczywiście pomocne w jego analizie. Istnieją następujące reguły inżynierskie wiążące wartość metryki MI ze stopniem utrzymywalności systemu [374]: jeśli MI > 85, to system jest efektywny w utrzymaniu; jeśli 65 < MI ≤ 85, to system jest umiarkowanie efektywny w utrzymaniu; jeśli MI ≤ 65, to system jest trudny w utrzymaniu.

Rozważmy ponownie program z listingu 43.1. Obliczymy indeks utrzymywalności tego programu (traktujemy go jako system, który jest złożony tylko z jednego modułu, zatem wszystkie wartości średnie są po prostu miarami tej jedynej funkcji). Mamy:

Zatem

Oznacza to, że system ten jest bardzo efektywny w utrzymaniu (co zgadza się z naszą intuicją).

43.5.2. Metryka złożoności systemu Agrestiego–Carda–Glassa Metryka Agrestiego–Carda–Glassa (ACG) jest oparta na zasadach projektowania strukturalnego dotyczących kohezji i powiązania (ang. coupling) [375], [376]. Wykorzystuje również złożoność modułów oraz poziom skomplikowania zależności między modułami. Metryka ACG odzwierciedla stopień jakości architektury systemu. Jest zadana równaniem ACG = S + D gdzie S jest całkowitą złożonością strukturalną (między modułami), a D jest całkowitą złożonością modelu danych (metryka wewnątrzmodułowa). Komponent S bazuje na metryce Henry’ego–Kafury (ignorując obciążenie wejścia i uwzględniając jedynie obciążenie wyjścia):

gdzie OUTi jest złożonością wyjścia modułu i (bez zliczania struktur danych), a n to całkowita liczba komponentów systemu. Z kolei komponent D jest oparty na założeniu, że wewnątrzmodułowa złożoność wzrasta wraz ze wzrostem liczby wewnętrznych zmiennych i maleje wraz ze zwiększeniem delegacji funkcjonalności do innych modułów, co jest mierzone miarą złożoności wyjścia:

gdzie Vi jest liczbą zmiennych przesyłanych z oraz do i-tego modułu (tzw. zmiennych wejść/wyjść). Relatywna złożoność systemu (ang. Relative System Complexity, RSC) to normalizacja złożoności systemu na liczbę wszystkich modułów systemu, czyli po prostu uśredniona wartość ACG:

Istniejące badania (niestety nie ma ich zbyt wiele) wskazują, że miara RSC jest dobrym predyktorem częstości defektów. Przyjmuje się, że systemy, dla których RSC ≤ 26, mają dobrą architekturę (i odpowiednio niską złożoność), natomiast te, dla których RSC > 26, są zbyt złożone. Decydując się na wykorzystanie tej metryki, należy jednak zdefiniować własne reguły na podstawie pomiarów swoich

projektów. Metryka ACG była z powodzeniem wykorzystywana w dużych projektach takich firm jak HP czy Motorola [372]. Przykład. Rozważmy przykładowy system z rysunku 43.3. Każdy prostokąt oznacza moduł z informacją o liczbie zmiennych wejść/wyjść Vi. Strzałki reprezentują wywołania modułów. Obliczymy metrykę ACG dla tego systemu.

Rysunek 43.3. Przykładowy system Mamy V1 = 2, V2 = 4, V3 = 5, V4 = 1, V5 = 3. Złożoność wyjścia OUT jest reprezentowana przez liczbę strzałek wychodzących z modułu, zatem OUT1 = 3, OUT2 = OUT3 = OUT4 = 1, OUT5 = 0. Mając wartości Vi oraz OUTi, możemy policzyć złożoność modelu danych: D1 = 0,5; D2 = 2; D3 = 2,5; D4 = 0,5; D5 = 3. Zatem D = ∑ iDi = 8,5. Całkowita złożoność strukturalna S to suma kwadratów wartości OUTi: S = 9 + 1 + 1 + 1 + 0 = 12. Zatem ACG = 8,5 + 12 = 20,5, natomiast relatywna złożoność systemu

wynosi RSC = 20,5/5 = 4,1. Możemy uznać, iż jest to system nieproblematyczny, o dobrej, niskiej złożoności.

43.6. Metryki obiektowe Miary

obiektowe

odnoszą

się

do

systemów

budowanych

zgodnie

z paradygmatem programowania/projektowania obiektowego. Programy zorientowane obiektowo są zbudowane z obiektów, które mogą się ze sobą komunikować. Każdy obiekt reprezentuje część funkcjonalności systemu związaną z jakimś jego elementem. Paradygmat obiektowy posługuje się technikami takimi jak: klasy, modularność, enkapsulacja, dziedziczenie, abstrakcja. Jest to zupełnie inne podejście niż strukturalna technika projektowania/programowania. Okazuje się, że nie wszystkie metryki strukturalne można bezpośrednio przenieść na programy obiektowe, ze względu na odmienną definicję operacyjną złożoności strukturalnej w przypadku programów obiektowych. Na przykład, metryka linii kodu nie jest dobrą miarą wielkości kodu obiektowego, podobnie jak złożoność wyjścia nie jest dobrą metryką złożoności programu obiektowego. W rozdziale tym omówimy dwa najbardziej znane zbiory metryk zdefiniowanych specjalnie dla programów obiektowych. Są one powszechnie wykorzystywane w inżynierii oprogramowania obiektowego. Zbiory te to metryki Lorenza oraz metryki Chidambera–Kemerera. Do zrozumienia treści zawartych w kolejnych podrozdziałach jest wymagana znajomość paradygmatu obiektowego, w szczególności takich pojęć jak klasa, obiekt, enkapsulacja, dziedziczenie itp. Na rynku istnieje wiele książek omawiających ten temat w odniesieniu do poszczególnych języków programowania.

43.6.1. Metryki Lorenza Metryki Lorenza zostały wprowadzone w książce [377]. Jest to zbiór jedenastu metryk, z których część to bardziej wskazówki dotyczące sposobu wytwarzania programów obiektowych, niż metryki w ścisłym tego słowa znaczeniu. Przedstawione są one w tabeli 43.4, przytoczonej za [200]. Jak widać, metryka L8 to raczej dobra praktyka programistyczna, L9 jest wskaźnikiem jakości, a L11 można zaliczyć do zbioru wewnątrzprocesowych

metryk jakości służących kontrolowaniu poprawności obiektowego procesu produkcji. Zalecenia Lorenza, podane w ostatniej kolumnie tabeli 43.4, są pomocnymi wskazówkami dla deweloperów oraz inżynierów jakości.

43.6.2. Metryki CK Chidamber i Kemerer zdefiniowali w 1994 roku zestaw sześciu metryk obiektowych, znanych pod nazwą metryk CK [378]. Obecnie jest to chyba najpopularniejszy zestaw metryk obiektowych stosowanych w praktyce. Oto ich spis: WMC (ang. Weighted Methods per Class), czyli ważona liczba metod w klasie. Jest to liczba metod ważona współczynnikami odpowiadającymi złożoności tych metod:

gdzie n jest liczbą metod w klasie, a ci – złożonością i-tej metody. Jeśli wszystkie wagi są równe 1, to WMC jest po prostu liczbą metod w klasie. W oryginalnej Tabela 43.4. Metryki Lorenza5

Metryka Nazwa

Zalecenia/komentarze

L1

Ś redni rozmiar metody (w LOC)

Powinien być mniejs zy niż 8 LOC w S malltalk i 24 w C++.

L2

Ś rednia liczba metod w klas ie

Nie powinna przekraczać 20 (więks za ś rednia oznacza powierzenie wielu zadań niewielkiej liczbie obiektów).

L3

Ś rednia liczba zmiennych wys tąpienia w klas ie

Nie powinna przekraczać 6 (więks za wartoś ć oznacza zbyt duże obciążenie pojedynczej klas y).

L4

Nie powinna przekraczać 6 Poziom zag nieżdżenia (DIT=dług oś ć najdłużs zej ś cieżki hierarhii klas (DIT5) w drzewie dziedziczenia).

L5

Liczba zależnoś ci typu Powinna być mniejs za niż wartoś ć Pods ys tem/Pods ys tem metryki L6.

L6

Powinna być s tos unkowo wys oka, bo odzwierciedla poziom kohezji Liczba zależnoś ci typu klas w pods ys temie. Jeś li kohezja Klas a/Klas a jes t niewielka, to należy rozważyć w pods ys temach przenies ienie klas y do inneg o pods ys temu.

L7

Użycie zmiennej wys tąpienia

Jeś li pos zczeg ólne g rupy metod jednej klas y odpowiadają g rupom używanych przez nie zmiennych, to należy rozważyć podzielenie klas y na mniejs ze, według wykrytych g rup.

L8

Ś rednia liczba linii komentarzy w metodzie

Powinna być więks za niż 1.

L9

Liczba zg łos zonych problemów w klas ie

Powinna być możliwie nis ka.

L10

Liczba ponownych użyć klas y

Jeś li klas a nie jes t używana wielokrotnie, to należy rozważyć jej przebudowę.

Liczba odrzuconych klas i metod

Powinna tylko nieznacznie zmieniać s ię w czas ie trwania proces u. Jeś li s ię zmienia, to prawdopodobnie mamy do czynienia z produkcją przyros tową, a nie właś ciwym proces em iteracji obiektowo orientowanym.

L11

pracy [378] Chidamber i Kemerer sugerowali, aby wagi odzwierciedlały stopień trudności implementacji (np. przez wykorzystanie metryki złożoności cyklomatycznej), jednak ponieważ sposób zdefiniowania wag istotnie wpływa na wartość metryki, postać wag jest wciąż przedmiotem debaty wśród badaczy. Często przyjmuje się jako wagi wektor normalizujący do 1, czyli współczynniki, których suma wynosi 1, a ich wielkości są np. proporcjonalne do złożoności cyklomatycznych poszczególnych modułów6. Większe wartości WMC oznaczają większy wpływ klasy na jej potomków. Klasy z dużą liczbą metod są zwykle specyficzne dla logiki biznesowej danej aplikacji, co może ograniczać ich potencjalne ponowne wykorzystanie. CBO (ang. Coupling Between Objects), czyli powiązanie obiektów. Jest to liczba powiązań klasy z innymi klasami; powiązanie występuje, gdy w danej klasie istnieje odwołanie do metod lub zmiennych innych klas. Metryka CBO zlicza również powiązanie wynikające z dziedziczenia oraz tylko jawne wywołania konstruktorów obiektów innych klas. Wraz ze wzrostem CBO zmniejsza się możliwość reużycia danej klasy. Duża liczba wywołań innych metod w danej klasie (złożoność wyjścia, ang. fan-out) jest niepożądana. Duża liczba metod odwołujących się do metody lub zmiennej danej klasy (złożoność wejścia, ang. fanin) oznacza zwykle, ze klasa ta jest dobrze zaprojektowana i jest dobrym kandydatem do reużywania. Patrząc globalnie na cały system, nie da się niestety utrzymać dla wszystkich klas wysokiej złożoności wejścia i niskiej złożoności wyjścia. DIT (ang. Depth of the Inheritance Tree), czyli głębokość drzewa dziedziczenia. Jest to długość najdłuższej ścieżki w drzewie dziedziczenia prowadzącej od korzenia do liścia, liczonej jako liczba klas na tej ścieżce. Najkrótsza ścieżka może więc przyjąć wartość 1 (drzewo złożone tylko z jednej klasy). Większe wartości DIT oznaczają, że projekt ma większą złożoność, ale też stanowi dobre środowisko dziedziczenia. Jednocześnie wysokie DIT może wskazywać na trudności w testowaniu systemu. NOC (ang. Number of Children of a Class), czyli liczba potomków klasy. Jest to liczba bezpośrednich podklas danej klasy w hierarchii dziedziczenia. Generalnie uważa się, że odpowiednio duża głębokość drzewa (DIT) jest ważniejsza niż duża jego szerokość, ponieważ wskazuje na poprawne wykorzystanie reużycia komponentów. Ponadto, klasy leżące wyżej w hierarchii powinny mieć większe

wartości NOC niż klasy leżące niżej. Metryka NOC określa pośrednio wpływ klasy na projekt testowy: klasy z dużą liczbą dzieci mogą wymagać więcej testowania. RFC (ang. Response for a Class), czyli odpowiedź klasy. Jest to liczba metod, które mogą być wykonane w odpowiedzi na komunikat (lokalny bądź zdalny) odebrany przez obiekt danej klasy. Większe wartości RFC oznaczają większą funkcjonalność, większą złożoność klasy, większy wysiłek potrzebny do jej przetestowania oraz większe trudności w jej zrozumieniu. LCOM (ang. Lack of Cohesion of Methods), czyli brak kohezji metod. Jest to liczba par metod niedzielących żadnych wspólnych zmiennych lokalnych minus liczba par metod, które dzielą przynajmniej jedną wspólną zmienną lokalną. Formalnie, niech klasa K ma n metod M 1, …, M n. Przez Zi oznaczmy zbiór zmiennych wykorzystywanych przez klasę M i. Zdefiniujmy dwa zbiory: P = {{Zi, Zj}:i ≠ j ∧ Zi ∩ Zj = Ø}, Q = {{Zi, Zj}:i ≠ j ∧ Zi ∩ Zj ≠ Ø}. Zauważmy, że zawsze będzie zachodzić ponieważ tyle istnieje dwuelementowych podzbiorów zbioru n-elementowego. Metrykę braku kohezji metod dla klasy K możemy teraz formalnie zdefiniować jako

Metryka LCOM opisuje stopień podobieństwa metod: jeśli dwie metody używają wspólnych instancji zmiennych, to są uważane za podobne. Istnieją również inne definicje metryki LCOM: Niech G będzie nieskierowanym grafem, w którym wierzchołki reprezentują metody, a dwa wierzchołki są połączone wtedy i tylko wtedy, gdy odpowiadające im metody wykorzystują przynajmniej jedną wspólną zmienną. Wtedy LCOM jest liczbą spójnych składowych grafu G. Metryka podobna do poprzedniej z tą różnicą, że połączenia dodaje się również w przypadku, gdy jedna z metod wywołuje drugą lub na odwrót. Niech V i E będą odpowiednio liczbą wierzchołków i krawędzi grafu z poprzedniego punktu. Wtedy

Niech M 1, …, M n będą metodami, a {a 1, …, a m} zbiorem atrybutów. Niech μj oznacza liczbę metod, które odwołują się do atrybutu a j. Wtedy

Przykład. Rozważmy projekt, którego diagram klas jest przedstawiony na rysunku 43.4. Po lewej stronie jest przedstawiona hierarchia klas. Każda klasa ma swoją nazwę, zbiór zmiennych (oznaczanych jako a1, a2 itd.) oraz zbiór metod (oznaczanych jako M1(), M2() itd.). Klasy B, C i D dziedziczą po klasie A, E i F – po klasie B, G po klasie D, a H po klasie F.

Rysunek 43.4. Przykładowa hierarchia klas Obliczmy metryki CK dla niektórych klas. Dla miary WMC przyjmijmy wagi równe 1. Mamy wtedy WMC(A) = WMC(E) = 2, WMC(B) = WMC(D) = 3, WMC(C) = WMC(F) = WMC(G) = WMC(H) = 1. Środkowa część rysunku przedstawia definicję klasy E. Obiekty tej klasy odwołują się do zmiennych a1 i a2 z klasy A oraz a4 z klasy B. Ponadto, metoda M10 klasy E wywołuje metodę M3 z klasy B. Klasa E jest więc powiązana z dwoma innymi klasami: A i B. Zatem CBO(E) = 2. DIT = 4, ponieważ najdłuższy łańcuch klas w drzewie dziedziczenia zawiera 4 klasy: A, B, F, H. NOC(A) = 3, ponieważ klasa A ma 3 potomków w drzewie dziedziczenia. Analogicznie, NOC(B) = 2, NOC(C) = NOC(E) = NOC(G) = NOC(H) = 0, NOC(D) = NOC(F) =

1. RFC(E) = 2, ponieważ w odpowiedzi na otrzymany komunikat G.a6 obiekt klasy E wywołuje obie swoje metody, M10 i M11 (patrz prawa strona rys. 43.4). Załóżmy, że w klasie D metoda M7 wykorzystuje zmienne a8 i a9, M8 – zmienną a9, a M9 – zmienną a10. Metody {M7, M8} mają wspólną zmienną a9, natomiast zarówno metody {M7, M9}, jak i {M8, M9} nie mają wspólnych zmiennych. Spośród 3 par zmiennych jedna ma co najmniej jedną wspólną zmienną, a dwie nie mają ani jednej. Zatem LCOM(D) = 2 – 1 = 1.

43.7. Metryki złożoności dokumentacji Dla dokumentacji, tak jak w przypadku kodu źródłowego, możemy – poza miarami objętości opisanymi w punkcie 41.1.3 – konstruować miary jej złożoności. Złożoność definiujemy tu jako trudność w zrozumieniu dokumentu. Metryki te mogą być przydatne, gdy dokumentacja użytkowa (lub techniczna) jest istotnym elementem dostarczanego oprogramowania. Jej czytelność ułatwia korzystanie z aplikacji, zwiększa zatem pośrednio jakość produktu. Istnieje kilka powszechnie wykorzystywanych miar złożoności dokumentacji. Dalej omawiamy trzy przykładowe. Miara Lexile. Jest to narzędzie komercyjne (www.lexile.com), służące zarówno do oceny zdolności rozumienia tekstu, jak i oceny trudności samego tekstu. Dzięki takiej dwutorowej ocenie możemy np. dopasować poziom trudności tekstu dokumentacji użytkowej programu do przeciętnego poziomu zdolności czytania wśród potencjalnych użytkowników tego programu. Indeks czytelności Flescha. Indeks FRES (ang. Flesch Reading Ease Score) określa stopień trudności w rozumieniu tekstu zapisanego w języku angielskim. Metryka ta jest wyliczana na podstawie trzech parametrów tekstu: liczby słów, sylab oraz zdań w tekście według wzoru

FRES = 206,835 – 1,015 ⋅

całkowita liczba s łów całkowita liczba zdań

całkowita liczba – 84,6 s ylab ⋅ całkowita liczba s łów

Im niższa wartość indeksu, tym tekst jest trudniejszy do zrozumienia. W interpretacji wyniku testu może być pomocna tabela 43.5.

Tabela 43.5. Indeks Flescha – interpretacja wyników testu

W artość indeksu

T rudność tekstu

90–100

Teks t łatwy do zrozumienia dla jedenas toletnieg o ucznia

60–70

Teks t łatwy do zrozumienia dla uczniów w wieku 13– 15 lat

0–30

Teks t zrozumiały dla abs olwentów wyżs zych uczelni

Indeks Gunninga. Indeks Gunninga (tzw. Gunnig Fog Index) mierzy czytelność tekstu anglojęzycznego. Wykorzystuje trzy zmienne: liczbę słów, liczbę zdań oraz liczbę tzw. fog words, czyli słów „złożonych”. Słowa złożone to słowa zawierające 3 lub więcej sylab, przy czym do sylab nie zalicza się typowych końcówek słów angielskich (-es, -ing, -ed). Do słów złożonych nie zalicza się również nazw własnych, słów pochodzących z żargonu lub będących złączeniem dwóch słów. Indeks Gunninga wylicza się ze wzoru

Wartość indeksu określa liczbę lat formalnej edukacji potrzebnej, aby przeczytać dany tekst ze zrozumieniem. Wartość indeksu równa 12 lub mniej oznacza, że tekst powinien być zrozumiały dla przeciętnego osiemnastolatka. Teksty powszechnie zrozumiałe powinny mieć indeks 8 lub niższy. Istnieje więcej tego typu miar, np. indeks Coleman–Liau, formuła czytelności Dale–Chall, Automated Readibility Index, indeks SMOG, formuła czytelności Frya i inne.

43.8. Metryki złożoności algorytmicznej Miary złożoności algorytmicznej odnoszą się do czasu oraz miejsca potrzebnego na przeprowadzenie obliczeń wykonywanych przez dany algorytm. Miary te są funkcją rozmiaru danych wejściowych. W opisie złożoności algorytmicznej wykorzystuje się najczęściej tzw. notację dużego O. Na przykład, jeśli algorytm ma

złożoność czasową O(n2) to oznacza to, że jeśli otrzyma na wejściu dane o rozmiarze n, to czas jego działania będzie proporcjonalny do n2, czyli do kwadratu rozmiaru wejścia. Im złożoność czasowa/pamięciowa niższa, tym szybszy i efektywniejszy jest dany algorytm. Zwykle trzeba jednak iść na kompromis, ponieważ złożoności czasowa i pamięciowa są ze sobą w relacji – zmniejszenie złożoności czasowej powoduje najczęściej wzrost złożoności pamięciowej i na odwrót. Kwestie związane z pomiarem złożoności obliczeniowej i w ogóle z algorytmiką wykraczają poza zakres tej książki (Czytelnika zainteresowanego szczegółami odsyłamy do podręcznika Cormena [379]). Niemniej jednak zagadnienia te mogą być istotne w kontekście np. testowania wydajności programów silnie opartych na algorytmach obliczeniowych.

1 Badanie było przeprowadzone na bardzo małej liczbie elementów – pod uwagę wzięto tylko 7 programów stworzonych w jednej firmie, ale za to z dobrze udokumentowanymi danymi dotyczącymi efektywności ich utrzymania. 2 Złożoność cyklomatyczną dla G3 moglibyśmy równoważnie wyliczyć, zliczając występujące w nim decyzje binarne (3) i dodając 1. 3 Badania te były przeprowadzane na programach napisanych w COBOLu, stąd zmienne takie jak liczba odwołań czy liczba poleceń SELECT. Dwa pierwsze równania wyprowadzono na podstawie badań próbki 20 modułów, trzecie – na podstawie 66 modułów. 4 Rozszerzona złożoność cyklomatyczna, poza decyzjami zlicza wykorzystanie w predykatach spójników logicznych – każde wykorzystanie spójnika OR lub AND wnosi 1 do całkowitej złożoności cyklomatycznej. 5 DIT to powszechnie używany skrót będący akronimem angielskiej nazwy głębokości drzewa dziedziczenia (ang. Depth of the Inheritance Tree). 6 Na przykład, jeśli klasa składa się z 3 metod o złożonościach cyklomatycznych odpowiednio 2, 14 i 4, to wagi unormowane będą miały postać 2/20 = 0,1; 14/20 = 0,7 oraz 4/20 = 0,2.

44. Metryki i modele wysiłku

Metryki te są – z naturalnych względów – najczęściej wykorzystywanym narzędziem estymacji wysiłku przez kierowników i menedżerów. Wraz z modelami wysiłku służą do szacowania: kosztu przedsięwzięcia (kosztochłonność); czasu trwania projektu lub jego części (czasochłonność); nakładu pracy potrzebnego (pracochłonność).

do

wykonania

danej

czynności

Są to głównie narzędzia służące do planowania i szacowania pracy. W szczególności mogą być wykorzystane na potrzeby projektu testowego. W rozdziale 22 opisaliśmy obszernie zagadnienia związane z szacowaniem testów. Wysiłek można mierzyć różnymi miarami. Te podstawowe to czas i pieniądze. Najczęściej jednak wykorzystuje się metryki pochodne: iloczynowe (osobogodziny, osobotygodnie, osobomiesiące itp.) oraz ilorazowe (koszt na punkt funkcyjny, czas oprogramowania jednego przypadku użycia wykorzystywane w metodach szacowania wysiłku to:

itp.).

Główne

techniki

zgadywanie (np. w metodzie Delfickiej czy w pokerze planistycznym); dekompozycja (np. Work Breakdown Structure); wiedza ekspercka (np. w metodzie Delfickiej); porównanie (np. benchmarki, dane historyczne); wykorzystanie bezpośrednich i pośrednich (tzw. proxy points) miar wielkości (np. w modelach parametrycznych czy metodzie analogii). Poszczególne metody mogą wykorzystywać jednocześnie kilka wymienionych technik, np. można wyobrazić sobie metodę opartą na dekompozycji, w której

szacowanie każdego komponentu odbywa się za pomocą zgadywania wielkości, a następnie wykorzystaniu jej w modelu parametrycznym.

44.1. Modele oparte na zgadywaniu i intuicji Techniki szacowania oparte na zgadywaniu czy intuicji omówiliśmy w punktach 22.2.1 oraz 29.4.3 (poker planistyczny).

44.2. Modele oparte na dekompozycji W punkcie 22.2.3 pokazaliśmy przykład szacowania liczby testów na podstawie metody WBS (struktury podziału prac). Moglibyśmy równie dobrze od razu szacować nie samą liczbę testów, ale np. czas, koszt lub pracochłonność ich tworzenia, a więc wykorzystać WBS jako technikę szacowania wysiłku. WBS można wykorzystać na dwa sposoby: dekompozycji podlegać może praca i czynności lub struktura projektu. W tabelach 44.1 i 44.2 mamy podane przykłady obu dekompozycji w odniesieniu do szacowania pracochłonności. Tabela 44.1. Pracochłonność projektu testowego według podziału prac i czynności

Pracochłonność (w osobotygodniach) Czynność Miesiąc >

1 2 3 4 5 6 7 8 9 10 11 12 13 14 Razem

Planowanie tes tów

5 8 5

18

Analiza wymag ań

4 2

6

Opracowanie s trateg ii 1 5 2

8

Plan tes tów

4

1 3

Projekt tes tów

2 4 4 8 4

22

Tes ty akceptacyjne

2 4

6

Tes ty s ys temowe

4 5

9

Tes ty integ racyjne Implementacja i wykonanie

3 4 2

7

6 8 6 6

10 9

Tes ty jednos tkowe

6 6 4 4

4

Tes ty integ racyjne

2

6

Tes ty s ys temowe

Tes ty dokumentacji

2 2

61

6

14 4 4

2

6

24

3

Tes ty akceptacyjne

8

7 4

8

2

8

RAZEM

101

W tabeli 44.1 na projekt testowy patrzymy jak na zbiór czynności, które musimy wykonać. Każdą z nich szacujemy z osobna (być może wykorzystując inne techniki estymacji, takie jak zgadywanie, metoda ekspercka, porównanie czy model parametryczny). W szarych wierszach znajdują się sumy częściowe odpowiadające większym grupom czynności: planowaniu, projektowaniu oraz implementacji i wykonaniu. Tabela 44.2. Pracochłonność projektu testowego według podziału systemu

Moduł

Graficzny interfejs użytkownika

Jednos tka s terująca

Czynność

Pracochłonność

Projektowanie tes tów

6

Tes towanie dokumentacji

4

Tes towanie

14

Projektowanie tes tów

11

Tes towanie dokumentacji

7

Baza danych

Tes towanie

25

Projektowanie tes tów

3

Tes towanie

6

RAZEM

76 os obotyg odni

W tabeli 44.2 przedmiotem podziału prac jest struktura systemu. Jest on dekomponowany na części (GUI, jednostka centralna, baza danych). Jeśli części te byłyby nadal zbyt duże, to moglibyśmy dekomponować je dalej, do momentu uzyskania komponentów na tyle małych, by móc z dużą dokładnością oszacować pracochłonność związaną z danym komponentem (lub móc porównać je z innymi komponentami z poprzednich projektów). Dla każdego komponentu wyróżniliśmy ponadto kilka czynności testowych (np. projektowanie testów, wykonanie testów).

44.3. Modele oparte na wiedzy eksperckiej Techniki eksperckie omówiliśmy w podrozdziale 29.4 (wielokrotne głosowanie oraz metody: delficka i delficka szerokopasmowa) oraz – w odniesieniu do szacowania testów – w punkcie 22.2.4 (estymacja grupowa).

44.4. Modele oparte na benchmarkach Techniki stosujące porównanie wykorzystują zwykle dwa rodzaje danych porównawczych: dane przemysłowe (benchmarki) oraz swoje własne dane historyczne. W tabeli 22.2 podaliśmy przykładowe dane przemysłowe związane z testowaniem. Wartości z tej tabeli można wykorzystać podczas szacowania czynności testowych. Na przykład możemy z niej odczytać, że średnio liczba potrzebnych przypadków testowych da się wyrazić wzorem FP1,2, gdzie FP to liczba punktów funkcyjnych, a wysiłek potrzebny dla stworzenia jednego przypadku testowego to 1 osobogodzina. Jeśli szacujemy, że nasz projekt będzie miał 40 punktów funkcyjnych, to według danych przemysłowych będziemy potrzebowali 401,2 ≈ 84 testy, których stworzenie zajmie 84 osobogodzin. Jeśli

zespół testerski liczy 3 osoby, to szacujemy, że czas potrzebny na stworzenie testów wyniesie 84 osobogodzin/3 godziny = 28 godzin. Przyjmując efektywność pracy na 5,5 godziny dziennie, szacujemy, że samo tworzenie testów zajmie 28/5,5 ≈ 5 dni. Inna reguła inżynierska [198], [380], służąca do dokonania szybkiego, ogólnego szacowania, mówi, że czas T trwania projektu (w miesiącach kalendarzowych), wymagany personel P dla nowego projektu, wymagany personel PM dla projektu utrzymywanego oraz wysiłek E wynoszą odpowiednio T = PFD, P = PF/150, PM = PF/750, E = T ⋅ P (lub T ⋅ PM ) gdzie D jest stałą (między 0,32 a 0,5) zależną od typu projektu. Zalecane wartości D są podane w tabeli 44.3. Tabela 44.3. Rekomendowane wartości współczynnika D

T yp projektu

Zalecane D

Wojs kowy

0,45

Przeciętny

0,4

Pros ty

0,32

Internetowy, duży

0,32

Internetowy, mały

0,5

Załóżmy, że tworzymy duży projekt internetowy liczący 800 PF. Wykorzystując powyższe reguły inżynierskie, obliczamy: czas T = 8000,32 ≈ 8,5 miesięcy kalendarzowych; personel

osób;

wysiłek E = 8,5 ⋅ 5,3 ≈ 45 osobomiesięcy. Oznacza to, że 5,3 osób może ukończyć projekt w ok. 8,5 miesiąca. Mając zespół trzech deweloperów, szacujemy, że rzeczywisty czas trwania projektu wyniesie 45/3 = 15 miesięcy. Do danych przemysłowych należy podchodzić bardzo ostrożnie. Mogą być dobrym punktem wyjścia do naszych analiz, ale nigdy nie powinny być jedynym

źródłem naszych szacunków. Należy pamiętać, że zawsze są to średnie z projektów, które niekoniecznie musiały być podobne do naszych i niekoniecznie musiały być przeprowadzane w podobnych do naszych warunkach (i zwykle nie są!).

44.5. Modele oparte na porównaniu 44.5.1. Porównanie proste (naiwne) Znacznie bardziej wartościowe jest wykorzystanie danych historycznych z własnych, poprzednich projektów. Te wartości będą dokładniejszymi estymatorami przyszłego wysiłku, ponieważ zwykle dotyczą tego samego (lub bardzo podobnego) zespołu i środowiska produkcyjnego. Rozważmy następujący przykład. W tabeli 44.4 są podane dane z dziewięciu ostatnich projektów dotyczące: rozmiaru projektu, liczby stworzonych przypadków testowych oraz całkowity czas poświęcony na ich implementację. Tabela 44.4. Przykładowe dane historyczne z poprzednich projektów

Projekt

1

2

3

4

5

6

7

8

9

Rozmiar projektu (PF)

16

42

38

32

116

40

39

71

63

Liczba przypadków tes towych

186 455 420 381 2066 327 461 290 365

Czas implementacji (os ⋅ h) 190 420 407 423 2550 380 493 305 429 Możemy uśrednić każdą z tych zmiennych oraz policzyć jej odchylenie standardowe: rozmiar projektu = 51 ± 29 liczba przypadków testowych = 550 ± 575 czas implementacji = 622 ± 728 Nie są to jednak zbyt przydatne oszacowania: w dwóch ostatnich zmiennych odchylenie standardowe jest większe od średniej! Jest to spowodowane tym, że

firma realizowała wiele różnych projektów – od niewielkich (takich jak projekt nr 1), do bardzo dużych (np. projekt nr 5). Aby uzyskać lepsze, bardziej wiarygodne szacunki, najpierw powinniśmy oszacować rozmiar analizowanego projektu, a potem wykorzystać dane historyczne tylko z podobnych projektów. Załóżmy, że estymacja wielkości nowego projektu wynosi 40 punktów funkcyjnych. Najbardziej podobne1 wielkością są projekty nr 2, 3, 6 i 7. Obliczmy ponownie średnie i odchylenia trzech parametrów ograniczając się tylko do czterech powyższych projektów: rozmiar projektu = 39,75 ± 1,7 liczba przypadków testowych = 416 ± 62 czas implementacji = 425 ± 48 Jak widać, są to lepsze oszacowania, z o wiele mniejszym odchyleniem standardowym. Jeśli zatem nasz nowy projekt ma mieć 40 PF, to liczba przypadków testowych będzie się wahać między 416 – 62 = 354 a 416 + 62 = 478, a czas implementacji między 377 a 473 osobogodzin.

44.5.2. Porównanie z uwzględnieniem różnic Można próbować dokonywać jeszcze precyzyjniejszej estymacji przez ilościowe uwzględnienie różnic między analizowanym projektem a projektem analogicznym, z którym dokonujemy porównania [372]. Schemat metody jest pokazany na rysunku 44.1.

Rysunek 44.1. Model szacowania przez porównanie Z bazy danych wybieramy projekt najbardziej podobny do bieżącego (oczywiście możemy wybrać ich więcej, dla każdego z nich przeprowadzić czynności opisane dalej, a otrzymane wartości uśrednić). Następnie określamy cechy projektu, które wpływają na wielkość wysiłku. Porównujemy projekt analizowany z projektem z bazy pod kątem tych cech, przypisując każdej z nich czynnik modyfikujący opisujący wpływ danej cechy na wartość bazową wysiłku. Rozważmy cechy opisane w tabeli 44.5. Wynika z niej np., że uznaliśmy, iż złożoność analizowanego systemu jest większa niż systemu z bazy i wysiłek związany z tą cechą będzie o 20% wyższy niż w projekcie bazowym. Tabela 44.5. Czynniki modyfikujące dla metody szacowania przez porównanie

Czynnik modyfikujący

Efekt na Estymacja bazę wpływu

Złożonoś ć s ys temu

+

20%

1,20

GUI



5%

0,95

Interfejs y i komunikacja



10%

0,90

Znajomoś ć ś rodowis ka wytwórczeg o przez zes pół

+

5%

1,05

Poziom krytycznoś ci s ys temu

0

0%

1,00

Modyfikator końcowy

W artość czynnika

1,077

Na podstawie wszystkich czynników modyfikujących oblicza się modyfikator końcowy będący iloczynem wartości wszystkich zidentyfikowanych czynników. W naszym przypadku wynosi on 1,2 ⋅ 0,95 ⋅ 0,9 ⋅ 1,05 ⋅ 1 ≈ 1,077. Następnie określamy, do których elementów szacowania wysiłku modyfikator ten będzie stosowany, a do których nie. Załóżmy, że projekt testowy składa się z następujących zadań: testy GUI, testy integracyjne, automatyzacja, testy bezpieczeństwa. GUI, interfejsy oraz krytyczność systemu są czynnikami, które bierzemy pod uwagę, więc dla czynności im odpowiadających będziemy stosować modyfikator. Projekt automatyzacji testów nie ma związku z tymi czynnikami. Będzie wyglądał podobnie w analizowanym projekcie, zatem nie stosujemy dla niego modyfikatora (niekoniecznie musimy przepisać wartości szacowane z projektu bazowego – możemy dokonać tu porównania ręcznego). Ostateczne porównanie i szacowanie wysiłku oraz kosztu projektu jest przedstawione w tabeli 44.6. Tabela 44.6. Szacowanie wysiłku i kosztu przez porównanie

Element kosztu/wysiłku

System bazowy

Modyfikator Estymacja

Tes ty GUI

360 os ⋅ h

1,077

387 os ⋅ h

Tes ty integ racyjne

200 os ⋅ h

Automatyzacja

170 os ⋅ h

Tes ty bezpieczeńs twa

450 os ⋅ h

Całkowita liczba os obog odzin

1180

Kos zt 1 os obog odziny

50 PLN

Kos zt tes towania

59 000 PLN

1,077

215 os ⋅ h 170 os ⋅ h

1,077

485 os ⋅ h 1257

62 850 PLN

44.6. Modele parametryczne Modele parametryczne wysiłku są opracowywane na podstawie danych historycznych z rzeczywistych projektów. Zmienną niezależną w tych modelach jest zwykle KLOC lub PF. Modele te należą zazwyczaj do rodziny modeli wykładniczych (czyli modeli opisywanych równaniem y = a + b ⋅ xc lub liniowych (y = ax + b). W literaturze istnieje wiele różnych modeli parametrycznych. Oto niektóre z nich [381]: model Walstona–Felixa: E = 5,2 ⋅ KLOC0,91 model Baileya–Basiliego: E = 5,5 + 0,73 ⋅ KLOC1,16 model Boehma: E = 3,2 ⋅ KLOC1,05 model Doty’ego: E = 5,288 ⋅ KLOC1,047 model Albrechta-Gaffneya: E = – 13,39 + 0,0545 ⋅ PF model Kemerera: E = 60,62 + 7,728 ⋅ 10–8 ⋅ PF 3 model Matsona–Barreta–Meltichampa: E = 585,7 + 15,12 ⋅ PF model SLIM Putnama:

Rysunek 44.2. Porównanie modeli parametrycznych dla wysiłku

W modelu Putnama S oznacza rozmiar oprogramowania w tzw. efektywnych liniach kodu, ELOC (Effective Lines of Code), B jest współczynnikiem proporcjonalności (jest to funkcja rozmiaru projektu), P oznacza produktywność procesu2,

czyli

zdolność

organizacji

do

wytwarzania

oprogramowania

o określonym rozmiarze na określonym poziomie gęstości defektów, T jest czasem trwania projektu w latach. Model Putnama zależy od rozmiaru oprogramowania S, a następnie wykorzystywany jest rozkład Rayleigha, aby wyprowadzić estymację wysiłku. Użytkownik może manipulować kształtem krzywej za pomocą współczynników B (ang. initial slope of the curve, zwany też MBI – Manpower Buildup Index) oraz P (tzw. stała technologiczna). Model ten można wykorzystać na dwa sposoby: albo dopasować jego parametry do danych historycznych, albo odpowiedzieć na 22 pytania ankiety SLIM, na podstawie których zostaną określone parametry B i P. Opcja druga jest dobrym rozwiązaniem, gdy nie mamy dostępnych żadnych danych historycznych, a chcielibyśmy ten model jednak wykorzystać. Może wydawać się dziwne, że każdy z wymienionych modeli daje inną predykcję (patrz rys. 44.2). Jest tak dlatego, że każdy z nich był budowany na podstawie danych z odmiennych projektów. Ponadto są to bardzo proste modele – wykorzystują zaledwie jedną zmienną niezależną (KLOC lub PF) jako predyktor wysiłku. W modelach Albrechta–Gaffneya oraz Kemerera estymacja wysiłku jest praktycznie funkcją stałą, niezależną od PF. Jaki z tego wszystkiego wynika wniosek? Modeli parametrycznych (tak jak danych przemysłowych) należy używać z dużą ostrożnością. Każdy z nich był konstruowany na podstawie specyficznych danych, projektów, środowisk wytwórczych. Jeśli mamy odpowiednią ilość danych historycznych z własnych, przeszłych projektów, to możemy zbudować swój własny model na ich podstawie. Jeśli środowisko wytwórcze w naszej organizacji jest stabilne, to własny model będzie zwykle lepszym narzędziem do szacowania wysiłku, niż te wymienione. Inną opcją, wartą rozważenia, jest użycie znanego i często wykorzystywanego modelu COCOMO II. Oba te podejścia opiszemy w kolejnych podrozdziałach.

44.6.1. Tworzenie własnego modelu Stworzenie własnego modelu przebiega w następujących krokach: 1. Zebranie danych historycznych.

2. Analiza (graficzna) zebranych danych i wybór postaci modelu. 3. Dopasowanie współczynników modelu. Załóżmy, że nasze dane historyczne wyglądają tak, jak w tabeli 44.7. Tabela 44.7. Dane historyczne o wysiłku i punktach funkcyjnych

PF

W ysiłek (osobomiesiące)

65

7

185

13

440

62

660

92

847

91

1023

177

Możemy wykorzystać zwykły arkusz kalkulacyjny, aby przeanalizować te dane graficznie, tzn. zobaczyć, jak wygląda zależność między punktami funkcyjnymi a wysiłkiem (rys. 44.3 z lewej).

Rysunek 44.3. Dane o PF i wysiłku oraz krzywa do nich dopasowana Uznajemy, że jest to zależność wykładnicza, tzn. opisana równaniem E = a ⋅ PFb, gdzie a i b są szukanymi współczynnikami modelu. Moglibyśmy oczywiście

uznać, że jest to zależność liniowa i dopasować model liniowy: y = ax + b. Wykorzystując pakiety statystyczne, takie jak R Statistical package3, możemy ocenić, który z modeli lepiej opisuje tę zależność przez obliczenie błędu dla każdego z modeli. W arkuszu kalkulacyjnym Excel istnieje możliwość wypisania na ekranie linii trendu (rys. 44.3 z prawej) oraz wartości R2. Możemy wypisać kilka linii trendu (np. liniową, potęgową, wykładniczą, wielomianową, logarytmiczną) i wybrać tę, która ma największą wartość R2, przy czym należy uważać, aby model nie był przeuczony4. Zdecydowaliśmy się na model wykładniczy, dlatego po dopasowaniu krzywej wykładniczej otrzymaliśmy równanie: E = 0,0428 ⋅ PF 1,1712 Jeśli wiemy, że nowy projekt będzie liczył 1400 PF, to z modelu szacujemy, że wysiłek będzie wynosił E = 0,0428 ⋅ 14001,1712 ≈ 207 osobomiesięcy.

44.6.2. Model COCOMO II Model COCOMO II został stworzony przez Barry’ego Boehma. Nazwa pochodzi od początkowych liter słów COnstructive COst MOdel, czyli konstruktywny model kosztu. Jest to ulepszona wersja wcześniejszego modelu, COCOMO I zdefiniowanego w 1981 roku, który w obliczu gwałtownego rozwoju narzędzi i metod inżynierii oprogramowania utracił swoją przydatność. COCOMO II jest lepiej przystosowany do nowoczesnych modeli wytwarzania oprogramowania, w szczególności modeli iteracyjno-przyrostowych. COCOMO II to tak naprawdę trzy modele: Application Composition Model – przeznaczony do stosowania w projektach wykorzystujących nowoczesne narzędzia tworzenia interfejsów użytkownika (np. RAD, czyli szybkie prototypowanie); Early Design Model – przeznaczony do stosowania we wczesnych fazach projektu, tzn. na etapie tworzenia architektury; Post-Architecture Model – stosowany, gdy architektura systemu została już opracowana.

Najczęściej wykorzystuje się model trzeci, ponieważ równania COCOMO II – tak jak w innych modelach szacowania wysiłku – opierają się na rozmiarze oprogramowania, który łatwiej oszacować, mając gotowy projekt architektury. Omówimy teraz właśnie model postarchitektoniczny. Ma on postać

gdzie: E jest wysiłkiem w osobomiesiącach; A i B są stałymi skalibrowanymi na podstawie 161 projektów wynoszącymi odpowiednio 2,94 i 0,91; KLOC jest rozmiarem produktu wyrażonym w tysiącach źródłowych linii kodu; SF i (dla i = 1, 2, …, 5) są czynnikami skali;

EM j (dla j = 1, 2, …, 17) są współczynnikami pracochłonności. Dla przeciętnego projektu wszystkie mnożniki pracochłonności wynoszą 1, zatem dla takich projektów czynnik

można w modelu pominąć. Model COCOMO II definiuje 5 czynników skali: typowość, elastyczność,

zarządzanie ryzykiem, spójność zespołu oraz dojrzałość procesu wytwarzania. Dla każdego z nich wyznacza się w sześciostopniowej skali wpływ na projekt, od bardzo niskiego do ekstremalnie wysokiego. W zależności od poziomu, każdy czynnik przyjmuje określoną wartość, tak jak to przedstawiono w tabeli 44.8. Typowość określa, czy realizowany projekt jest typowy dla danej organizacji i czy zawiera elementy innowacyjności (np. skomplikowane, nowoczesne algorytmy, rozwiązania architektoniczne, specjalistyczne oprogramowanie). Elastyczność określa, czy istnieje potrzeba zgodności z wymaganiami oraz zewnętrznymi interfejsami. Zarządzanie ryzykiem bierze pod uwagę istnienie planu zarządzania ryzykiem, priorytetyzacji zadań, ustalania budżetu oraz innych narzędzi zarządzania. Kohezja zespołu określa stopień, w jakim poszczególni interesariusze projektu są w stanie efektywnie współpracować i komunikować się ze sobą. Dojrzałość procesu określana jest na podstawie poziomu dojrzałości modelu CMM. Tabela 44.8. COCOMO II – wagi czynników skali w zależności od ich wpływu

W pływ czynnika

Czynnik

Bardzo Bardzo Ekstrem Mały Średni Duży mały duży duży

PREC (typowoś ć)

6,20

4,96

3,72

2,48

1,24

0

FLEX (elas tycznoś ć)

5,07

4,05

3,04

2,03

1,01

0

RES L (architektura/zarządzanie 7,07 ryzykiem)

5,65

4,24

2,83

1,41

0

TEAM (kohezja zes połu)

5,48

4,38

3,29

2,19

1,10

0

PMAT (dojrzałoś ć proces u)

7,80

6,24

4,68

3,12

1,56

0

Biorąc pod uwagę cząstkowe czynniki skali, można łatwo pokazać, że wykładnik dla KLOC, czyli składnik

zawiera się w przedziale od 0,91 do 1,226. Im wartość ta bliższa jedności, tym bardziej liniowa zależność między rozmiarem projektu a wysiłkiem. Im bardziej oddala się na prawo od jedności, tym bardziej wykładniczy wzrost tej zależności. Mnożniki pracochłonności różnią się w zależności od przyjętej wersji modelu. Dla modelu postarchitektonicznego są to: ACAP (Analyst Capability) – zdolności analityka; APEX (Applications aplikacjami;

Experience)



doświadczenie

z

podobnymi

PCAP (Programmer Capability) – zdolności programisty; PCON (Personnel Continuity) – ciągłość personelu (np. brak rotacji w zatrudnieniu); PLEX (Platform Experience) – doświadczenie z podobną platformą (środowiskiem); LTEX (Language and Tool Experience) – doświadczenie w używaniu języka programowania i narzędzi; RELY (Required Software Reliability) – wymagana niezawodność systemu; DATA (Database Size) – rozmiar bazy danych; CPLX (Software Product Complexity) – złożoność produktu;

RUSE (Required Reusability) – wymagane reużycie komponentów; DOCU (Documentation Match to Life-Cycle Needs) dokumentacji z potrzebami wynikłymi z cyklu życia;



zbieżność

TIME (Execution Time Constraint) – ograniczenia na czas wykonania; STOR (Main Storage Constraint) – ograniczenia na przechowywanie danych; PVOL (Platform Volatility) – zmienność platformy; TOOL (Use of Software Tools) – użycie narzędzi; SCED (Required Development Schedule) – wymagany harmonogram rozwoju aplikacji; SITE (Multisite Development) – rozwój oprogramowania w wielu miejscach. Podobnie jak w przypadku czynników skali, dla mnożników pracochłonności określa się ich wpływ na projekt i z odpowiednich tabel odczytuje wartości odpowiednich współczynników EM j. Dokładne informacje na temat modelu COCOMO II (w tym dotyczące wartości współczynników mnożników pracochłonności) można znaleźć w [382]. Przykład. Zaczynamy nowy projekt deweloperski. Będzie on typowy w sensie mnożników pracochłonności, zatem pomijamy ten komponent w modelu. Szacujemy, że projekt będzie liczył 120,000 linii kodu. Jesteśmy raczej zaznajomieni z tego typu projektem (PREC = 2,48). Projekt w silnym stopniu wymaga zgodności z określonymi wymaganiami i standardami (FLEX = 5,07). W organizacji stosujemy w pełny sposób zarządzanie ryzykiem (RESL = 0). Zespół jest bardzo kooperatywny (TEAM = 1,10), a organizacja ma stopień dojrzałości CMMI 3 (PMAT = 3,12). Chcemy obliczyć wysiłek (w osobomiesiącach) dla tego projektu, wykorzystując model COCOMO II. Podstawiając uzyskane wartości do wzoru, dostajemy E = 2,94 ⋅ 1200,91 + 0,01 ⋅ (2,48 + 5,07 + 0 + 1,1 + 3,12) = 2,94 ⋅ 1201,0277 ≈ 403 os ⋅ mc Wysiłek związany z tym projektem wynosi 403 osobomiesiące.

44.7. Łączenie modeli i uwagi na temat estymacji

Zaleca się, aby w procesie szacowania wykorzystać co najmniej trzy niezależne metody. Przez niezależność należy tu rozumieć metody, które nie mają tych samych źródeł błędów. Na przykład, nie ma sensu wykorzystywać trzech modeli parametrycznych, ponieważ zasada działania każdego z nich jest taka sama. Przykłady metod komplementarnych to [372]: metody top-down i metody bottom-up; opinia ekspercka i metoda analogii; opinia ekspercka i metoda wykorzystująca proxy points; metoda proxy points i metoda analogii. Mając kilka szacunków pochodzących z kilku modeli, można ocenić je „inżynierskim okiem” i odrzucić te, których wyniki wydają się niewiarygodne. Pozostałe wyniki można uśrednić, wziąć ich medianę lub zastosować inną technikę uśredniania (patrz np. p. 19.6.4 i 22.2.4). Kiedy i jak często dokonywać estymacji? To zależy od projektu i przyjętej metodologii. Należy pamiętać, że sama estymacja jest czynnością niedostarczającą żadnego konkretnego, fizycznego produktu pracy. Należy więc przeznaczyć na nią tyle czasu, aby zmaksymalizować zyski, minimalizując straty (koszt i czas poświęcony na szacowanie). W typowych projektach estymację przeprowadza się trzy razy: w fazie początkowej – zgrubna makroestymacja; w fazie wymagań – dokładna, szczegółowa estymacja; w fazie projektu – zmodyfikowana, uszczegółowiona (ang. refined) estymacja. Na każdym etapie mogą istnieć inne wymagania dotyczące dokładności szacunków. Kierownictwo wyższego szczebla powinno rozumieć, że szacunki jeszcze przed rozpoczęciem projektu, w sytuacji, gdy nie ma żadnych danych historycznych, siłą rzeczy będą niedokładne (obarczone dużym błędem). Jeśli kierownictwo w takiej sytuacji żąda szacunków, to można: uświadomić kierownictwu, obarczone dużym błędem;

że

dostarczone

szacunki

mogą

być

poprosić o więcej czasu tak, aby zebrać odpowiednie dane i na ich podstawie przedstawić dokładniejszą estymację (np. po fazie

wymagań); podać wstępną estymację z zastrzeżeniem, że po odpowiednim czasie dostarczone zostaną dokładniejsze szacunki; podać estymację w postaci średniej i odchylenia standardowego (wymaga to dobrego zrozumienia przez kierownictwo istoty wariancji rozkładu prawdopodobieństwa).

1 Typowa różnica analogicznego projektu, który nadaje się do porównania wynosi ±25%. Jeśli jakiś projekt różni się o więcej niż 25% od analizowanego, to nie powinno się go brać pod uwagę w porównaniu. 2 Pojęcia tego nie należy mylić z produktywnością wyrażaną jako iloraz rozmiaru oprogramowania przez wysiłek. 3 Pakiety statystyczne mają tę przewagę nad programami takimi jak Excel, że potrafią

dopasowywać

dane

do

dowolnych

modeli,

nie

tylko

tych

predefiniowanych w arkuszu kalkulacyjnym. 4 Dobry model ma niewiele zmiennych (musi mieć mniej zmiennych niż danych, na których podstawie jest tworzony). Zawsze można model dopasować idealnie do danych, np. dla n punktów można znaleźć wielomian stopnia n –1, który będzie przechodził dokładnie przez te dane, jednak moc predykcyjna takiego modelu będzie niewielka (jeśli w ogóle jakakolwiek).

Więcej na: www.ebook4all.pl

45. Metryki i modele dla defektów

W rozdziale tym omówimy metryki dotyczące defektów. Na ich podstawie wyprowadzimy wiele modeli związanych z defektami. Modele te pozwalają na badanie efektywności usuwania defektów oraz szacowanie liczby pozostałych w kodzie usterek. Metryki i modele defektów są potężnymi narzędziami zarówno dla inżyniera jakości, jak i kierownika testów czy kierownika projektu. Umożliwiają m.in.: porównanie efektywności różnych faz, technik i metod testowania; modelowanie całkowitej efektywności usuwania defektów w zależności od efektywności poszczególnych faz; estymację, ile defektów zostało po danej fazie testowania; estymację czasu potrzebnego do osiągnięcia odpowiedniego poziomu jakości wyrażonego w terminach gęstości lub liczby pozostałych w kodzie defektów; obliczenie kosztu usuwania defektów w poszczególnych fazach i szacowanie kosztu wsparcia technicznego po wydaniu oprogramowania do klienta. Modele defektów możemy podzielić na dwie główne grupy: Modele statyczne defektów – oparte na danych historycznych (w tym danych z poprzednich faz bieżącego projektu). Są one bardzo przydatne w ocenie i przewidywaniu wpływu zmian w procesie na końcową

jakość produktu. Przykładami modeli statycznych są: model fazowy, model dwufazowy czy model efektywności usuwania defektów. Modele dynamiczne defektów – oparte na rozkładzie prawdopodobieństwa pojawiania się defektów, który jest budowany na podstawie danych o defektach. Są przydatne do szacowania tempa pojawiania się defektów w nadchodzących fazach, a także do estymacji liczby defektów pozostałych w kodzie, co umożliwia wyznaczanie optymalnego czasu trwania testów. Przykładami modeli dynamicznych są modele: Rayleigha, S-kształtny czy wykładniczy. Predykcja pozostałych w kodzie defektów (czyli tzw. defektów rezydualnych) jest możliwa nie tylko przy wykorzystaniu modeli dynamicznych. Dobrze sprawdza się tu również analiza mutacyjna (patrz rozdz. 45.5) oraz modele niezawodności (rozdz. 46). Idea szacowania liczby defektów

przyrostu

rezydualnych i w ogóle wykorzystania modeli predykcyjnych dla defektów pokazana jest na rysunku 45.1.

Rysunek 45.1. Wykorzystanie modeli defektów Każdy punkt na wykresie oznacza skumulowaną liczbę wykrytych do danego dnia defektów. Patrząc na wykres, podejrzewamy, że punkty te układają się

w krzywą zbliżoną do wykresu przeskalowanej dystrybuanty rozkładu wykładniczego: y(x) = N(1 – e–λx), gdzie y(x) to skumulowana liczba defektów w dniu x. Krzywa ta ma tę własność, że rośnie coraz wolniej i ma asymptotę y = N. Wartość N jest szacowaną całkowitą liczbą defektów w programie. Używając narzędzi statystycznych, dopasowujemy funkcję y(x) do zaobserwowanych punktów przez znalezienie parametrów N i λ. W naszym przypadku otrzymaliśmy1 N ≈ 386 i λ ≈ 0,4. Widać, że krzywa bardzo dobrze dopasowuje się do obserwowanych punktów, zatem mamy podstawy przypuszczać, że jest dobrym modelem predykcyjnym. Przewiduje on, że łącznie w produkcie znajduje się 386 defektów. Podstawiając odpowiednie wartości x do funkcji y = 386 ⋅ (1 – e–0,4⋅x) dostaniemy przewidywaną skumulowaną liczbę defektów, jakie powinniśmy wykryć do dnia x. Do dzisiaj wykryliśmy łącznie około 320 defektów, a planowany termin wydania oprogramowania to początek szóstego miesiąca projektu. Z modelu możemy obliczyć, że do tego dnia odkryjemy jedynie ok. 356 defektów. W kodzie pozostanie 30 defektów rezydualnych. Aby znaleźć je wszystkie (lub prawie wszystkie), projekt powinien trwać około 10 miesięcy. Jak widać, dobry model predykcyjny potrafi nie tylko szacować liczbę defektów rezydualnych, ale również pozwala określać zależność między terminem wydania a poziomem jakości oprogramowania. Jest również dobrym narzędziem kontroli postępu testów – w kolejnych dniach punkty oznaczające skumulowaną liczbę defektów powinny układać się wzdłuż wyliczonej krzywej. Jeśli układają się pod nią, oznacza to, że albo proces testowy stał się niewydolny, albo nasz model nie do końca dobrze przewiduje te wartości i być może należy go przekalibrować. Wreszcie, model taki pozwala zdefiniować metryki pomiaru zbieżności, czyli mierzące stopień osiągnięcia celu. W naszym przykładzie metryka taka mogłaby mieć postać Z = D/N, gdzie D jest dotychczas wykrytą liczbą defektów, a N to szacowana całkowita liczba defektów w produkcie. miara zbieżności (ang. convergence metric) – miara pokazująca postęp w osiągnięciu zdefiniowanego kryterium, np. zbieżność całkowitej liczby wykonanych testów do całkowitej liczby zaplanowanych testów

45.1. Natura defektów

Wbrew temu, co myśli większość początkujących testerów, defekty nie pojawiają się w losowych momentach ani miejscach systemu. Rządzą nimi dosyć wyraźne regularności, stanowiące swoiste „prawa przyrody” w inżynierii jakości oprogramowania. Dalej przedstawiamy niektóre z tych „praw”. Testerzy powinni być świadomi tych zjawisk – ułatwia to planowanie testowania, a także interpretację różnego rodzaju raportów dotyczących defektów czy w ogóle procesu testowego. Według Kana [200] w przypadku systemów operacyjnych ponad 95% defektów jest wykrywanych w ciągu 4 lat od wypuszczenia produktu na rynek. W przypadku pozostałych aplikacji większość defektów jest znajdowana zwykle w ciągu 2 lat od rozpoczęcia sprzedaży produktu. Informacja ta może być wykorzystana w metrykach, które wykorzystują pojęcie „całkowitej liczby błędów”. Całkowita liczba defektów nigdy nie jest znana, dlatego można ją szacować na podstawie sumarycznej liczby błędów znalezionych w oprogramowaniu właśnie w czasie 2 bądź 4 lat, zależnie od typu aplikacji. Na rysunku 45.2 jest przedstawionych symbolicznie kilka zjawisk dotyczących dynamiki oraz pojawiania się defektów. Wykres A pokazuje znaną nam już krzywą U-kształtną (ang. bath-tube) reprezentującą zależność między gęstością defektów a rozmiarem programu. Istnieje pewien optymalny rozmiar modułu, dla którego gęstość ta jest najmniejsza. Im większe odchylenie od tej wartości (zarówno w lewo, jak i w prawo), tym gęstość defektów rośnie. Na wykresie B przedstawiono bardzo ważną funkcję – krzywą Rayleigha. Jest to rozkład prawdopodobieństwa stanowiący podstawę tzw. modelu Rayleigha, który omówimy w podrozdziale 45.4. Krzywa Rayleigha pokazuje, jak zmienia się liczba znalezionych defektów w czasie, na przestrzeni wszystkich faz projektu. Na początku liczba znajdowanych defektów szybko rośnie. Po osiągnięciu maksimum liczba kolejnych znajdowanych defektów powoli, systematycznie spada. Część wykresu oznaczona strzałką z napisem

Rysunek 45.2. Dynamika i występowanie defektów – przykłady „FAZA TESTOWANIA” odnosi się do faz testowych. Ten fragment krzywej Rayleigha jest funkcją wykładniczą, która z kolei stanowi podstawę modeli wzrostu niezawodności omówionych w rozdziale 46. Na diagramie C w symboliczny sposób pokazano, że rozmieszczenie defektów w kodzie nie jest równomierne. Defekty bardzo często występują „w grupach”, obok siebie. Istnieje paradoksalna reguła mówiąca, że im więcej znajdziemy w jakimś module błędów, tym większe szanse, że znajdziemy ich w tym miejscu jeszcze więcej. Ta obserwacja często jest wykorzystywana do identyfikacji modułów podatnych na błędy, które należy przetestować dokładniej niż inne. Na diagramie D pokazano, że zależność między wysiłkiem (wyrażonym np. w osobotygodniach) a znalezionymi defektami jest mniej więcej liniowa. Aby znaleźć r razy więcej defektów niż zrobiliśmy to przy nakładach P ludzi w czasie T, powinniśmy zwiększyć p-krotnie rozmiar zespołu i wydłużyć q-krotnie czas tak, aby p ⋅ q = r.

45.2. Metryki defektów

Najczęściej wykorzystywanymi metrykami związanymi z defektami są: metryka liczby defektów; metryka gęstości defektów; metryka częstości defektów; metryka odsetka wykrytych defektów; wzorzec powstawania defektów. Liczba defektów. Pierwotna metryka liczby defektów opisuje liczbę defektów znalezionych w danym module, w danym czasie, w danej fazie procesu rozwoju oprogramowania itp. Na jej podstawie konstruuje się metryki złożone, takie jak gęstość defektów czy wzorzec powstawania defektów. Gęstość defektów. Jest to chyba najpowszechniejsza „techniczna” miara jakości oprogramowania. Mierzy liczbę defektów na jednostkę objętości kodu. Zdefiniowana jest następująco:

gdzie D jest liczbą wykrytych defektów, a V – jednostką objętości kodu. Najczęściej spotykanymi jednostkami są: linie kodu (LOC), tysiące linii kodu (KLOC) oraz punkty funkcyjne (PF). Liczba defektów zawsze dotyczy konkretnego miejsca programu (modułu lub grupy modułów) oraz czasu, w którym została zmierzona (określona faza cyklu życia). Miara gęstości defektów może zmieniać się w czasie, jeśli w trakcie użytkowania programu znajdowane są kolejne defekty, a w ramach utrzymania aplikacji następuje modyfikacja kodu źródłowego. Metryka gęstości uniezależnia nas od fizycznego rozmiaru oprogramowania. Używając jej, możemy porównywać jakość programów (modułów) niewielkich z tymi bardzo dużymi. Gęstość defektów służy również do porównywania kolejnych wersji oprogramowania w celu śledzenia postępów w redukcji defektów i zwiększania poziomu jakości. Na rysunku 45.3 pokazano typowy raport z takiego użycia metryki gęstości defektów. Częstość defektów (zwana również tempem pojawiania się defektów) to liczba wykrytych defektów na jednostkę czasu:

Rysunek 45.3. Gęstość defektów w kolejnych wersjach oprogramowania Częstość defektów jest miarą statyczną, związaną z konkretnym miejscem i czasem pomiaru. Dokładniejsze dane o jakości tworzonego produktu w ujęciu dynamicznym możemy uzyskać przez wielokrotny pomiar częstości w czasie, przez cały okres fazy testów. Uzyskamy w ten sposób wykres obrazujący tzw. wzorzec powstawania defektów. Jednostką czasu, według której skaluje się pomiar, jest najczęściej tydzień. Jednostki mniejsze (takie jak dzień czy godzina) są zbyt małe – pomiary dokonywane co godzinę czy co dzień cechują się znacznie większą wariancją niż dane zagregowane po tygodniach. Z kolei stosując jednostki większe (takie jak np. miesiące), otrzymamy niewiele pomiarów, jeśli np. testowanie trwa tylko kilka miesięcy. Metryka częstości jest wykorzystywana w modelach przyrostu niezawodności (patrz rozdz. 46). Odsetek wykrytych defektów. Metryka ta mierzy frakcję wykrytych defektów:

gdzie D to liczba dotychczas wykrytych defektów, a N – całkowita liczba defektów w produkcie. Aby móc obliczać tę metrykę, musimy znać wartość N. Niestety jest ona nieznana, dlatego możemy jedynie ją szacować. W kolejnych rozdziałach opiszemy modele pozwalające przewidywać tę wartość.

odsetek wykrytych błędów, odsetek wykrywania usterek (ang. Defect Detection Percentage, DDP; Fault Detection Percentage, FDP) – liczba usterek wykrytych w fazie testowania podzielona przez liczbę usterek wykrytych w całym cyklu życia oprogramowania (tzn. w fazie testów i później)

45.3. Modele statyczne defektów 45.3.1. Model wprowadzania/usuwania defektów Jest to prosty model oparty na równie prostym fakcie mówiącym, że niektóre czynności powodują wprowadzanie do produktu defektów, a inne – ich usuwanie. Jakość produktu możemy zwiększyć przez zmniejszenie liczby wprowadzanych defektów i zwiększenie liczby usuwanych defektów. Model jest schematycznie przedstawiony na rysunku 45.4. Wadą tego modelu jest to, że nie wiemy z góry, ile defektów zostało wprowadzonych w poszczególnych fazach. Wiemy tylko, ile defektów znaleźliśmy podczas weryfikacji i walidacji oprogramowania. Model wprowadzania/usuwania defektów jest wysokopoziomowy i służy raczej do zrozumienia idei procesu usuwania defektów niż do praktycznego, ilościowego wykorzystania w konkretnym celu. Na idei modelu wprowadzania/usuwania defektów jest oparty model fazowy opisany w punkcie 45.3.2. Bardziej szczegółowa wersja modelu wprowadzania/usuwania defektów jest pokazana na rysunku 45.5. Uwzględnia on często występujące w rzeczywistości sytuacje, w których defekt zostaje naprawiony nieprawidłowo.

Rysunek 45.4. Model wprowadzania/usuwania defektów

Rysunek 45.5. Model wprowadzania i usuwania defektów w pojedynczej fazie



Na początku fazy w programie istnieje określona liczba błędów. W trakcie fazy wprowadzane kolejne defekty. Część wprowadzonych defektów jest

naprawiona, ale niektóre naprawy są źle wykonane, co skutkuje pozostaniem defektu lub nawet pojawieniem się nowych defektów. Znakami plus i minus na rysunku oznaczono odpowiednio istniejące lub wprowadzone oraz usunięte defekty w danej fazie. Na podstawie tego modelu można wyprowadzić równanie opisujące efektywność fazy:

gdzie E, DU, DP, DWPR to odpowiednio: efektywność fazy, defekty usunięte w fazie (wartość ta jest pomniejszona o źle wykonane naprawy), defekty istniejące na początku fazy i defekty wprowadzone w fazie (na skutek prac deweloperskich lub źle wykonanych napraw). Równanie to niestety trudno zastosować w praktyce, ponieważ dokładne śledzenie wprowadzania i usuwania defektów, zwłaszcza związanych ze źle wykonanymi naprawami, jest bardzo trudne. Z doświadczeń niektórych autorów (np. [200]) wynika, że zazwyczaj odsetek źle wykonanych napraw w fazie testowania jest na szczęście znikomy i wynosi około 2%. Jeśli jednak w naszym procesie zaobserwujemy znacznie wyższą jego wartość, to należy opracować własne metryki związane ze śledzeniem tego zjawiska.

45.3.2. Model fazowy Jeśli potrafimy oszacować liczbę lub gęstość defektów wprowadzanych w kolejnych fazach (np. wykorzystując dane historyczne z poprzednich projektów, rozkład defektów wprowadzanych w kolejnych fazach), to możemy wykorzystać tzw. model fazowy do symulacji efektywności procesu testowania. Opisuje on relacje między liczbą wprowadzanych defektów, liczbą usuwanych defektów oraz efektywności poszczególnych faz. Efektywność fazy rozumiemy tutaj jako zdolność do wykrywania błędów. Efektywność równa n% oznacza, że spośród wszystkich defektów obecnych w danej fazie wykrytych zostanie n%. Model jest oparty na uproszczonym schemacie z rysunku 45.5 i zadany równaniem: DK = DP + DWPR – DU gdzie DK , DP, DWPR, DU to odpowiednio: defekty na koniec fazy, defekty istniejące na początku fazy, defekty wprowadzone w fazie, defekty usunięte w fazie. Model

ma postać tabeli prezentującej przepływ defektów między poszczególnymi fazami. Przykład jest pokazany w tabeli 45.1. Zamiast wartości DP, DWPR, DU i DK możemy użyć metryk gęstości tych defektów. Szare pola tabeli to te, w które wprowadzamy dane. Dotyczą one liczby (bądź gęstości) defektów wprowadzanych w kolejnych fazach oraz efektywności poszczególnych faz usuwania defektów. W fazie wymagań gęstość defektów wynosiła 1,6/KLOC. W fazie tej usunięto 75% defektów (1,2/KLOC), zatem na koniec fazy wymagań mamy 0,4 defektów/KLOC. Wartość ta przechodzi na początek fazy architektury, w której dodatkowo wprowadzono 7,3 defektów na KLOC. Efektywność tej fazy to 80%, zatem usunięto 80% ⋅ (0,4 + 7,3) = 6,16 defektów/KLOC. Na koniec fazy architektury mamy więc 7,7 – 6,16 = 1,54 defektów/KLOC. Obliczając te wartości dla wszystkich faz, otrzymujemy na końcu gęstość defektów na koniec fazy testów akceptacyjnych, czyli gęstość defektów polowych, równą 0,97 defektów/KLOC. Tabela 45.1. Wykorzystanie modelu fazowego

Efektywność fazy

Faza Wymag ania

1,60 1,60

75%

1,20 0,40

Architektura

0,40 7,30 7,70

80%

6,16 1,54

Projekt

1,54 5,80 7,34

67%

4,92 2,42

Kodowanie

2,42 12,5 14,92

43%

6,42 8,50

Tes towanie integ racyjne

8,5 2,10 10,60

52%

5,50 5,10

Tes towanie s ys temowe

5,10



5,10

62%

3,16 1,94

Tes towanie akceptacyjne

1,94



1,94

50%

0,97 0,97

Defekty polowe



0,97

Kierownik testów, planując jakość bieżącego produktu, może modyfikować wartości odpowiadające efektywności poszczególnych faz i sprawdzać, jak wpłynie to na poziom jakości końcowego produktu. Przy tego typu symulacji należy oczywiście wziąć pod uwagę różnego rodzaju ograniczenia, które nie pozwalają nam na uzyskanie stuprocentowej efektywności w każdej fazie (dostępność zasobów ludzkich, koszt, czas itp.).

45.3.3. Model dwufazowy Model dwufazowy, opracowany w 1979 roku, pochodzi od Remusa i Zillesa [383]. Choć jest modelem matematycznym, którego założenia rzadko kiedy są spełnione w rzeczywistych warunkach wytwarzania oprogramowania, dostarcza interesujących informacji dla kierownika jakości. Model dwufazowy opisuje zależności między efektywnością usuwania defektów, liczbą defektów wykrytych w początkowych fazach procesu (przed integracją kodu) oraz liczbą defektów wykrytych w późniejszych fazach (testy oraz defekty polowe). Model ma dwa kluczowe założenia: 1. Istnieją tylko dwie fazy usuwania defektów. 2. Efektywność usuwania defektów w obu fazach jest taka sama. Oznaczmy przez D1, D2, DKL, D odpowiednio: liczbę defektów znalezionych w fazie pierwszej, w fazie drugiej, znalezionych przez klienta po wydaniu produktu (defekty operacyjne) oraz całkowitą liczbę defektów w cyklu życia: D = D1 + D2 + DKL. Przez μ oznaczymy stosunek liczby defektów wykrytych w fazie pierwszej do liczby defektów wykrytych w fazie drugiej:

Zakładamy, że μ > 1. Im większe wartości μ, tym większa efektywność fazy początkowej. Z definicji efektywności obu faz mamy E1 = D1/D oraz E2 = D2/(D – D1). Z założenia o równości efektywności usuwania defektów, E1 = E2 oraz po dokonaniu prostych przekształceń otrzymujemy równania:

Równania te można wykorzystać w procesie planowania jakości produktu. Znając μ oraz jedną z wartości D1, D2, możemy oszacować liczbę DKL defektów

pozostałych w produkcie. Jeśli (np. na podstawie danych historycznych) stwierdzimy, że gęstość defektów jest zbliżona do poprzedniego projektu, to można przy ustalonym docelowym poziomie jakości wyznaczyć docelową wartość μ. Znając tę wartość, kierownik jakości może zastosować odpowiednie środki, narzędzia i metody, które pozwolą na osiągnięcie docelowej wartości μ (np. bardziej szczegółowe inspekcje kodu, wydłużony czas na testowanie określonych modułów). Rozważmy ponownie dane z tabeli 45.1. Załóżmy, że D = 29,3 defektu/KLOC2 oraz DKL = 0,97 defektu/KLOC. Wykorzystując przedstawione równanie, możemy obliczyć wartość μ:

Oznacza to, że jeśli efektywność usuwania defektów w obu fazach jest taka sama, aby uzyskać pożądany poziom jakości w fazie pierwszej, należy usunąć 5,5 razy więcej defektów niż w fazie drugiej (czyli w fazie testów). Niestety, model dwufazowy nie był szeroko badany empirycznie na rzeczywistych danych, głównie z powodu tego, że w prawdziwych projektach założenia modelu prawie nigdy nie są spełnione. Dlatego jego przydatność praktyczna, mimo pewnych korzyści teoretycznych, jest niestety nieznana.

45.3.4. Efektywność usuwania defektów i powstrzymanie fazowe Model efektywności usuwania defektów (ang. defect removal efficiency) bierze pod uwagę nie tylko to, w której fazie defekt został usunięty, lecz także to, w której fazie został wprowadzony. Informację tę przedstawia się w postaci macierzy defektów (patrz rys. 45.6A). Wiersze odpowiadają fazom wykrycia, kolumny – fazom wprowadzenia defektów. Na przecięciu wiersza x i kolumny y znajduje się liczba mówiąca, ile defektów wprowadzonych w fazie y zostało wykrytych w fazie x. Na przykład, w fazie projektowania wykryto 3 defekty wprowadzone w fazie wymagań i 36 defektów w samej fazie projektowania. Nie można usunąć defektu, zanim się go nie wprowadzi, dlatego macierz defektów nie ma wartości w polach (x, y), gdzie x jest fazą poprzedzającą fazę y. Suma wartości w poszczególnych wierszach określa całkowitą liczbę defektów wykrytych w danej fazie. Na przykład, dla macierzy z rysunku 45.6A w fazie wymagań wykryto 20 defektów, w fazie projektu 3 + 36 = 39 defektów itd. Sumy wartości w poszczególnych kolumnach oznaczają łączną liczbę defektów wprowadzonych w danej fazie. Na

przykład, dla macierzy z rysunku 45.6A w fazie wymagań wprowadzono łącznie 20 + 3 + 4 + 0 + 2 + 3 + 1 = 33 defekty.

Rysunek 45.6. Macierz defektów i obliczanie metryk PCE, DRE i Dunna W macierzy nie uwzględniliśmy faz testowania jako fazy wprowadzania defektów – zakładamy, że w fazach tych wprowadzanie defektów nie następuje. Oczywiście nie zawsze jest to prawdą – wtedy można zdefiniować macierz defektów, w której kolumny odpowiadają tym samym fazom, co wiersze. Na podstawie analizy danych z macierzy defektów można obliczyć kilka metryk efektywności usuwania defektów: Efektywność powstrzymania fazowego, PCE (ang. Phase Containment Effectiveness), zwane również efektywnością ograniczania defektów w fazie. Jest to obliczana dla ustalonej fazy F miara mówiąca, jaką frakcję defektów wprowadzonych w F udało się odkryć w F:

PCE F =

liczba defektów wprowadzonych i wykrytych w fazie F liczba ws zys tkich defektów wprowadzonych w fazie F

⋅ 100%

Mianownik metryki zawiera defekty z licznika plus pozostałe defekty wprowadzone w fazie F, ale znalezione w fazach następujących po F. Miara ta mówi, ile w danej fazie udało się wykryć defektów spośród wszystkich wprowadzonych w niej samej. Dotyczy więc defektów wprowadzonych tylko w jednej, ustalonej fazie, ale przy obliczaniu są brane pod uwagę wszystkie fazy wykrycia tych defektów. Sposób obliczania metryki PCE dla przykładowej macierzy z rysunku 45.6A jest pokazany na rysunku 45.6B. Na przykład, powstrzymanie fazowe dla fazy kodowania to liczba defektów wprowadzonych i wykrytych podczas kodowania podzielona przez wszystkie defekty wprowadzone podczas kodowania: PCEkod = 57/126 ≈ 45%. Analogicznie, mamy PCEwymagania = 20/33 ≈ 60% oraz PCEprojekt = 36/50 = 72%. powstrzymanie fazowe (ang. phase containment) – odsetek defektów, które zostały usunięte w tej samej fazie cyklu życia oprogramowania, w której zostały wprowadzone Całkowite powstrzymanie fazowe TDCE (ang. Total Defect Containment Effectiveness) traktuje wszystkie fazy cyklu życia (przed wydaniem) jak jedną. Jest to liczba defektów znalezionych przed wydaniem podzielona przez całkowitą liczbę defektów:

TDCE =

liczba defektów znalezionych przed wydaniem liczba ws zys tkich wykrytych defektów

⋅ 100%

Rozważmy ponownie przykład z rysunku 45.6A. W fazach wymagań, projektu i kodowania wykryto przed wypuszczeniem odpowiednio 32, 48 i 121 defektów, czyli łącznie 32 + 48 + 121 = 201 defektów. Zatem całkowite powstrzymanie fazowe wynosi TDCE = 201/209 ⋅ 100% ≈ 96%. Efektywność usuwania defektów DRE (ang. Defect Removal Efficiency). Metryka DRE jest liczona dla ustalonej fazy F. Jest ona ilorazem liczby defektów

znalezionych w fazie F podzielonych przez całkowitą liczbę defektów obecnych w trakcie tej fazy:

DRE F =

liczba defektów znalezionych i us uniętych w fazie F liczba ws zys tkich defektów is tniejących w trakcie fazy F

⋅ 100%

W przeciwieństwie do metryki PCE, metryka DRE może być obliczana dla każdej techniki testowania z osobna. Na przykład, jeśli w danej fazie były stosowane inspekcje oraz testy jednostkowe, dla każdej z tych czynności z osobna możemy policzyć DRE. Metryka DRE bierze pod uwagę wszystkie defekty, które mogły być znalezione w danej fazie F (nie tylko te, które w niej były wprowadzone, ale również te, które istniały przed fazą F). Metrykę DRE oblicza się bardzo prosto na podstawie macierzy defektów. Rozważmy ponownie przykład z rysunku 45.6A. Na rysunku 45.6C zilustrowano obliczanie DRE dla fazy projektowej. W fazie tej znaleziono łącznie 3 + 36 = 39 defektów. Mogły być one wprowadzone w fazie projektowej lub wcześniej (w tym przypadku – w fazie wymagań). W trakcie fazy projektowej łącznie w kodzie istniało więc 33 + 50 – 20 defektów (bo 20 zostało usuniętych wcześniej, w fazie wymagań). Aby policzyć, ile defektów istniało w trakcie fazy F, należy zsumować wszystkie wartości leżące w wierszu odpowiadającemu fazie F oraz dodać do nich wszystkie wartości leżące pod nimi. Dla naszego przykładu z rysunku 45.6A, DRE dla poszczególnych faz wynosi:

Efektywność fazy operacyjnej (dla defektów polowych) zawsze będzie wynosiła 100%, bo po niej nie następuje już żadna inna faza, w której można by wprowadzić defekty. Z kolei wartość metryki DRE dla fazy pierwszej (w naszym przypadku fazy wymagań) zawsze będzie równa metryce efektywności powstrzymania fazowego PCE dla tej fazy, ponieważ w fazie pierwszej możemy znaleźć wyłącznie defekty wprowadzone w tej fazie. Metryka DRE jest wykorzystywana w modelu fazowym omówionym w punkcie 45.3.2 jako efektywność usuwania defektów w fazie. Metryka Dunna D. Metryka Dunna [384] została zdefiniowana w 1987 roku jako miara efektywności danej fazy:

gdzie DEF F to liczba defektów znalezionych w fazie F, a DEF NF to liczba defektów znalezionych w fazach następujących po F. Zauważmy, że metryka ta różni się od metryki DRE tym, że w mianowniku uwzględnia także te defekty wprowadzone po zakończeniu fazy F, a więc te, których nie mamy szans wykryć w fazie F. Dlatego efektywność Dunna dla danej fazy zawsze będzie mniejsza lub równa niż efektywność usuwania defektów DRE. Sposób obliczania metryki Dunna dla naszego przykładu z rysunku 45.6A pokazany jest na rys. 45.6D. Metryka Dunna dla poszczególnych faz wynosi:

Metryki Dunna dla kolejnych faz będą takie same jak metryki DRE dla tych faz, ponieważ każda z nich obejmuje te same fazy wykrycia defektów. Używając metryki Dunna należy pamiętać, że nie da się osiągnąć stuprocentowej wartości tej metryki dla żadnej z faz oprócz końcowej, jeśli w fazach następujących są znajdowane jakieś defekty.

45.3.5. Modele zmian w kodzie Modele zmian w kodzie (ang. churn model) zostały opracowane w 2005 roku w firmie Microsoft [385]. Pozwalają one na przewidywanie gęstości defektów za pomocą specjalnie skonstruowanego zbioru metryk zmian w kodzie. Studium przypadku, przeprowadzone na produkcie Windows Server 2003, pokazało, że metryki te są bardzo dobrymi, wczesnymi predyktorami gęstości defektów w systemie. Metryka zmiany w kodzie określa ilość kodu, który został zmieniony w produkcie w określonym czasie. Informację taką łatwo uzyskać przez analizę historii zmian w systemie. Dane o zmianach mogą pochodzić np. z systemu kontroli wersji. Istnieją proste programy (np. unixowe polecenie diff) pozwalające w szybki, automatyczny sposób porównać dwa pliki ze sobą i określić stopień różnic między nimi. Model zmian w kodzie wykorzystuje relatywne metryki zmian, tzn. metryki znormalizowane na liczbę linii kodu, liczbę zmienionych plików, całkowitą liczbę plików itp. Okazuje się, że relatywne metryki zmian są lepszymi predyktorami gęstości defektów niż ich surowe, nieznormalizowane wersje. Można je również wykorzystać w modelu dyskryminacyjnym, tzn. modelu rozróżniającym moduły niezawierające defektów od pozostałych (ang. error-prone). Model wykorzystuje siedem surowych metryk: LOC (liczone jako niekomentowane, wykonywalne linie); zmienione LOC (suma dodanych i zmienionych LOC między dwoma wersjami programu); usunięte LOC;

liczba plików – liczba plików skompilowanych w celu uzyskania binarnej wersji programu; tygodnie zmian – całkowity czas, w którym plik był otwarty do edycji; liczba zmian – liczba wprowadzonych zmian między dwoma wersjami programu; zmienione pliki – liczba plików, które uległy zmianie. Na podstawie tych metryk wprowadzono osiem miar relatywnych:

M1 =

M3 =

M5 =

M7 =

zmienione LOC

M2 =

LOC zmienione pliki

M4 =

liczba plików tyg odnie zmian liczba plików zmienione pliki us unięte LOC

M6 =

M8 =

us unięte LOC LOC liczba zmian zmienione pliki

zmienione + us unięte pliki tyg odnie zmian zmienione + us unięte pliki liczba zmian

Najbardziej skorelowane z gęstością defektów okazały się metryki M 1 (ρ = 0,88), M 2 (ρ = 0,79), M 3 (ρ = 0,87) i M 5 (ρ = 0,73). Na podstawie miar relatywnych można budować modele liniowe postaci

gdzie G jest gęstością defektów w kodzie, a αi to współczynniki modelu. Jeśli nie chcemy włączyć pewnych metryk do modelu, to odpowiadające im współczynniki αi zerujemy. W Microsofcie przetestowano kilka modeli liniowych dla różnych podzbiorów metryk. Wyniki są przedstawione w tabeli 45.2. Tabela 45.2. Modele zmian w kodzie – porównanie3

Predyktory w modelu

R2

s tała, M2

0,592

1,909

s tała, M2, M3

0,685

1,678

s tała, M2, M3, M8

0,769

1,437

s tała, M1, M2, M3, M8

0,802

1,332

s tała, M1, M2, M3, M6, M8

0,808

1,313

s tała, M1, M2, M3, M5, M6, M8

0,810

1,306

s tała, M1, M2, M3, M4, M5, M6, M8

0,811

1,301

Błąd MS E 40

45.4. Modele dynamiczne defektów Modele dynamiczne opisują zmianę liczby (gęstości) defektów w czasie, wykorzystując w tym celu odpowiednie rozkłady prawdopodobieństwa modelujące ten proces. Mając zadany model oraz kilka początkowych punktów danych (np. dla kilku pierwszych tygodni czasu trwania projektu lub dla kilku początkowych faz), możemy dopasować parametry modelu do zebranych danych, co pozwoli na predykcję rozkładu liczby defektów w przyszłości. Istnieje wiele modeli dynamicznych, jednak my omówimy tylko kilka najważniejszych: model Rayleigha (dla całego cyklu życia), modele: wykładniczy i S-kształtny (opisujące tylko fazy procesu testowania) oraz model COQUALMO (oparty na COCOMO II) służący do predykcji defektów pozostałych w kodzie. Do zrozumienia materiału opisanego w tym rozdziale jest wymagana znajomość teorii prawdopodobieństwa, w szczególności pojęcia rozkładu (gęstości) prawdopodobieństwa oraz dystrybuanty rozkładu (patrz Dodatek C, a także np. [350]).

45.4.1. Model Rayleigha Model Rayleigha opisuje rozkład znajdowania defektów w czasie. Jest oparty na rozkładzie Rayleigha, którego funkcja gęstości f oraz dystrybuanta F mają postać

gdzie t jest zmienną oznaczającą czas (t ≥ 0), e jest podstawą logarytmu naturalnego, e ≈ 2,718, a c – współczynnikiem skali. Przykład rozkładu4 Rayleigha dla różnych parametrów skali jest pokazany na rysunku 45.7.

Rysunek 45.7. Model Rayleigha dla różnych wartości współczynnika skali Model Rayleigha jest szczególnym przypadkiem rozkładu Weibulla, którego gęstość i dystrybuanta mają postać:

gdzie m jest współczynnikiem kształtu, a c – współczynnikiem skali. Model Rayleigha to model Weibulla dla m = 2. Na rysunku 45.8 jest pokazany rozkład Weibulla dla kilku różnych wartości współczynnika kształtu m.

Rysunek 45.8. Rozkład Weibulla dla różnych współczynników kształtu W analizach niezawodności oprogramowania najczęściej wykorzystuje się rozkład Weibulla z parametrem m = 1 lub m = 2. W przypadku rozkładu Rayleigha niektórzy autorzy sugerują, by zamiast m = 2 przyjmować m = 1,8. Krzywa Rayleigha ma następującą własność: najpierw szybko rośnie, a po uzyskaniu maksimum spada do zera (tzn. funkcja f(t) dąży do 0 przy t → ∞). Możemy obliczyć argument tm, dla którego funkcja Rayleigha przyjmuje maksimum. Aby go znaleźć, należy rozwiązać równanie, w którym pochodną f po t przyrównuje się do zera:

skąd otrzymujemy

Pole powierzchni pod krzywą Rayleigha do momentu tm wynosi czyli około 40%. Oznacza to, że od początku projektu do czasu tm wykrytych zostanie około 40% z całkowitej liczby defektów. Jeśli znamy wartość tm, to możemy jednoznacznie wyznaczyć rozkład Rayleigha. Model Rayleigha jest rozkładem prawdopodobieństwa i dlatego pole powierzchni pod całą krzywą wynosi 1. W praktycznych zastosowaniach chcemy operować nie na prawdopodobieństwach, ale na liczbach defektów. Dlatego funkcję gęstości f(t) mnoży się przez całkowitą liczbę defektów D. Wykorzystując

wzór (*), możemy ponadto za c podstawić

i ostatecznie otrzymamy

następujący model Rayleigha:

Zawsze, gdy model Rayleigha jest tworzony na podstawie istniejących danych, parametry K oraz tm należy wcześniej oszacować. Podamy teraz przykład praktycznego wykorzystania modelu Rayleigha, na którym bardzo dobrze będzie widać korzyści, jakie można uzyskać z tego narzędzia. Załóżmy, że w pierwszych dziewięciu tygodniach projektu zbieraliśmy dane o znalezionych defektach:

Tydzień

1

2

3

4

5

6

7

8

9

Liczba defektów

20

41

48

52

62

59

52

44

33

Chcemy wykorzystać rozkład Rayleigha, aby modelować tempo pojawiania się defektów w kolejnych tygodniach projektu. W tym celu musimy znaleźć takie wartości K oraz c, aby krzywa Rayleigha parametryzowana tymi wartościami najlepiej dopasowała się do pierwszych dziewięciu punktów danych. Przykład ten pochodzi z [372]. Autorzy proponują kilka prostych metod oszacowania tych parametrów. Pierwsza z nich polega na znalezieniu tygodnia, w którym liczba defektów osiągnęła maksimum. Jest to tydzień piąty, zatem kładziemy tm = 5. Ponieważ wiemy, że do momentu osiągnięcia maksimum znajdziemy około 40% defektów, zatem suma defektów znalezionych w pierwszych pięciu tygodniach będzie stanowić mniej więcej 40% wszystkich defektów. Stąd Metoda druga polega na przyjęciu tm = 5, tak jak w poprzedniej metodzie, a następnie rozwiązaniu równania f(t) = Dt, gdzie Dt jest liczbą zaobserwowanych defektów w tygodniu t. Rozwiązując dziewięć takich równań dla t = 1, 2, …, 9, otrzymamy dziewięć różnych wartości K, które możemy uśrednić. W ten sposób otrzymamy wartość K = 491,2. Metody te nie są jednak poprawne ze statystycznego punktu widzenia w tym sensie, że wyniki będą wrażliwe ze względu na wartość tm. Niekoniecznie musi być tak, że wartość tm jest dokładnie równa 5. Być może wynosi ona 5,2, albo 4,91? Niestety dokładnej wartości nie znamy. Lepszą metodą jest podejście statystyczne. Wykorzystamy funkcję nls z pakietu statystycznego R, która służy do dopasowywania funkcji nieliniowych do danych. Na listingu 45.1 przedstawiamy

przykładowy przebieg sesji w programie R, służącej do znalezienia parametrów K oraz c krzywej Rayleigha. Zauważmy, że w metodzie tej nie musimy znać dokładnej wartości tm. Wartości obu nieznanych parametrów funkcji (K oraz c)

zostaną wyliczone automatycznie. Jedyne, co będziemy musieli podać, to ich przybliżone wartości, służące jako wartości początkowe procedury nls5. Pogrubione linie skryptu rozpoczynające się liczbą i znakiem „>” oznaczają polecenia wydane programowi R (liczby zostały dodane, aby łatwiej odnosić się do treści skryptu w dalszej części naszych rozważań). Pozostałe linie to zwracane przez program wyniki.

1> obs pred points points t d 1 1 20 2 3

2 41 3 48

4 4 5 5 6 6 7 7 8 8 9 9 10 10

52 62 59 52 44 33 NA

11 11 NA 12 12 NA 13 13 NA 14 14 NA 15 15 NA 5> plot(points$t, points$d, xlim=c(1, 15), ylim=c(0, 65), pch=16) 6> model summary(model)

Formula: d ~ K * 2 * (t/c^2) * exp(-(t/c)^2) Parameters: Estimate Std. Error t value Pr(>|t|) K 489.7381 13.2938 36.84 2.82e-09 *** c 7.0245 --Signif. codes:

0.1687

41.65 1.20e-09 ***

0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.977 on 7 degrees of freedom Number of iterations to convergence: 4 Achieved convergence tolerance: 3.137e-06 8> points$predVal points t d predVal 1 1 20 19.451786 2 3

2 41 36.608784 3 48 49.621468

4 5 6 7 8

4 5 6 7 8

52 62 59 52 44

57.411464 59.799303 57.419853 51.474341 43.407212

9 9 33 34.601170 10 10 NA 26.159044 11 11 NA 18.801191 12 12 NA 12.868915 13 13 NA 8.399807 14 14 NA 5.233792 15 15 NA 3.115583 10> lines(points$t, points$predVal, type=”o”, pch=1) Listing 45.1. Skrypt wykorzystujący model Rayleigha do predykcji defektów

W linii 1. definiujemy tzw. ramkę danych (czyli tabelę) o nazwie obs złożoną z dwóch kolumn o nazwach t oraz d. Wartości t oznaczają kolejne tygodnie, w których obserwowaliśmy dane i przebiegają od 1 do 9. Odpowiadające im wartości d to liczby zaobserwowanych defektów w poszczególnych tygodniach. Operator c(...) służy do zdefiniowania wektora liczb, a operator : jest operatorem zakresu, na przykład c(1:9)=c(1,2,3,4,5,6,7,8,9). W linii 2. definiujemy analogiczny zbiór danych pred dla tygodni 10–15, dla których będziemy chcieli przeprowadzić predykcję liczby wykrytych defektów. Na razie nie znamy tych wartości, więc w kolumnie d wstawiamy symbole NA (ang. not available), symbolizujące brak danych. W linii 3. pod zmienną points wstawiamy sklejone tabele obs i pred. W linii 4. wypisujemy zawartość tabeli points na ekran. W linii 5. rysujemy punkty danych na ekranie za pomocą polecenia plot. Pierwsze dwa parametry oznaczają współrzędne x oraz y naszych punktów. Opcjonalne parametry xlim i ylim definiują zakres wartości osi układu współrzędnych, a parametr pch odnosi się do typu rysowanych obiektów (w naszym przypadku będą to wypełnione czarnym kolorem punkty). Rezultat tego polecenia jest przedstawiony na rysunku 45.9.

Rysunek 45.9. Graficzna reprezentacja zaobserwowanych danych

Linia 6. jest kluczowa dla całej analizy. Tworzymy w niej model (pod zmienną o nazwie model), którego parametry będą dopasowane za pomocą funkcji nls. Oto jej poszczególne parametry: d ~ K*2*(t/c^2)*exp(-(t/c)^2)



formuła,

którą

chcemy

dopasować do danych. Jest to model Rayleigha pomnożony przez stałą K. Poza nią występuje tu jeszcze parametr c. Procedura nls sama znajdzie ich optymalne wartości tak, aby jak najlepiej dopasować krzywą Rayleigha do naszych dziewięciu obserwacji. data=points[1:9,]



parametr

data

określa

dane wejściowe.

W naszym przypadku jest to pierwszych dziewięć wierszy tablicy points, zawierających zaobserwowane liczby defektów w pierwszych dziewięciu tygodniach. Brak wartości po przecinku w notacji R oznacza, że zwracamy wszystkie kolumny tabeli points. start=list(K=450, c=8)



parametr

start

określa

wartości

początkowe dla szukanych parametrów. Suma defektów wykrytych w pierwszych 9 tygodniach wynosi 411, zatem szacujemy całkowitą ich liczbę na 450. Ponieważ widzimy, że funkcja osiąga maksimum mniej więcej w 5. tygodniu, zatem, korzystając ze wzoru , mamy c ≈ 7,07. Jako wartość początkową wybieramy c = 8. Oczywiście są to tylko przybliżenia, ale im bliższe prawdziwej wartości, tym szybciej metoda zakończy obliczenia. W linii 7. wypisujemy na ekran informacje o zbudowanym modelu. Widzimy, że estymowane wartości parametrów K i c to K ≈ 489 i c ≈ 7,02. Chcemy teraz wykorzystać model Rayleigha z tak oszacowanymi parametrami do predykcji defektów dla t = 10, 11, …, 15. W tym celu, w linii 8. dodajemy do tabeli points kolumnę predVal, a jako wartości wstawiamy tam estymowane z naszego modelu liczby defektów dla wszystkich tygodni. W linii 9. wypisujemy na ekran tę tablicę. Widzimy, że dla pierwszych 9 tygodni wartości przewidywane są bliskie wartościom zaobserwowanym, co znaczy, że model jest dobrze dopasowany (świadczy o tym również statystyczna istotność wartości estymowanych parametrów K i c widoczna w opisie modelu po wykonaniu linii 7. skryptu). Mamy również podane wartości dla tygodni 10–15. Możemy teraz narysować przewidziane przez model wartości. Po wykonaniu linii 10. otrzymujemy wykres

jak na rysunku 45.10 (polecenie lines nakłada na stworzony wcześniej rysunek inny wykres).

Rysunek 45.10. Predykcja na podstawie modelu Rayleigha Ostatecznie otrzymaliśmy model Rayleigha postaci

Przewidywana liczba defektów w tygodniach 10–15 to (po zaokrągleniu) odpowiednio 26, 19, 13, 8, 5, 3. Uzyskaliśmy bardzo cenną informację: przewidywaną dynamikę pojawiania się defektów w nadchodzących tygodniach. Model Rayleigha to potężne narzędzie. Oto kilka korzyści, jakie uzyskaliśmy z jego zastosowania: 1. Wiemy, czego się spodziewać w najbliższej przyszłości (czy w danym tygodniu wykrytych zostanie dużo, czy mało defektów) i jak będzie zmieniać się liczba wykrywanych defektów. Jeśli wartości rzeczywiste będą znacznie odbiegały od szacunków (np. będą dużo mniejsze), to możliwe, że proces testowania stał się nieefektywny i należy rozważyć przedsięwzięcie czynności zaradczych. 2. Znamy szacunkową całkowitą liczbę defektów w programie (K = 490).

3. Możemy w miarę dokładnie określić zależność poziomu jakości (w sensie liczby pozostałych w programie defektów) od czasu przeznaczonego na testowanie. Już po 9 tygodniu trwania projektu jesteśmy w stanie powiedzieć kierownikowi projektu, że jeśli np. proces testowy będzie trwał 12 tygodni, to wykryjemy około 467 defektów (suma predykcji dla tygodni 1– 12), zatem po wydaniu w programie zostanie 490 – 467 = 23 defekty. Efektywność procesu testowego wyniesie (490 – 23)/490 ≈ 95,3%. Jeśli jednak wydłużymy proces testowy jeszcze o 3 kolejne tygodnie, to w ciągu 15 tygodni znajdziemy ok. 484 defekty. Efektywność tego procesu wyniesie zatem ok. 484/490 ≈ 98,7%. Model Rayleigha jest znakomitym narzędziem symulacji i predykcji. Dostarcza cennych informacji, zwłaszcza wtedy, gdy musimy negocjować z właścicielami projektu zakres testowania oraz oceniać jego efektywność i wpływ na końcową jakość produktu. Badania empiryczne potwierdzają poprawność i przydatność modelu Rayleigha. W badaniach Putnama i Myersa [386] błąd oszacowania w modelu Rayleigha nie przekraczał 5–10%. IBM [200] wykorzystał model Rayleigha to predykcji defektów operacyjnych w szeregu projektów. Wyniki były zbieżne z rzeczywistymi. Thangarajan i Biswas [387] z powodzeniem zastosowali model Rayleigha w ponad 20 projektach. W 2001 roku Northrop Grumman IT [388] wykorzystało z sukcesem model Rayleigha do predykcji defektów w fazie testów na podstawie defektów znalezionych we wcześniejszych fazach cyklu życia (w jednym modelu szacowana wartość wynosiła rzeczywistej 15, w drugim – 1,44 wobec 1).

14,55 defektów wobec

Aby korzystać formalnie z modelu Rayleigha, jest konieczne przyjęcie następujących dwóch założeń: częstość defektów wykrytych podczas testowania jest dodatnio skorelowana z częstością defektów polowych, tzn. im więcej wykryjemy defektów w fazach testowych, tym więcej powinno być defektów wykrytych przez klienta; im więcej defektów wykrytych we wcześniejszych fazach, tym mniej ich zostaje w fazach późniejszych, tzn. im wcześniej krzywa defektów osiąga maksimum, tym szybciej opada w końcowych fazach cyklu życia.

W celu weryfikacji pierwszego założenia można zebrać dane o defektach dla pewnej liczby n komponentów K 1, K 2, …, K n. Dla komponentu i przez ti oznaczmy

defekty wykryte w fazach testowych, a przez p i defekty operacyjne (polowe).

Następnie badamy korelację według stopni Spearmana dla wektorów danych (t1, t2, …, tn) oraz (p 1, p 2, …, p n). Jeśli korelacja ta jest wysoka, to możemy przyjąć, że pierwsze założenie jest spełnione. Aby sprawdzić poprawność drugiego założenia, najlepiej przeprowadzić kontrolowany eksperyment na programistach (o zbliżonych umiejętnościach)

podzielonych losowo na dwie grupy – kontrolną i eksperymentalną. Najpierw wszyscy programiści tworzą jakąś zadaną funkcję, przy użyciu tych samych metod i tego samego procesu. Następnie obie grupy programują inną funkcję, przy czym grupa eksperymentalna kładzie nacisk na usuwanie defektów w początkowych fazach procesu, a grupa kontrolna używa tych samych metod, co w przypadku pierwszej funkcji. Obie funkcje muszą być porównywalne pod kątem złożoności i trudności opracowania. Jeśli częstość defektów w grupie eksperymentalnej jest niższa zarówno dla fazy testów, jak i fazy operacyjnej (i jest to statystycznie istotne), to uznajemy, że założenie drugie jest spełnione. W praktyce weryfikacja założeń w sposób jak to opisano powyżej jest trudna lub wręcz niemożliwa. Zazwyczaj założeń tych się nie sprawdza. Jak wspomnieliśmy wcześniej, niektórzy autorzy zalecają zamiast modelu Rayleigha wykorzystanie modelu Weibulla dla m = 1,8. Wykorzystując podejście statystyczne, można w ogóle przyjąć jako model wyjściowy rozkład Weibulla i dopasować również parametr m, zamiast przyjmować z góry określoną jego wartość.

45.4.2. Model wykładniczy i S-kształtny Narzędzia te służą do modelowania defektów w końcowych fazach cyklu życia, mianowicie w fazach testowania (gdy spojrzymy na prawą część wykresu krzywej Rayleigha, od momentu osiągnięcia maksimum, to zobaczymy, że jest to właśnie malejąca funkcja wykładnicza). Oczywiście nie będą odpowiednie w sytuacji, gdy np. przyjęliśmy metodykę zwinną wytwarzania i testowanie trwa od samego początku projektu. W takiej sytuacji lepiej użyć modelu Rayleigha. Rozkład wykładniczy oraz jego dystrybuanta mają postać f(t) = λt ⋅ e–λt F(t) = 1 – e–λt

gdzie λ jest parametrem. Tak jak w przypadku modelu Rayleigha, chcemy operować na liczbie defektów, zatem mnożymy prawe strony wzorów przez całkowitą liczbę defektów K: f(t) = K ⋅ λt ⋅ e–λt F(t) = K ⋅ (1 – e–λt) Gęstość rozkładu wykładniczego f oraz jego dystrybuanta F są pokazane na rysunku 45.11.

Rysunek 45.11. Rozkład wykładniczy – gęstość i dystrybuanta Dystrybuanta (skumulowana funkcja gęstości) służy do modelowania skumulowanej liczby wykrytych defektów. Modele te stosuje się dokładnie w taki sam sposób, jak model Rayleigha opisany wcześniej. Mając zbiór danych, dopasowujemy do niego model, np. przy użyciu skryptu z listingu 45.1. W zasadzie jedynymi rzeczami, jakie należy zmienić, są: formuła w definicji modelu w linii 6. skryptu, parametry (zamiast dwóch mamy tylko jeden, λ) i wartość początkową. Oprócz modelu wykładniczego stosuje się również modele S-kształtne. Nazwa pochodzi od ich kształtu – wydłużonej litery S. Wykres w początkowej fazie powoli rośnie, potem następuje gwałtowny wzrost i znowu spadek tempa wzrostu. Koncepcja modeli S-kształtnych opiera się na założeniu, że w początkowych fazach projektu defekty mogą być trudne do zlokalizowania. Wszystkie modele Skształtne opierają się na niejednorodnym procesie Poissona dla rozkładu zgłoszeń6. Przykładowy model S-kształtny może mieć postać F(t) = K(1 – (1 + λt)–λt) Jak zwykle, dystrybuanta odpowiedniego rozkładu została tu przemnożona przez całkowitą liczbę defektów K. Modele wykładnicze i S-kształtne częściej wykorzystuje się w analizie niezawodności, którą dokładniej omówimy w rozdziale 46.

45.4.3. Model COQUALMO Model COQUALMO (COnstructive QUALity MOdel) [389] jest modelem kosztowojakościowym rozszerzającym model COCOMO II omówiony w punkcie 44.6.2. Pozwala na ocenę wpływu różnych technik usuwania defektów i opisuje wpływ charakterystyk jakościowych projektu, produktu i środowiska na jakość końcową oprogramowania. Może być użyty w określaniu optymalnego czasu wydania produktu. Jednak z naszego punktu widzenia najciekawszą jego cechą jest możliwość szacowania liczby pozostałych w kodzie defektów. COQUALMO wykorzystuje ideę zawartą w modelu wprowadzania/usuwania defektów (patrz p. 45.3.1). Tak jak COCOMO II, model COQUALMO może być stosowany zarówno we wczesnych fazach cyklu życia (analiza i projektowanie), jak i w późniejszych, gdzie dysponujemy bardziej szczegółowymi danymi dotyczącymi projektu. COQUALMO składa się z dwóch podmodeli: podmodel wprowadzania defektów; podmodel usuwania defektów. Schematycznie zostały przedstawione na rysunku 45.12. COQUALMO wyróżnia 3 źródła wprowadzania defektów: wymagania, projekt i kodowanie oraz trzy aktywności służące usuwaniu defektów: przeglądy, analiza automatyczna i testowanie. Wejściem dla podmodelu wprowadzania defektów jest szacunkowy rozmiar oprogramowania oraz – tak jak w COCOMO II – czynniki wpływające na szacunki, związane z wykorzystywanym środowiskiem, umiejętnościami personelu itp. Wyjściem jest szacunkowa liczba nietrywialnych defektów. Przez defekty nietrywialne rozumie się defekty: o znaczeniu krytycznym (powodujące awarię systemu lub utratę danych); o znaczeniu wysokim (powodujące awarie poszczególnych funkcji systemu); o znaczeniu średnim (powodujące awarie jw., ale dające się rozwiązać). Nie wlicza się defektów o znaczeniu niskim (powodujące niewygodę czy irytację) oraz zaniedbywalnym.

Rysunek 45.12. COQUALMO – podmodele wprowadzania i usuwania defektów Wejściem do podmodelu usuwania defektów jest wyjście z poprzedniego modelu (czyli szacunkowa liczba wprowadzonych defektów), oszacowanie rozmiaru oprogramowania oraz tzw. profile usuwania defektów. Zalicza się do nich: dla aktywności analizy automatycznej: narzędzia analizy statycznej, analizatory kodu, składni, narzędzia weryfikujące poprawność wymagań, narzędzia weryfikacji modelowej (ang. model checkers) itp.; dla przeglądów: przeglądy nieformalne, przejrzenia, inspekcje itp.; dla testowania: debugery, testowanie ad hoc, narzędzia pokrycia kodu, wykorzystane techniki projektowania testów itp.

Od poziomu zaawansowania i sformalizowania powyższych profili zależą wartości współczynników, które wykorzystuje się w modelu. Równanie dla liczby wprowadzonych defektów ma postać

gdzie A j to współczynniki dla poszczególnych faz (tzw. DI Rate Adjustment Factors), Bj to współczynniki dla rozmiaru programu odpowiednio w fazach wymagań, projektu i kodowania, a DI_Driver ij (ang. Defect Insertion Driver) to i-ty czynnik odpowiadający za wprowadzenie defektów w fazie j-tej. Dla modelu usuwania defektów mamy równania (osobno dla każdego z 3 typów defektów: defektów wymagań, projektu i kodu) opisujące liczbę defektów rezydualnych:

gdzie to szacunkowa liczba defektów pozostałych w kodzie, Cj to stałe dla poszczególnych faz, DIj to szacunkowa liczba defektów wprowadzonych w fazie j, a DRF ij to frakcja usuniętych defektów dla i-tego profilu (automatycznej analizy, przeglądów i testowania) i j-tej fazy. Więcej szczegółowych informacji o modelu COQUALMO Czytelnik znajdzie w [390].

45.5. Analiza mutacyjna Analiza mutacyjna to metoda wykorzystująca testowanie mutacyjne (omówione w podrozdz. 9.15) do szacowania liczby pozostałych w kodzie defektów. Idea metody jest następująca. Załóżmy, że w procesie testowania wykryliśmy D defektów w kodzie. Następnie przeprowadzamy testowanie mutacyjne: stworzyliśmy M mutantów, z których M Z ≤ M zostało zabitych. Oznacza to, że odsetek wykrytych defektów symulowanych przez mutacje wynosi M Z/M, czyli tyle, ile pokrycie mutacyjne. Skoro tak, to możemy wnioskować, że D „prawdziwych” defektów również stanowi odsetek M Z/M wszystkich „prawdziwych” defektów w naszym programie. Zatem całkowita liczba defektów w kodzie wynosi D ⋅ M/M Z. Skoro wykryliśmy już D spośród nich, w kodzie zostało

defektów. Jeśli wykorzystane przez nas operatory mutacyjne dobrze naśladują defekty popełniane przez programistów, to kryterium pokrycia testów

mutacyjnych może służyć jako dobry predyktor liczby pozostałych defektów w programie. Na przykład, jeśli nasz zbiór testowy T doprowadził do wykrycia 240 defektów, a w procesie testowania mutacyjnego zabił 93% mutantów, możemy przyjąć, że 240 wykrytych przez T defektów to około 93% wszystkich defektów istniejących w programie. Zatem szacujemy, że w programie pozostało jeszcze

W testowaniu mutacyjnym może jednak zdarzyć się, że część wygenerowanych mutantów będzie równoważna oryginalnemu programowi. Jeśli jest to znacząca liczba i pojawia się w wielu stosowanych w naszej organizacji procesach testowania mutacyjnego, to możemy wziąć z tych pomiarów uśredniony odsetek i uwzględnić ten fakt w modelu. Załóżmy, że przeciętny odsetek mutantów równoważnych wynosi me. Wtedy wzór (*) przyjmie postać

ponieważ tylko (1 – me)M mutantów nie jest mutantami równoważnymi i tylko one mogą zostać zabite.

45.6. Metryki dynamicznej stylometrii Metryki te zostały zaproponowane w pracy [391]. Model dynamicznej stylometrii7 opiera się na założeniu, że gęstość defektów zależy nie tylko od miar statycznych kodu, takich jak LOC, PF czy złożoność cyklomatyczna, lecz także od sposobu, w jaki kod jest pisany przez programistę – w końcu defekty powstają bezpośrednio na skutek konkretnych czynności programistycznych. Jeśli da się ilościowo ująć taki proces dynamicznego tworzenia kodu, to można zbudować model przewidujący jakość kodu na podstawie procesu twórczego programisty. W pracy [391] wyróżniono następujące podstawowe akcje programisty: pisanie – odpowiada dodaniu do kodu pojedynczego znaku; grupowe pisanie – odpowiada jednoczesnemu pojawieniu się więcej niż jednego znaku w kodzie (np. przez operację kopiuj-wklej); usuwanie – akcja odwrotna do pisania; grupowe usuwanie – usunięcie więcej niż jednego znaku w kodzie naraz (np. przez zaznaczenie fragmentu kodu i naciśnięcie klawisza Delete);

zamiana – jednoczesne usunięcie części kodu i wstawienie na to miejsce innego kodu; utrata skupienia na edytorze – następuje, gdy użytkownik opuści środowisko programistyczne (np. przełączy się na okno przeglądarki, które staje się aktywnym oknem). Z akcjami jest związany pomiar czasu. Czas pojedynczej akcji jest bliski zerowemu, dlatego założono, że czas akcji A n to liczba milisekund, jakie upływają między wykonaniem akcji A n–1 a A n. Dla wszystkich opisanych akcji zdefiniowano następujące metryki: całkowita ilość czasu poświęcona na akcję danego typu; liczba akcji danego typu; średnia, odchylenie i mediana czasu danej akcji. Dla akcji grupowych zdefiniowano ponadto metrykę średniej długości zmienionego kodu. Aby uniezależnić się od bezwzględnego czasu pisania kodu, wprowadzono również metryki pochodne mierzące stosunek czasu poświęconego na wykonanie danej akcji do czasu pisania całego programu (w sumie 5 metryk). Dodatkowo zostały zdefiniowane dwie metryki mierzące to, czy programista pisał kod w sposób „ciągły”, czy też wykonywał podczas pisania wiele skoków w różne miejsca kodu. Pierwsza metryka bierze pod uwagę liczbę skoków oraz ich długość, druga natomiast – bezwzględną liczbę skoków. Ostatnią zdefiniowaną metryką jest wykorzystanie kodu. Jest to iloraz długości ostatecznego kodu do całkowitej ilości kodu, który był napisany w procesie tworzenia. Niska wartość tej metryki może świadczyć o słabym przygotowaniu programisty, a także niezrozumienie lub nieprzemyślenie architektury kodu. Po zdefiniowaniu metryk przeprowadzono kontrolowany eksperyment na grupie kilkudziesięciu studentów Instytutu Informatyki UJ. Polegał on na zaimplementowaniu w Javie interfejsu pewnej klasy. Programy były testowane i na podstawie wyników testów określano gęstość defektów w kodzie. Dla tak zebranych danych stworzony został model liniowy szacujący wartość tej metryki. Na podstawie opisanych zmiennych wybrano ich podzbiór, który dawał możliwie najlepsze przewidywanie gęstości defektów. Wyniki są przedstawione na rysunku 45.13.

Rysunek 45.13. Model dynamicznej stylometrii – predykcja gęstości defektów Model liniowy zawiera 11 zmiennych (współczynnik determinacji R2 = 0,931). Należy podkreślić, że nie zawiera on metryki LOC, PF, złożoności cyklomatycznej ani żadnej innej „statycznej” miary kodu powszechnie uznawanej za dobry predyktor gęstości defektów. Mimo to widać, że predykcja jest dosyć dobra. Wyniki eksperymentu są dosyć obiecujące i wydaje się, że warto prowadzić dalsze badania w tym obszarze. Metryki dynamicznej stylometrii mogą być dobrym predyktorem jakości kodu, w szczególności w połączeniu z wcześniej wspomnianymi miarami typu LOC, PF czy CC.

1 Praktyczne dopasowanie krzywej do obserwowanych danych przy użyciu pakietu statystycznego R pokażemy w punktach 45.4.1 oraz 46.3.1. 2 Wartość tę uzyskaliśmy dodając wszystkie defekty/KLOC z trzeciej kolumny tabeli 45.1. Będzie ona jednak prawdziwa tylko pod warunkiem, że metryki w poszczególnych fazach odnoszą się do tej samej objętości kodu, co nie musi być prawdą, bo np. w fazie testowania mamy już prawie gotowy kod, a w fazie wymagań kod jeszcze praktycznie nie istnieje (być może poza jakimiś prototypami). W takiej sytuacji rozmiar kodu się zmienia i nie można wtedy wprost dodać do siebie gęstości z poszczególnych faz, ponieważ mianowniki tych metryk mają różne wartości. 3 Błąd średniokwadratowy (ang. Mean Square Error, MSE) dla modelu liniowego o k parametrach zbudowanego na podstawie n obserwacji zdefiniowany jest jako gdzie yi to rzeczywiste wartości danych, a przewidziane przez model.

to wartości

4 W przypadku zmiennych losowych ciągłych rozkład prawdopodobieństwa oznacza to samo co funkcja gęstości. 5 Procedura nls jest metodą iteracyjną. W kolejnych przebiegach znajduje coraz lepsze przybliżenia szukanych parametrów. W tym celu musimy jej jednak podać jakieś wartości początkowe. Mogą być one wzięte „z głowy”, ale jeśli będą mniej więcej bliskie prawdziwym wartościom, to procedura będzie potrzebować mniej iteracji do tego, aby osiągnąć zbieżność i zakończyć swoje działanie. 6 Modele poissonowskie są wykorzystywane zwłaszcza w systemach kolejkowych, modelując np. rozkład nadchodzenia zgłoszeń do serwera, centrali telefonicznej. 7 Stylometria to nauka badająca w sposób ilościowy styl, w jakim napisano określony dokument. Nas interesuje dynamiczny aspekt tworzenia (a więc nie ostateczna postać kodu źródłowego, lecz sposób, w jaki on powstawał), stąd nazwa metody: dynamiczna stylometria.

46. Metryki i modele przyrostu niezawodności

46.1. Wprowadzenie Opisane w poprzednim rozdziale modele dotyczyły powstawania i usuwania defektów. Modele przyrostu niezawodności odnoszą się natomiast do awarii. Przypomnijmy, że awaria jest to widoczne dla użytkownika błędne działanie systemu (np. wypisanie złego wyniku, zawieszenie systemu, przerwanie działania systemu na skutek jakiegoś błędu). W teorii niezawodności ten atrybut jakościowy oprogramowania definiuje się jako prawdopodobieństwo bezawaryjnego działania systemu w określonych warunkach przez określoną ilość czasu. Przeanalizujmy poszczególne części tej definicji: prawdopodobieństwo – niezawodności nie da się zdefiniować jako konkretnej wartości; ma ona zawsze charakter probabilistyczny, ponieważ nie wiemy, kiedy dokładnie będą następowały awarie; potrafimy opisać je tylko w sposób statystyczny; bezawaryjne działanie – musimy umieć odróżnić awarię od innych form odchylenia programu od oczekiwanego stanu, np. od przestoju (ang. outage); czasami będą nas tylko interesować awarie o określonym poziomie dotkliwości; określone warunki – warunki, w jakich operuje program są istotnym czynnikiem wpływającym na niezawodność; określają one nie tylko środowisko, zasoby, rodzaj i wielkość przetwarzanych danych, lecz także intensywność oraz sposób użycia programu przez użytkownika, czyli profil operacyjny;

określony czas – podczas definiowania metryk niezawodności zawsze musimy określić czas, w jakim dokonujemy pomiaru. Niezawodność jest pochodną ciągłości usług, w odróżnieniu od omówionej w rozdziale 47 dostępności, która z kolei jest gotowością do użycia. Ciągłość usług jest zaburzona, jeśli następuje jakaś awaria. Im więcej takich awarii, tym gorsza ciągłość usług, a więc niższa niezawodność. Gotowość do użycia jest zaburzona, jeśli użytkownik musi długo czekać na naprawę awarii. Im krótszy czas awarii, tym gotowość do użycia większa, a więc i lepsza dostępność. Niezawodność oraz jej ilościowe ujęcie jest niezwykle atrakcyjną miarą nie tylko dla inżynierów oprogramowania, którzy mogą w ten sposób badać wzrost jakości tworzonego produktu, lecz także dla użytkowników, którzy oczekują wysokiego poziomu bezawaryjności zakupionych aplikacji. Modele wzrostu niezawodności mogą być użyte do estymacji bieżącego poziomu niezawodności oraz do predykcji tego poziomu w przyszłości. Ilościowe obliczanie niezawodności jest bardzo trudne, chociażby ze względu na następujące czynniki [392]: niepewność profilu operacyjnego – bardzo trudno jest symulować rzeczywisty sposób użytkowania oprogramowania; wysoki koszt generacji danych testowych – koszt ten jest tym większy, im więcej kryteriów pokryć chcemy spełnić; statystyczna niepewność danych o awariach – do dobrego opisu wzrostu niezawodności potrzeba danych o wielu awariach; małe zbiory tego typu danych oznaczają wysoką wariancję i niepewność konstruowanego modelu. Mimo tych trudności inżynieria niezawodności oprogramowania dostarcza metod i narzędzi pozwalających badać niezawodność praktycznie na wszystkich etapach cyklu życia oprogramowania. Ocena niezawodności składa się z dwóch zasadniczych czynności: estymacji niezawodności – czyli oceny tego, jak niezawodne jest oprogramowanie do tej pory; jest to metoda retrospektywna, wykorzystująca dane historyczne o zachowaniu się oprogramowania w przeszłości;

predykcji niezawodności – czyli szacowaniu przyszłego poziomu niezawodności. Ilościowe wartości metryk niezawodności są zwykle związane z czasem. Zwykle wyróżniamy trzy typy czasu: czas wykonania – aktualny czas procesora poświęcony na wykonanie określonego przypadku testowego; czas kalendarzowy – czas wyrażony w dniach, tygodniach, miesiącach lub latach, w którym program działa i jest wykonywany; czas zegarowy – bezwzględny czas działania programu, uwzględniający czas przestoju oraz wykonywania innych programów. Powszechnie przyjmuje się, że metryki wyrażone w czasie wykonania są bardziej wiarygodne i adekwatne w porównaniu do czasu kalendarzowego. Jednak najczęściej miary niezawodności i tak koniec końców odnosi się do czasu kalendarzowego, aby wyniki mogły być łatwo interpretowalne przez człowieka. Istnieją techniki pozwalające na „transformację” czasu wykonania na czas kalendarzowy (np. [393]). Mając zdefiniowany czas, możemy w różny sposób opisywać awarie. Typowe sposoby opisu to: skumulowana funkcja awarii – dla zadanego czasu t opisuje całkowitą liczbę awarii, która nastąpiła do czasu t (matematycznie rzecz ujmując, jest to po prostu dystrybuanta rozkładu zmiennej losowej opisującej pojawianie się awarii); funkcja intensywności awarii – opisuje stopień zmian skumulowanej funkcji awarii (matematycznie rzecz ujmując, funkcja intensywności jest pochodną skumulowanej funkcji awarii); funkcja częstości awarii – opisuje prawdopodobieństwo, że awaria nastąpi w przedziale czasowym [t, t + Δt] zakładając, że nie nastąpiła ona przed momentem t; MTTF (Mean Time To Failure), czyli średni czas do awarii – opisuje oczekiwany czas do następnej awarii.

w

Wszystkie te cztery miary są ze sobą związane i każdą z nich można wyrażać terminach pozostałych. Szczegółowe relacje między nimi opiszemy

w podrozdziale 46.2. Z punktu widzenia inżynierii jakości jest ważne, aby móc badać relacje między wzrostem niezawodności a kosztami ponoszonymi na ten wzrost (w końcu niezawodności nie dostajemy za darmo!). Niestety relacja ta nie jest do końca dobrze poznana. Istnieje kilka modeli dotyczących tego związku. Jednym z nich jest COCOMO II, który wykorzystuje niezawodność jako czynnik kosztu (RELY). Wartości tego czynnika mogą przyjmować wartości od 0,82 do 1,26. Wartość 1 jest nominalna, tzn. stosowana dla projektów przeciętnych. Jeśli koszt projektu szacowanego jako przeciętny wynosi K = 100 000 USD, ale w drodze dalszych analiz okaże się, że awarie mają jednak katastrofalny wpływ na system, czynnik RELY przyjmie wartość 1,26, co oznacza, że koszt stworzenia systemu wzrośnie do 1,26 ⋅ K = 126 000 USD. Drugi model pochodzi od Marcusa i Sterna [394]. Wprowadzają oni pojęcie indeksu niezawodności, które pokazuje logarytmiczną zależność między dostępnością systemu a kosztem. Zakładając, że R = e–t/MTTF oraz A = MTTF/(MTTF + MTTR), dostajemy R = e(A –1)t/(A ⋅MTTR), gdzie A jest dostępnością systemu a R niezawodnością. Jeśli tak obliczone wartości R skojarzymy z kosztami, jakie musieliśmy ponieść, aby osiągnąć poziom R, to powinniśmy otrzymać wykres taki, jak na rysunku 46.1.

Rysunek 46.1. Indeks niezawodności

Wniosek z tego taki, że od pewnego momentu wzrost nakładów na zapewnienie niezawodności powoduje coraz mniejszy wzrost tej niezawodności. Istnieje więc koszt, powyżej którego nie opłaca się już dalej inwestować w niezawodność, gdyż jej dalszy przyrost będzie nieznaczny. Indeks niezawodności dotyczy systemów i uwzględnia zarówno sprzęt, jak i oprogramowanie. Nie wiadomo, czy relacja jest taka sama w przypadku samego oprogramowania. Innym modelem, dającym podobną zależność logarytmiczną jest model Sha [395]: R = e–kCt/E, gdzie k jest stałą, C – złożonością systemu, a E dodatkowym wysiłkiem poświęconym na wzrost niezawodności. Nie wszystkie awarie traktowane są jednakowo – jedne są bardzo poważne, inne mogą być wręcz pominięte w analizie niezawodności. Zanim więc do niej przystąpimy, musimy określić poziomy dotkliwości awarii oraz które z nich będziemy brać pod uwagę. Dwie awarie powinny mieć przyporządkowaną tą samą klasę dotkliwości, jeśli mają podobny wpływ na system, środowisko, użytkownika itp. Zwykle definiuje się cztery klasy dotkliwości (1 = awarie najpoważniejsze, 4 = awarie najmniej istotne).

46.2. Matematyczne podstawy teorii niezawodności 46.2.1. Funkcja niezawodności i funkcja awarii Z matematycznego punktu widzenia niezawodność (na przedziale czasowym od 0 do t) definiuje się następująco: R(t) = P(T > t), t ≥ 0 gdzie T jest zmienną losową oznaczającą czas do awarii lub czas, w którym następuje awaria systemu. Niezawodność systemu przez okres czasu t to prawdopodobieństwo, że system nie ulegnie awarii do czasu t, tzn. że pierwsza awaria nastąpi po czasie t. Rozważmy funkcję F(t), zdefiniowaną jako prawdopodobieństwo, że jeśli system ulegnie awarii w czasie t to F(t) = P(T ≤ t), t ≥ 0 Funkcja F(t) jest zwana funkcją awarii. Jeśli f(t) jest gęstością prawdopodobieństwa zmiennej losowej T, to F jest jej dystrybuantą. Zachodzi również związek

W dalszej części omówimy najczęściej wykorzystywane w teorii niezawodności rozkłady prawdopodobieństwa zmiennej losowej T. Między funkcjami R i F zachodzi oczywisty związek R(t) = 1 – F(t)

46.2.2. Metryka MTTF (średniego czasu do awarii) Metrykę MTTF (średniego czasu do awarii) możemy teraz zdefiniować jako oczekiwaną ilość czasu, w którym system lub komponent działa bezawaryjnie:

Korzystając ze związku między funkcjami f, F i R można pokazać, że

Miara MTTF jest najpowszechniej stosowaną metryką niezawodności. Można ją wykorzystać, jeśli znamy rozkład czasów awarii.

46.2.3. Rozkłady prawdopodobieństwa modelujące występowanie awarii Najczęściej stosowanymi rozkładami prawdopodobieństwa modelującymi występowanie awarii są rozkłady: dwumianowy, Poissona, wykładniczy, normalny, Weibulla, gamma, beta, oraz logistyczny. Rozkłady dwumianowy i Poissona modelują liczbę awarii na określoną liczbę okazji do powstania awarii. Jeśli T ma rozkład dwumianowy, to funkcja niezawodności R(k) ma postać

gdzie R(k) oznacza prawdopodobieństwo, że co najmniej k spośród n elementów nie uległo awarii, a p jest prawdopodobieństwem awarii pojedynczego elementu. W przypadku rozkładu Poissona (z parametrem λ) mamy

gdzie R(k) to prawdopodobieństwo, że w czasie t nastąpi co najwyżej k awarii.

46.2.4. Rozkład wykładniczy i jego związek z metryką MTTF

Rozkład wykładniczy jest rozkładem ciągłym, modelującym długości interwałów czasowych między kolejnymi awariami, które nadchodzą zgodnie z jednorodnym rozkładem Poissona z parametrem λ. Mamy wtedy R(t) = e–λt gdzie R(t) jest prawdopodobieństwem, że kolejna awaria nie nastąpi przez okres czasu t. Gęstość rozkładu wykładniczego jest zadana wzorem f(t) = λe–λt Rozkład ten można zinterpretować następująco: załóżmy, że w chwili t0 nastąpiła awaria. Po jakim czasie nastąpi kolejna? Rozkład tego czasu to właśnie rozkład wykładniczy. Zauważmy, że czas, po którym nastąpi kolejna awaria nie zależy od momentu t0 wystąpienia pierwszej awarii. Ta własność rozkładu wykładniczego nazywa się tzw. brakiem pamięci (ang. memoryless). Formalnie można zapisać to następująco: jeśli zmienna losowa T ma rozkład wykładniczy, to P(T > t + s|T > t) = P(T > s) Innymi słowy, prawdopodobieństwo, że awaria nastąpi po czasie t + s pod warunkiem, że nie wystąpiła do czasu t jest równe prawdopodobieństwu, że awaria wystąpi po czasie s, licząc od chwili obecnej. Współczynnik λ, czyli parametr rozkładu, interpretuje się jako częstość awarii, którą można wyrazić jako odwrotność miary MTTF:

Można tę zależność uzasadnić w bardzo prosty sposób. Wiemy, że MTTF to wartość oczekiwana zmiennej losowej T. Jeśli T ma rozkład wykładniczy, to wtedy

Metrykę MTTF można obliczyć empirycznie, na podstawie danych o awariach. Załóżmy, że badany system w ciągu pierwszych 29 dni użytkowania ulegał awarii w dniach 2, 6, 7, 11, 18 i 24. MTTF jest średnim czasem między awariami. Musimy więc policzyć długości okresów między kolejnymi awariami i uśrednić je:

Średni czas między awariami wynosi 4 dni. Oznacza to, że intensywność awarii wynosi 1/4 dni, czyli średnio dziennie zdarza się ćwierć awarii (tzn. jedna awaria na 4 dni).

46.2.5. Funkcja częstości awarii oraz ryzyko (hazard rate) Jakie jest prawdopodobieństwo, że system ulegnie awarii w interwale czasowym [t, t + Δ], Δ > 0 pod warunkiem, że do czasu t nie nastąpiła awaria? Inaczej mówiąc: jakie jest prawdopodobieństwo, że pierwsza awaria pojawi się w interwale czasowym [t, t + Δ]? Prawdopodobieństwo to wyraża się wzorem

Jeśli znamy rozkład czasów między awariami, to możemy obliczyć prawdopodobieństwo, że system ulegnie awarii w danym czasie. Załóżmy, że rozkład ten jest wykładniczy z parametrem λ = 1/10, tzn. awarie zdarzają się średnio co λ–1 = 10 dni. Jakie jest prawdopodobieństwo, że system po raz pierwszy ulegnie awarii między dniem t1 = 5 a t2 = 6? Oznaczmy to zdarzenie przez X. Mamy a więc prawdopodobieństwo to wynosi około 5,7%. Częstość awarii jest tym samym prawdopodobieństwem, ale wyrażonym na jednostkę czasu: Częstość awarii = Druga równość wynika z własności prawdopodobieństwa warunkowego: P(A|B) = P(A ∩ B)/P(B). Funkcja ryzyka (ang. hazard rate), zwana też awaryjnością lub funkcją chwilowej częstości awarii, oznaczana jako z(t), jest zdefiniowana jako częstość awarii, w której długość rozważanego interwału zmierza do zera1:

Funkcja ryzyka jest chwilową (ang. instantaneous) częstością awarii w chwili t. W szczególności, prawdopodobieństwo, że poczynając od czasu t, system ulegnie awarii w ciągu Δt czasu wynosi z(t) ⋅ Δt. Jeśli chwilowa częstość awarii jest funkcją ciągłą, tzn. z(t) = λ, to implikuje ona wykładniczy rozkład awarii: f(t) = λe–λt. Jeśli chwilowa częstość awarii rośnie liniowo w czasie, z(t) = Kt, gdzie K jest stałą, K > 0, t ≥ 0, to awarie będą miały rozkład Rayleigha: W wielu sytuacjach chwilowej częstości awarii nie da się aproksymować funkcją liniową. W takiej sytuacji dyskutowane wcześniej modele (czyli postaci funkcji f) będą błędne, więc rozważa się awaryjność w tzw. postaci modelu Weibulla: z(t) = Ktm dla m > – 1. Gęstość awarii wyraża się wtedy wzorem

46.2.6. Prawdopodobieństwo awarii do czasu t Prawdopodobieństwo, że awaria nastąpi do czasu t to po prostu funkcja awarii F(t). Załóżmy, że czas między kolejnymi awariami ma rozkład wykładniczy z parametrem λ = 0,1/h, tzn. średnia częstość awarii to 0,1 na godzinę. Jakie jest prawdopodobieństwo, że w ciągu 5 godzin działania systemu nastąpi awaria? Obliczamy, podstawiając za t = 5, a za λ = 0,1: P(T ≤ 5) = F(5) = 1 – e–λ⋅t = 1 – e–0,1⋅5 = 1 – e–0.5 ≈ 1 – 0,6 = 0,4

Rysunek 46.2. Dystrybuanta rozkładu wykładniczego Funkcja awarii F (czyli dystrybuanta rozkładu wykładniczego dla λ = 0,1) jest przedstawiona na rysunku 46.2. Jak widać wartość F dąży do 1 wtedy, gdy t dąży do nieskończoności. Jest to oczywiste, ponieważ prawdopodobieństwo, że awaria nastąpi do czasu t, rośnie wraz ze wzrostem t. W granicy mamy F(+∞) = 1: prawdopodobieństwo, że awaria w ogóle kiedykolwiek nastąpi, jest zdarzeniem pewnym.

46.3. Modele przyrostu niezawodności Modele niezawodności (zwane również modelami wzrostu niezawodności lub modelami przyrostu niezawodności) dzielą się na dwie zasadnicze grupy, ze względu na obiekt, który modelują. Pierwsza (historycznie starsza) to grupa modeli, w których badaną zmienną jest czas między kolejnymi awariami. Czas

ten powinien ulegać wydłużaniu wraz z usuwaniem kolejnych defektów. W drugiej rodzinie modeli badaną zmienną jest liczba awarii lub usterek w określonym przedziale czasu. Przedział czasowy określa się z góry. Może być wyrażony jako czas kalendarzowy lub czas wykonania. Oczekuje się, że wraz z usuwaniem kolejnych defektów liczba awarii będzie maleć. Podstawowym parametrem szacowanym przez te modele jest liczba pozostających defektów lub awarii. Obie kategorie nie są rozłączne – istnieją modele potrafiące operować zarówno na czasie między kolejnymi awariami, jak i na liczbie awarii. Istnieje jeszcze trzecia grupa modeli, tzw. modele fenomenologiczne, w których zakłada się istnienie statystycznych związków między wartościami wskaźników niezawodności oprogramowania a wielkościami (parametrami) bezpośrednio charakteryzującymi to oprogramowanie, takimi jak miary LOC, PF, złożoność cyklomatyczna, punkty obiektowe itp. model przyrostu niezawodności, model wzrostu niezawodności (ang. reliability growth model) – model pokazujący poprawę niezawodności modułu lub systemu jako wynik poprawiania defektów w czasie W literaturze przedmiotu zaproponowano setki modeli wzrostu niezawodności. Pytanie, które z nich warto używać i które są najlepsze, jest wciąż pytaniem otwartym. Co gorsza, znakomita większość tych modeli istnieje tylko na papierze – ich poprawność nie została zweryfikowana w żadnych wiarygodnych badaniach empirycznych. Dlatego w dalszej części tego rozdziału omówimy tylko kilka klasycznych, najczęściej wykorzystywanych i – jak się wydaje – raczej wiarygodnych modeli. Możemy wprowadzić taksonomię modeli niezawodności, charakteryzując każdy z nich pięcioma atrybutami: 1. Czas (czas wykonania vs. czas zegarowy). 2. Kategoria (rozważany czas, w którym mogą zostać wykryte wszystkie awarie jest skończony lub nieskończony). 3. Typ (rozkład liczby awarii na jednostkę czasu; najważniejsze typy to rozkład dwumianowy oraz Poissona). 4. Klasa (tylko dla skończonego czasu z p. 2) – postać intensywności awarii w funkcji czasu. 5. Rodzina (tylko dla nieskończonego czasu z p. 2) – postać intensywności awarii w funkcji oczekiwanej liczby awarii.

Najczęściej spotykane modele należą do klasy o skończonej kategorii, w której intensywność awarii ma rozkład wykładniczy. Modele o typach dwumianowych charakteryzują się stałą funkcją ryzyka z(t) = λ. Ta chwilowa częstość awarii jest funkcją pozostałych w systemie defektów. Modele o typach poissonowskich również charakteryzują się stałą funkcją ryzyka i wykładniczym rozkładem intensywności awarii. Proces Poissona może być jednorodny lub niejednorodny2, dlatego liczba usterek pojawiających się w ustalonym przedziale czasu jest zmienną losową o rozkładzie Poissona. W tych modelach czas między awariami ma rozkład wykładniczy. Pierwsze dwa z omówionych modeli: Jelinskiego–Morandy oraz niedoskonałego debugowania Goela–Okumoto należą do rodziny modeli czasu między awariami. Trzy kolejne: niejednorodny model Poissona–Goela–Okumoto, model logarytmiczny Musy–Okumoto i model S-kształtny są modelami zliczającymi awarie.

46.3.1. Model Jelinskiego–Morandy Jest to jeden z najstarszych modeli przyrostu niezawodności [396]. W modelu Jelinskiego–Morandy czas między wystąpieniem dwóch kolejnych awarii ma rozkład wykładniczy, którego parametr λ jest proporcjonalny do liczby defektów rezydualnych. Model ma następujące założenia: na początku fazy testów w programie występuje N usterek; częstość wykrywania defektów jest proporcjonalna do ich bieżącej liczby w programie; częstość wykrywania defektów pozostaje stała między wykryciem kolejnych defektów; naprawa defektu jest natychmiastowa i nie powoduje wprowadzenia dodatkowych defektów; awarie powodowane usterkami pojawiają się w losowych momentach i są niezależne od siebie; wszystkie usterki mają jednakowy udział w powstawaniu awarii. Funkcja ryzyka (chwilowa częstość awarii) w modelu Jelinskiego–Morandy zależy od liczby dotychczas występujących awarii: z(ti) = λ(N – (i – 1))

gdzie ti jest dowolnym punktem czasu między pojawieniem się i – 1 oraz i-tej awarii. Funkcja ryzyka ma więc postać funkcji „kawałkami stałej”, która obniża się z każdą występującą awarią (patrz rys. 46.3).

Rysunek 46.3. Model Jelinskiego–Morandy – chwilowa częstość awarii Przeanalizujmy model Jelinskiego–Morandy dla sytuacji z rysunku 46.3. Zakładamy, że na początku fazy testów (t = 0) w programie istnieje 5 usterek. Rozkład czasów między awariami jest wykładniczy z parametrem λ = Φ(N – (1 – 1)) = 5Φ. Po czasie x1 (wylosowanym z tego rozkładu) wystąpiła pierwsza usterka.

Od tego momentu rozkład czasów między awariami ma rozkład wykładniczy z parametrem 4Φ. Z każdą kolejną awarią parametr zmniejsza się o współczynnik proporcjonalności Φ. Im więcej awarii nastąpi, tym – średnio – wydłuża się średni czas oczekiwania na kolejną awarię. Aby wykorzystać model, potrzebujemy dane o czasach między n kolejnymi awariami, x1, …, xn lub czasy awarii t1, …, tn, gdzie xi = ti – ti –1, t0 = 0. Formalnie,

jeśli Xi = Ti – Ti –1, i = 1, …, n są czasami między kolejnymi awariami, to zmienne Xi są niezależnymi zmiennymi losowymi o rozkładach wykładniczych z wartością oczekiwaną 1/Φ(N – (i – 1)) = 1/z(Xi|Ti – 1), tzn. Można pokazać, że estymatory największej wiarygodności (ang. Maximum Likelihood Estimation, MLE) dla Φ i N, oznaczone jako gęstości zmiennych xi :

i

i obliczone z łącznej

są rozwiązaniami równań

które powstają po zlogarytmowaniu funkcji L i przyrównaniu jej pochodnych ∂ln L/∂N i ∂lnL/∂Φ do zera (tak właśnie działa metoda estymatorów największej wiarygodności). Drugie równanie jest rozwiązywane technikami numerycznymi dla estymatora największej wiarygodności N, a jego rozwiązanie podstawiane do równania pierwszego, aby znaleźć estymator dla Φ. Estymatory te można wykorzystać do wyprowadzenia różnych metryk niezawodności. Na przykład, czas do awarii MTTF po wystąpieniu n awarii (czyli czas do pojawienia się n + 1szej awarii) można oszacować jako

Przykład. Załóżmy, że zaobserwowaliśmy następujące długości dziesięciu interwałów czasowych między kolejnymi awariami: 7, 11, 8, 10, 15, 22, 20, 25, 28, 35. Obliczymy estymatory dla całkowitej liczby awarii N oraz współczynnika Φ, a także oszacujemy średni czas do awarii dla następnej awarii. W tym celu wykorzystamy pakiet statystyczny R oraz jego bibliotekę maxLik zawierającą metodę MLE.

1> library("maxLik") 2> i x LL N Phi sum(log(Phi*(N-(i-1))))-sum(Phi*(N-(i-1))*x) 8> } 9> fit summary(fit) -------------------------------------------Maximum Likelihood estimation Newton-Raphson maximisation, 9 iterations

Return code 2: successive function values within tolerance limit Log-Likelihood: -37.80457 2

free parameters

Estimates: Estimate Std. error t value N 11.5964263 Phi 0.0096343

3.5454784 0.0066917

Pr(> t)

3.2708 0.001073 ** 1.4398 0.149937

--Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 -------------------------------------------Listing 46.1. Model Jelinskiego–Morandy – praktyczne wykorzystanie Omówimy teraz po kolei instrukcje skryptu z listingu 46.1. W linii 1. ładujemy bibliotekę maxLik. W linii 2. definiujemy wektor liczb (1, 2, 3, 4, 5, 6, 7, 8, 9, 10). W linii 3. definiujemy wektor x zawierający długości interwałów między kolejnymi obserwowanymi awariami. W liniach 4.–8. definiujemy funkcję LL, która jest logarytmem naturalnym z funkcji (*), czyli z łącznego rozkładu prawdopodobieństwa dla obserwowanych danych x1, …, x10. W linii 9. wywołujemy funkcję maxLik dla funkcji LL podając „na oko” początkowe wartości szukanych parametrów, N = 14 oraz Φ = 0,01. Wynik tej funkcji podstawiamy pod zmienną fit. Po wykonaniu linii 10. otrzymujemy informacje o wynikach działania metody MLE. Estymowane wartości to

Szacujemy więc, że w systemie na początku było około 12 usterek. Za pomocą estymatorów możemy obliczyć szacunkowy MTTF dla następnej awarii:

Model Jelinskiego–Morandy stał się inspiracją i przedmiotem badań wielu uczonych. Powstało wiele jego modyfikacji i rozszerzeń, np. wiele modeli Littlewooda, które rozróżniają rozmiar usterek i przyjmują ich różny wkład w napotykane awarie, co osłabia jedno z założeń modelu Jelinskiego–Morandy. Dzięki temu założeniu modele Littlewooda są bliższe rzeczywistości, bo zazwyczaj usterki o dużych rozmiarach rozpoznaje się i naprawia wcześniej niż usterki mniej istotne.

46.3.2. Model niedoskonałego debugowania Goela–Okumoto W modelu Jelinskiego–Morandy zakłada się, że czas naprawy defektu jest zaniedbywalny i że naprawa nie wprowadza żadnych dodatkowych defektów. W rzeczywistości założenie to nie jest spełnione. Model Goela–Okumoto uwzględnia możliwość niedoskonałego usuwania błędów. W modelu tym funkcja ryzyka ma postać z(ti) = λ(N – p(i – 1)) gdzie N jest całkowitą liczbą defektów istniejącą na początku fazy testowania, p to prawdopodobieństwo wykonania złej naprawy, λ oznacza częstość awarii przypadającą na usterkę, a ti jest dowolnym momentem czasowym między wystąpieniem i – 1-szej oraz i-tej awarii.

46.3.3. Niejednorodny model procesu Poissona Goela–Okumoto Model NHPP (ang. Non Homogeneous Poisson Process) jest oparty na zliczaniu zaobserwowanych awarii w określonych przedziałach czasu testów. Liczby te są reprezentowane jako niezależne zmienne losowe o rozkładzie Poissona. Model został wprowadzony przez Goela i Okumoto w 1979 roku [397] i stał się podstawą dla licznych modeli opartych na liczbie awarii, w tym modeli S-kształtnych. Model ma następujące założenia: skumulowana liczba awarii do czasu t podlega procesowi Poissona ze średnią wartością μ(t); liczba awarii f 1, f 2, …, f n wykrytych w kolejnych przedziałach czasowych (t0 = 0, t1), (t1, t2), …, (tn – 1, tn) jest niezależna dla dowolnego zbioru czasów t1 < t2 < ⋯ < tn. Ponadto są wymagane dwie rzeczy dotyczące danych: po pierwsze, dla każdego z rozważanych przedziałów czasowych (ti–1, ti) muszą być zbierane liczby awarii f i. Po drugie, każdy przedział czasowy podlegający obserwacji musi być zakończony (tzn. nie możemy np. przerwać obserwacji w połowie trwania ostatniego interwału czasowego). Równanie modelu ma postać

gdzie N(t) jest oczekiwaną liczbą awarii w czasie t, a λ(t) to częstość awarii. Z założeń modelu można pokazać, że średnia wartość μ(t) procesu musi mieć postać μ(t) = N(1 – ebt) gdzie b jest tempem wykrywania usterek podzielonym przez liczbę usterek, a N to oczekiwana całkowita liczba defektów, które zostaną wykryte3. Funkcja intensywności awarii λ(t) jest pochodną μ(t), zatem

Funkcja ta należy do rodziny rozkładów wykładniczych, możemy więc określić funkcję gęstości dla pojedynczej usterki X: f X(x) = be–bx Mamy zatem λ(t) = Nbe–bt = Nf X(t) co pokazuje związek między funkcją intensywności awarii a gęstością dla pojedynczej usterki. Z założenia mamy też, że zliczenia f i awarii w kolejnych interwałach czasowych są niezależnymi zmiennymi losowymi o rozkładach Poissona z średnimi μ(ti) – μ(ti – 1), i = 1, 2, …, n. Stąd możemy obliczyć łączny rozkład prawdopodobieństwa dla f i:

Wykorzystujemy metodę największej wiarygodności dla Estymatory

tego rozkładu.

dla prawdziwych wartości N i b wylicza się, rozwiązując układ

równań

Równanie drugie jest rozwiązywane ze względu na metodami numerycznymi, a rozwiązanie podstawiane do równania pierwszego, skąd otrzymujemy

Mając te dwa oszacowania, możemy wyliczyć estymatory dla kolejnych miar niezawodności:

MLE dla oczekiwanej liczby defektów wykrytych w n + 1-szym przedziale czasowym to

46.3.4. Logarytmiczny model Poissona czasu wykonywania Musy–Okumoto Model Musy–Okumoto [398] tak jak NHPP zakłada niejednorodność procesu Poissona liczby awarii zaobserwowanych w czasie t. Wykorzystuje jednak inną funkcję wartości średniej, która uwzględnia zmniejszający się w czasie wpływ defektów na proces i niezawodność produktu. Model wykorzystuje czas wykonania, a nie czas kalendarzowy, aczkolwiek istnieje metoda zamiany otrzymanego modelu na wyskalowany zgodnie z czasem kalendarzowym. Funkcja wartości średniej w modelu Musy–Okumoto ma postać

gdzie λ0 to początkowa częstość awarii, a θ jest znormalizowanym tempem redukcji częstości awarii na jedną awarię. Model logarytmiczny jest zalecany w przypadku, gdy wiadomo, że sposób użytkowania oprogramowania będzie niejednostajny. To sprawia, że wcześniej wykryte awarie mają większy wpływ niż te wykryte później i model dobrze to oddaje.

46.3.5. Model S-kształtny Model S-kształtny przyrostu niezawodności [399] należy do typu o rozkładzie gamma awarii. Liczba awarii na ustalony interwał czasowy jest jednak, tak jak w poprzednim modelu, typu poissonowskiego. Wartość średnia dla procesu Poissona skumulowanej liczby awarii wynosi μ(t) = N(1 – (1 + bt)e–bt), a, b > 0 gdzie t jest czasem, λ tempem wykrywania defektów, a N – całkowitą liczbą defektów. Model zakłada, że czas między i – 1-szą a i-tą awarią zależy od czasu do i – 1-szej awarii. Założenie to umożliwia modelowanie nie tylko wykrywania, lecz także procesu izolacji defektów. Ze względu na czas potrzebny na analizę awarii mogą powstać znaczące opóźnienia w czasie od pierwszej obserwacji usterki do jej pełnej identyfikacji. Modele S-kształtne można traktować jako uwzględniające

okres uczenia się, w którym testerzy zapoznają sie z oprogramowaniem (początek fazy) [200].

46.3.6. Inne modele niezawodności Jak wspomnieliśmy wcześniej, w literaturze zdefiniowano bardzo wiele modeli. Spośród tych, których nie omówiliśmy wcześniej, na uwagę zasługują następujące: model Schneidewinda [400], który zakłada, że bieżąca częstość awarii może być lepszym predyktorem przyszłego zachowania, niż częstości awarii obserwowane w dalszej przeszłości; model Musy podstawowego czasu wykonania [401], który jako pierwszy wykorzystuje czas wykonania zamiast czasu kalendarzowego; model hiperwykładniczy Ohby [402], który zakłada, że różne fragmenty oprogramowania mają różne chwilowe częstości awarii; model Weibulla, który potrafi modelować wzrastającą, malejącą lub stałą chwilową częstość awarii dzięki wykorzystaniu bardzo elastycznego (tzn. mogącego przyjmować bardzo różne formy) rozkładu Weibulla; model Duane’a [403], zakładający, że oprogramowanie nigdy nie będzie wolne od błędów (kategoria nieskończonego czasu według taksonomii Musy); model geometryczny [404], który od modelu Jelinskiego–Morandy różni się tym, że kolejne skoki spadku chwilowej częstości awarii nie są stałe, ale maleją w postępie geometrycznym, przez co model – tak jak model Duane’a – należy do kategorii nieskończonego czasu; modele bayesowskie – wykorzystujące analizę bayesowską, w której zamiast punktowymi estymatorami parametrów (np. chwilowej częstości awarii) operuje się rozkładami tych parametrów; jednym z lepszych modeli tego typu jest model wzrostu niezawodności Littlewooda–Verralla [405]; model Worwy [406], należący do rodziny modeli opartych na liczbie awarii, oparty na tzw. macierzy charakterystycznej programu; wskaźnik niezawodności jest tu funkcją wielkości charakteryzujących zarówno testowany program, jak i przyjęty sposób testowania, dzięki

czemu model może być wykorzystany w szczególności do oceny skutków realizacji procesu testowego według określonej strategii.

1 Wzór ten wynika z definicji na pochodną funkcji: pochodna F′ funkcji F w punkcie t to granica ilorazu różnicowego

przy Δ zmierzającym do

zera. Zauważyliśmy wcześniej, że pochodną funkcji awarii F jest gęstość rozkładu awarii f. 2 W jednorodnym procesie Poissona wartość λ jest stała przez cały czas trwania procesu. W niejednorodnym może się zmieniać, jest więc funkcją czasu λ(t). 3 Liczba N nie musi być całkowita, ponieważ jest wartością oczekiwaną.

47. Metryki i modele dostępności

47.1. Dostępność Dostępność jest pojęciem szerszym niż niezawodność – poza samym pojęciem awarii jest również związana z czasem naprawy (MTTR) oraz strategią prowadzenia serwisu technicznego. W powszechnym rozumieniu dostępność oznacza sprawne działanie systemu w okresie korzystania z niego [200]. W kontekście inżynierii jakości oraz pomiarów dostępności, charakterystyka ta zdefiniowana jest ilościowo jako procent czasu, w którym system może być używany. Metryka dostępności systemu dobrze reprezentuje punkt widzenia klienta. Obecnie, w erze powszechnego internetu, usług sieciowych oraz skomputeryzowania niemal wszystkich aspektów życia ludzkiego wysoka dostępność systemu staje się wymaganiem kluczowym dla jakości oprogramowania czy serwisów internetowych. Od wielu serwisów biznesowych oczekuje się tzw. obsługi w schemacie 7×24 czy 365×24 (tzn. 7 dni w tygodniu, 24 godziny dziennie bądź 365 dni w roku, przez cały czas). Wyłączając zaplanowane z góry okresy serwisowe systemów, schematy takie wymagają nieprzerwanej dostępności systemu. Czy dostępność rzędu 99,99% jest wystarczająca? To zależy. W tabeli 47.1 przedstawiono przełożenie wybranych wartości dostępności na okres niedostępności w roku. Jeśli system rezerwacji biletów kolejowych ma dostępność rzędu 99,5%, oznacza to, że w ciągu roku będzie niedostępny przez ok. 44 godziny, czyli łącznie przez niecałe dwa dni. Jest to do przełknięcia. Jednocześnie, roczny obrót giełdy NASDAQ to 20 bilionów USD. Jeśli system giełdowy NASDAQ cechować się będzie dostępnością rzędu 99,9999%, to będzie niedostępny tylko przez 32 sekundy.

Zakładając dla uproszczenia, że sesje giełdowe trwają nieprzerwanie przez cały rok a obroty rozkładają się równomiernie w czasie, te tylko 32 sekundy niedostępności spowodują straty w wysokości 2 000 000 000 USD, czyli dwóch miliardów dolarów! Niedostępność systemu ma również wpływ na postrzeganie usługodawcy. Bardzo często można przeczytać w prasie o wściekłych klientach tego, czy innego banku, którzy Tabela 47.1. Przykładowe dostępności systemu i odpowiadające im czasy niedostępności

Dostępność systemu [%] W schemacie 365x24

Okres niedostępności w roku

99,9999

32 s ekund

99,999

5,3 minut

99,99

52,6 minut

99,95

4,4 g odzin

99,90

8,8 g odzin

99,80

17,5 g odzin

99,70

26,3 g odzin

99,50

43,8 g odzin

99,0

3,6 dnia

98,5

5,47 dnia

98,0

7,3 dnia

97,5

9,12 dnia

95,0

18,25 dnia

nie mogli skorzystać z systemu bankowości elektronicznej, bo był on niedostępny przez jedną czy dwie godziny. Takie awarie narażają banki czy innych usługodawców na utratę zaufania, co może doprowadzić do odpływu klientów a nawet bankructwa.

47.1.1. Dostępność ciągła a dostępność wysoka W przypadku systemów działających według schematu 7×24 (czy 365×24) mówi się o dostępności ciągłej. Dla mniej restrykcyjnych schematów oczekuje się dostępności podczas godzin, w których system jest używany (np. bankomat w placówce banku zamykanej między 22:00 a 6:00). W takich przypadkach dopuszczalna jest niedostępność systemu poza „godzinami pracy systemu”, czy to spowodowana planowanymi, czy nieplanowanymi czynnościami pielęgnacyjnymi lub naprawczymi. W takim przypadku mówimy o dostępności wysokiej. Przyjęcie określonej definicji dostępności (ciągła lub wysoka) implikuje inne wartości metryki dostępności. Rozważmy przykład systemu, który w trakcie działania przez jeden miesiąc (liczący 720 godzin) był niedostępny przez 7 godzin z powodu awarii systemu oraz przez 20 godzin ze względu na planowe czynności pielęgnacji systemu. Osiągnięte metryki dostępności będą wynosić odpowiednio: Dciągła Dwysoka

47.1.2. Metody zwiększające dostępność systemu Dostępność jest jedną z najważniejszych cech jakościowych w systemach krytycznych oraz dostarczających usługi masowym odbiorcom, dlatego powinna w szczególny sposób być analizowana pod kątem ryzyka wystąpienia nieprzewidzianych przestojów w pracy systemu. Z biegiem czasu opracowano wiele metod, zarówno sprzętowych, jak i programowych, zwiększających niezawodność i dostępność systemu, np. [200]: RAID (ang. Redundant Array of Independent Disks), czyli redundantne macierze dyskowe; mirroring (kopie zawartości zasobów przechowywane na innych serwerach);

zapasowe zasilanie (np. UPS); redundantny zapis pamięci cache; nieustanne zapisywanie informacji (streaming); jednoczesny serwis; jednoczesna aktualizacja wersji; jednoczesne wprowadzanie poprawek; równoległe zapisywanie/odczytywanie; przyspieszanie resetu systemu; niezależne pomocnicze magazyny danych (I-ASP); partycjonowanie logiczne; wykorzystanie klastrów pracy awaryjnej; serwis na odległość; odległe węzły klastrów. Według badań przeprowadzonych przez Kana [200] w znaczącej większości projektów awarie wpływające na dostępność powodowane były przez wadliwie działające oprogramowanie. Jones [407] podaje zależność między gęstością defektów a wartościami metryki średniego czasu do awarii MTTF (badania opublikowano w 1991 roku), przedstawioną w tabeli 47.2. Tabela 47.2. Związek między gęstością defektów i wartością MTTF

Gęstość defektów (defekty/KLOC)

Przeciętna wartość MTTF

> 30

< 2 minuty

20–30

4–15 minut

10–20

5–60 minut

5–10

1–4 g odzin

2–5

4–24 g odzin

1–2

24–160 g odzin

0. Im mniejsza, tym wydajniejszy proces wykonania testów. Średni czas tworzenia przypadku testowego. Mierzy czas projektu/implementacji przypadku testowego. Przydatna w planowaniu projektu testowego.

gdzie Ti to czas spędzony na projekt bądź implementację itego przypadku testowego a N to liczba wszystkich przypadków testowych. Implementacja odnosi się do automatycznych przypadków testowych (np. tworzenie skryptu). M > 0. Im mniejszy, tym wyższa wydajność procesu projektu/implementacji testów. Metryka może być wyrażana również w jednostkach wysiłku, jako czasochłonność, np. w osobodniach. Średni czas zamknięcia defektu. Mierzy efektywność zespołu deweloperskiego w usuwaniu defektów.

gdzie TRi to czas zamknięcia i-tego defektu, TZi to czas zgłoszenia i-tego defektu przez testera, a N to całkowita liczba

zgłoszonych defektów. Śledzenie tej metryki w czasie może ujawnić np., że proces usuwania defektów jest wąskim gardłem. M > 0. Im mniejsza, tym lepiej. Średni czas testów. Mierzy czas trwania określonej fazy testów na jednostkę objętości kodu. M = T/R, gdzie T jest czasem między kamieniami milowymi projektu określającymi początek i koniec określonej fazy testów, a R jest rozmiarem oprogramowania (np. w KLOC, PF, liczbie wymagań). Zamiast czasu można użyć miary wysiłku, np. czasochłonności. Odsetek błędnych testów. Mierzy jakość procesu tworzenia testów. M = B/N, gdzie B to liczba wadliwych testów (wymagających poprawy), a N to liczba wszystkich testów. M ∈ [0, 1]. Im mniejsza, tym lepiej. Współczynnik kosztu jakości. Mierzy efektywność procesu usuwania defektów. M = TS /TD, gdzie TS to całkowity czas spędzony na testowaniu statycznym (przeglądy, inspekcje), a TD to całkowity czas spędzony na testowaniu dynamicznym (testy jednostkowe, integracyjne itd.). M ≥ 0. Im większa wartość metryki, tym wydajniejszy będzie proces usuwania defektów (ale jednocześnie tym bardziej kosztowne będzie testowanie, ponieważ inspekcje są drogie).

Względny wysiłek testowy. Mierzy wysiłek poświęcony na testy w stosunku do całkowitego wysiłku. M = ET/EP, gdzie ET jest całkowitym wysiłkiem poświęconym testowaniu a EP to całkowity wysiłek poświęcony projektowi.

Metryka jest wyrażona w jednostkach pracochłonności (np. osobotydzień), ale może być również wyrażona w jednostkach czasu. Efektywność testów. Mierzy stosunek liczby wykrytych defektów do liczby wykonanych testów. M = D/T, gdzie D to liczba wykrytych defektów, a T to liczba wykonanych testów. Duże wartości metryki oznaczają, że mała liczba testów jest w stanie wykryć wiele defektów, zatem testy te cechują się wysokim prawdopodobieństwem wykrycia defektów. Niskie wartości metryki wskazują, że tylko mała liczba testów wykrywa jakieś defekty – większość testów jest słaba. M ≥ 0. Im wyższa, tym lepsze testy. W obliczaniu metryki powinno brać się pod uwagę tylko defekty o wysokiej dotkliwości (o wysokim priorytecie). Efektywność procesu testowego. Mierzy stosunek liczby wykrytych defektów do wysiłku testowego. M = D/ET, gdzie D to liczba wykrytych defektów, a ET to

wysiłek (lub czas) poświęcony na testowanie. M ≥ 0. Im większa wartość M tym bardziej proces testowy skupia się na wykrywaniu defektów. W obliczaniu metryki powinno brać się pod uwagę tylko defekty o wysokiej dotkliwości (o wysokim priorytecie). Poziom automatyzacji. Mierzy frakcję testów automatycznych. M = A/N, gdzie A jest liczbą testów automatycznych, a N to liczba wszystkich testów. Nie zawsze warto i należy dążyć do automatyzacji wszystkich testów (patrz rozdz. 30 w części dotyczącej automatyzacji). M ∈ [0, 1]. Zazwyczaj istnieje optymalna (na dany moment cyklu życia) wartość M gdzieś w środku tego przedziału. W rozdziale 8 i 9 zdefiniowaliśmy ponadto metryki dotyczące pokrycia elementów testowych. Postaci tych metryk zależą od stosowanej techniki projektowania testów. Jeśli stosujemy modele predykcyjne, możemy wykorzystać metryki mierzące dokładność naszych szacunków, np.:

Dokładnoś ć s zacowania wys iłku =

Rzeczywis ty wys iłek – S zacowany ⋅ wys iłek 100% S zacowany wys iłek Rzeczywis ta liczba tes tów – S zacowana liczba tes tów

Dokładnoś ć s zacowania tes tów =

Dokładnoś ć s zacowania czas u =

S zacowana liczba tes tów

⋅ 100%

Rzeczywis ty czas tes tów – S zacowany ⋅ czas tes tów 100% S zacowany czas tes tów

Jeśli któraś z tych metryk systematycznie przyjmuje wartości ujemne, oznacza to, że dana wartość jest przeszacowana w naszej estymacji. W kolejnych szacunkach można uwzględnić tę wartość, aby zwiększyć dokładność szacowania. Na przykład, jeśli w 4 kolejnych projektach dokładność szacowania testów wynosiła –15%, –20%, –15% oraz –18%, to średnio szacunki nasze w każdym z tych projektów były zaniżone o 17%. Jeśli w kolejnym projekcie model przewiduje szacowaną liczbę testów na x, to zamiast x możemy przyjąć x + 17% ⋅ x = 1,17x. Metryki dla testów mogą być bardzo przydatne w planowaniu i organizacji projektu testowego. Załóżmy, że z poprzednich projektów mamy następujące dane historyczne: Średnia czasochłonność projektu i implementacji przypadku testowego = 0,4 osobodnia. Średni czas testów = 10 dni/KLOC. Względny wysiłek testowy = 0,55 (czyli 55% wysiłku w projekcie dotyczyło testowania). Efektywność testów = 0,2 (czyli na 5 testów był wykrywany 1 defekt). Zakładamy, że wartości każdej z metryk były podobne w kolejnych projektach, tzn. ich wariancja była niewielka. Szacujemy, że nowy projekt będzie liczył ok. 12 KLOC. Zespół testowy liczy 3 testerów. Z poprzednich projektów wiemy, że gęstość defektów średnio wynosiła 11 defektów/KLOC. Na podstawie tych informacji możemy oszacować wiele przydatnych parametrów projektu, np.: oczekiwana liczba defektów w nowym projekcie to

biorąc pod uwagę efektywność testów, potrzeba 132/0,2 = 660 testów do wykrycia wszystkich defektów; średnia całkowita czasochłonność projektu i implementacji tych testów to 660 ⋅ 0,4 os ⋅ d = 264 os ⋅ d; mamy 3 testerów, więc średni czas projektu i implementacji to 264 os ⋅ d/3 os = 88 d; średni czas trwania testów to na podstawie dwóch powyższych miar możemy wyciągnąć średnią i estymować czas trwania projektu testowego na (88 d + 120 d)/2 = 104 d; biorąc pod uwagę względny wysiłek testowy, cały projekt powinien trwać 104 d/0,55 ≈ 189 d. Jednostki def, os, d, os ⋅ d, KLOC oznaczają odpowiednio: defekty, osoby, dni, osobodni, 1000 linii kodu. Przykład ten pokazuje, jak wiele korzyści przynosi właściwy, systematyczny i dokładny pomiar procesu testowego. Korzyści tych nie da się przecenić: nowy projekt jeszcze się nie rozpoczął, a my już dysponujemy w miarę dokładnymi szacunkami praktycznie wszystkich parametrów procesu testowego. Oczywiście praktyczne zastosowanie tej metody nie jest trywialne. Nie zawsze dane historyczne będą idealnie zgodne na przestrzeni wszystkich projektów. Czasem trzeba będzie wykorzystywać różne modyfikatory w obliczeniach, aby uwzględnić odmienny typ nowego projektu, zmieniony skład osobowy zespołu itp. Jednak nawet mimo tych trudności warto stosować pomiary i modele estymacji różnych parametrów procesu. Takie działania „ułatwiają życie”, dają poczucie pewności i obniżają ryzyko nietrafionych szacunków branych „z głowy”. Dane z metryk i modeli są również bardzo mocnym argumentem w dyskusjach i negocjacjach z klientem, właścicielem projektu czy kierownictwem wyższego szczebla.

49. Metryki zadowolenia klienta

Zadowolenie klienta jest ostateczną miarą jakości. Metryki służące do jego mierzenia różnią się jednak od wcześniej opisanych metryk tym, że nie da się ich obliczyć na podstawie parametrów oprogramowania, właściwości projektu czy z użyciem modeli predykcyjnych. Dotyczą one poziomu zadowolenia określonych ludzi, zatem pomiar ten jest możliwy za pomocą ankiet i kwestionariuszy1. Jest tak dlatego, że zadowolenie (satysfakcja) klienta to bardziej stan psychiczny indywidualnej jednostki niż obiektywna, ilościowa wartość fizyczna, którą łatwo zmierzyć. Proces pomiaru zadowolenia klienta składa się z następujących kroków: 1. Wybór metody i sposobu przeprowadzenia badania. 2. Opracowanie ankiety/kwestionariusza lub innego narzędzia badań. 3. Wybór metody próbkowania. 4. Wybór rozmiaru próby. 5. Zebranie, opracowanie i analiza danych.

49.1. Proces pomiaru zadowolenia klienta 49.1.1. Wybór metody i sposobu przeprowadzenia badania Istnieją dwie główne metody badań zadowolenia klienta: ilościowe i jakościowe. Metody ilościowe wykorzystują metryki i operują na wartościach liczbowych. Metody jakościowe posługują się formą opisową i nie wykorzystują miar ilościowych. Przykładowe metody badania przedstawiono na rysunku 49.1.

Rysunek 49.1. Metody pomiaru zadowolenia klienta Ankiety i kwestionariusze to klasyczne metody ilościowe. Różnią się tym, że ankieta wypełniana jest samodzielnie przez klienta, natomiast kwestionariusz – przez osobę prowadzącą badanie. W kwestionariuszu, oprócz pytań skierowanych do respondenta mogą znajdować się instrukcje i wskazówki dla ankietera dotyczące sposobu realizacji badania. Analiza zgłoszeń od klientów czy tzw. wywiady pogłębione to metody uzyskiwania wyniku pomiaru w formie opisowej. Wywiad pogłębiony jest przeprowadzany „w cztery oczy”, co sprawia, że badana osoba nie odczuwa presji ze strony grupy. Celem wywiadu pogłębionego jest dotarcie – przez odpowiednio prowadzoną rozmowę – do istoty badanego zjawiska, np. do przyczyny źródłowej, dla której klient zrezygnował z używania danego produktu. Przez wywiady pogłębione można uzyskać informacje trudne do odkrycia za pomocą ankiet czy kwestionariuszy. Istnieją trzy popularne techniki przeprowadzania badań: Metoda bezpośrednia – polega na bezpośrednim kontakcie osoby przeprowadzającej badanie z respondentem. Jej wadą jest dosyć wysoki koszt w przypadku dużej próby badawczej. Niewątpliwymi zaletami są elastyczność i możliwość obserwacji respondenta (mowa ciała).

Rozmowy telefoniczne – cechują się niskim kosztem. Nie wymagają od respondenta poświęcenia tak dużo czasu jak w przypadku metody bezpośredniej. Można je nadzorować, aby zapewnić przestrzeganie przyjętych procedur. Wadą metody jest brak możliwości obserwowania klienta (obecna technologia pozwala przezwyciężyć tę trudność, oferując komunikatory internetowe, takie jak Skype). Korespondencja – najtańsza z trzech omawianych metod. Polega na rozsyłaniu ankiet drogą elektroniczną. Wadą jest wysoki odsetek nieodesłanych ankiet, co może powodować utratę reprezentatywności próby. Wymaga starannego skonstruowania ankiety, a jej opracowanie po zebraniu danych – wiedzy statystycznej. Wybór metody i techniki przeprowadzania badań zależy od rozmiaru próby. Jeśli mamy tylko kilku dużych klientów, to można wykorzystać metody bardziej koszto- i czasochłonne. W przypadku, gdy nasz produkt jest wydawany na rynek masowy, zwykle wybiera się metody tańsze.

49.1.2. Opracowanie ankiety/kwestionariusza lub innego narzędzia badań Pytania w ankiecie, kwestionariuszu lub w innym narzędziu badań powinny oczywiście odnosić się do poziomu zadowolenia klienta. Mogą dotyczyć cech jakościowych produktu lub osobistego doświadczenia klienta z używania aplikacji, np.: łatwości użytkowania (czy program jest intuicyjny w użyciu?); wydajności oprogramowania (czy program działa szybko?); niezawodności

(czy

występowały

jakieś

awarie

lub

inne

niedogodności?); łatwości instalacji (czy były problemy z instalacją programu?); serwisu (jaki jest poziom zadowolenia z działu wsparcia klienta?); dokumentacja (czy w programie jest wyczerpujący system pomocy lub dokumentacja użytkowa?); dostępność (czy program/usługa był dostępny zawsze, gdy klient go potrzebował?). Należy wybrać odpowiednie skale pomiaru dla każdego z pytań. Odpowiedź na pytanie najczęściej przyjmuje postać binarną (tak/nie), binarną z możliwością

braku odpowiedzi (tak/nie/nie wiem) lub skali porządkowej (np. pięciostopniowej skali Likerta). Poszczególne stopnie skali porządkowej mają najczęściej opisy słowne, np. stopnie skali 1, 2, 3, 4, 5 mogą oznaczać odpowiednio: bardzo niezadowolony, niezadowolony, neutralny, zadowolony, bardzo zadowolony. Zdarzają się pytania, w których użytkownik powinien sam określić pewną wartość liczbową, np.: przez ile miesięcy używa Pan/Pani naszego oprogramowania? Odpowiedzi na takie pytania mogą być mapowane na skalę porządkową, np. dla powyższego pytania można dostarczyć następujące możliwe odpowiedzi: do 3 miesięcy; od 3 do 6 miesięcy; od 6 miesięcy do roku; ponad rok.

49.1.3. Wybór metody próbkowania Próba to podzbiór populacji. W badaniach statystycznych jest ważne, aby próba była reprezentatywna, tzn. odzwierciedlała wszystkie istotne z punktu widzenia badania cechy populacji, z której została wybrana. Na przykład, jeśli wiemy, że płeć jest istotnym czynnikiem dla badania, to staramy się, aby udział w próbie kobiet i mężczyzn był podobny do tej samej proporcji w całej populacji. Aby wyniki były wiarygodne, potrzebna jest odpowiednio duża próba. Etap wyboru próby składa się zatem z dwóch kroków: wyboru metody próbkowania2 oraz wyboru rozmiaru próby. Oto najczęściej stosowane metody próbkowania. Próbkowanie losowe (ang. random sampling). W tej metodzie każdy element populacji ma taką samą szansę na wylosowanie. Jeśli mamy 100 klientów, a chcemy do próby wylosować 10 z nich, każdemu przypisujemy numer od 1 do 100 i, używając generatora liczb losowych, losujemy bez zwracania 10 liczb ze zbioru {1, 2, …, 100}. Do próby wybieramy klientów, którym przypisano te numery. Próbkowanie systematyczne (ang. systematic sampling). W metodzie tej do próby wybierany jest co n-ty element z listy wszystkich elementów populacji. Na przykład, aby wybrać do próby 5 osób z populacji 100-osobowej, musimy wybrać co dwudziesty element (bo 100/5 = 20). Liczba ta (w naszym przypadku 20) jest nazywana współczynnikiem próbkowania. Losujemy jeden element z pierwszych

dwudziestu (np. element nr 13), a następnie wybieramy co dziesiąty począwszy od tego wylosowanego. Próba zawierać więc będzie elementy o numerach 13, 33, 53, 73, 93. Próbkowanie to jest szybsze niż losowe, ale nie można go stosować, jeśli lista elementów jest uporządkowana według jakiegoś trendu lub ma właściwości okresowe współgrające z współczynnikiem próbkowania. Próbkowanie stratyfikacyjne (ang. stratified sampling). W metodzie tej dzielimy najpierw całą populację na rozłączne grupy, których suma stanowi całą populację. Następnie z każdej grupy jest pobierana próba losowa. Wielkość próby losowej z grupy i powinna być proporcjonalna do wielkości grupy i. Jeśli na przykład populację 100 osób podzieliliśmy na dwie grupy liczące odpowiednio 30 i 70 osób, a próba ma mieć rozmiar 10, to z pierwszej grupy losujemy 3, a z drugiej – 7 osób. Kryterium podziału na grupy powinno uwzględniać czynniki zachowujące reprezentatywność, tzn. elementy w ramach jednej grupy powinny być podobne do siebie w sensie kryteriów istotnych dla badania. Stratyfikacja upewnia nas, że każda z istniejących grup będzie właściwie reprezentowana w próbie. Próbkowanie klastrowe (ang. cluster sampling). Zwane również próbkowaniem blokowym. Tak jak w próbkowaniu stratyfikacyjnym populacja jest dzielona na grupy, ale w przeciwieństwie do poprzedniej metody grupy te nie są homogeniczne. Wręcz przeciwnie – chodzi o to, by każda grupa była jak najbardziej różnorodna. Metodę tę stosuje się najczęściej w badaniach analizujących jednostki geograficzne. Grupami mogą wtedy być np. województwa, miasta, regiony, oddziały firmy. Próbkowanie przypadkowe (ang. haphazard sampling). Metody tej nie należy mylić z próbkowaniem losowym. W próbkowaniu przypadkowym próba wybierana jest przypadkowo, „na chybił trafił”, według własnego uznania. Jest metodą szybką, ale istnieje ryzyko wyboru próby niereprezentatywnej. Inną odmianą tej metody jest próbkowanie według osądu (ang. judgmental sampling), gdzie wybór próby następuje na podstawie posiadanej wiedzy i doświadczenia na temat populacji przez osobę tworzącą próbę.

49.1.4. Wybór rozmiaru próby Rozmiar próby zależy od poziomu ufności, jaki chcemy uzyskać oraz akceptowanej przez nas wielkości błędu pomiarowego. Im wyższy poziom ufności, tym mniejszy musi być margines błędu pomiaru, dlatego próba musi być

większa. Jednocześnie, im większy jest akceptowany przez nas margines błędu, tym niższy wymagany poziom ufności, więc próba może być mniejsza. Często błędnie zakłada się, że aby próba była reprezentatywna, musi stanowić pewien odsetek populacji. Innymi słowy, im większa populacja, tym większa powinna być próba. W rzeczywistości dla reprezentatywności ważny jest głównie względny rozmiar próby. Jej rozmiar w stosunku do populacji jest o wiele mniej istotny3. Na przykład, dla badań statystycznych na populacjach liczących kilka milionów elementów (np. sondaże wyborcze) próba rzędu 2–3 tysięcy osób jest w zupełności wystarczająca. Dla każdej metody próbkowania omówionej w poprzednim rozdziale istnieją metody statystyczne pozwalające na obliczenie rozmiaru próby dla zadanego poziomu ufności i marginesu błędu. Wzory te zależą od analizowanego parametru lub parametrów. Na przykład, dla metody próbkowania losowego wzór na rozmiar próby koniecznej do poprawnego oszacowania proporcji populacji podzielonej według wybranego parametru (np. odsetek zadowolonych klientów) ma postać

gdzie N jest rozmiarem populacji, to szacowany odsetek zadowolenia, ε ∈ (0, 1) to margines błędu, a zq dla zadanego q ∈ (0, 1) to taka wartość, że P(–zq < X
) jest podzbiorem iloczynu kartezjańskiego N × N, gdzie N oznacza zbiór liczb naturalnych. Do podzbioru tego należą np. pary (3, 1), (11, 8), (9, 2), gdyż 3 jest większe od 1, 11 od 8, a 9 od 2. Zapisujemy to jako 3 > 1, 11 > 8, 9 > 2. Para (1, 3) nie należy do relacji, gdyż 1 nie jest większe od 3. Relacja nie musi więc być symetryczna. Oto najważniejsze własności relacji: Zwrotność. Relacja R ⊂ A × A jest zwrotna, jeśli dla każdego a ∈ A zachodzi aRa, tzn. jeśli każdy element jest w relacji z samym sobą. Symetryczność. Relacja R ⊂ A × A jest symetryczna, jeśli dla każdych a, b ∈ A z tego, że aRb wynika, że bRa. Przechodniość. Relacja R ⊂ A × A jest przechodnia, jeśli dla każdych a, b, c ∈ A z tego, że aRb i bRc wynika, że aRc. Relacja, która jest jednocześnie zwrotna, symetryczna i przechodnia nazywana jest relacją równoważności. Wyznacza ona podział zbioru na tzw. klasy abstrakcji, zwane również klasami równoważności. Czarnoskrzynkowa metoda klas równoważności wykorzystuje właśnie tę relację. Przykładem relacji równoważności jest relacja przystawania modulo n, gdzie n jest dowolną, ale ustaloną liczbą naturalną. Dwie liczby naturalne a, b przystają do siebie modulo n (tzn. są ze sobą w relacji mod n, co zapisujemy jako a ≡ n b), jeśli ich reszty

z dzielenia przez n są takie same. Innymi słowy, a ≡ n b, jeśli n|a – b, tzn. jeśli a – b

jest podzielne przez n. Relacja ta jest zwrotna (bo a – a dzieli się przez n). Jest symetryczna, bo jeśli n|a – b, to n|b – a. Jest przechodnia, bo jeśli n|a – b i n|b – c, to n dzieli również sumę tych liczb: n|(a – b) + (b – c), czyli n|a – c. Algebra Boole’a to specjalny dział algebry, w którym rozważane zmienne mogą przyjmować wyłącznie wartości logiczne prawdy i fałszu, oznaczane odpowiednio jako 1 i 0. Zamiast arytmetycznych operatorów dodawania, mnożenia oraz brania liczby przeciwnej w algebrze Boole’a wykorzystuje się „odpowiadające” im operatory logiczne alternatywy ∨ i koniunkcji ∧ oraz negacji ∼. Priorytet wykonywania tych działań jest następujący: najpierw oblicza się wyrażenia w nawiasach, potem negacje, następnie koniunkcje, a na końcu alternatywy. Jeśli wartości logiczne 1 i 0 reprezentowalibyśmy jako liczby 1 i 0, to operatory logiczne można zdefiniować za pomocą operacji arytmetycznych w następujący sposób: a ∨ b =a +b – a ⋅ b a ∧ b =a ⋅ b ∼ a =1– a W logice wykorzystuje się również operator implikacji ⇒, gdzie zapis „a ⇒ b” czyta się „jeżeli a, to b” oraz operator równoważności ⇔, gdzie zapis „a ⇔ b” czyta się jako „a wtedy i tylko wtedy, gdy b”. Równoważność jest koniunkcją dwóch implikacji: a ⇔ b ≡ (a ⇒ b) ∧ (b ⇒ a) Wszystkie powyższe operatory można zdefiniować za pomocą tzw. tablic prawdy pokazanych w tabelach C.1 i C.2. W tablicach podano wartości logiczne wyrażeń zawierających dany operator dla zadanych wartości logicznych argumentów tego operatora. W algebrze Boole’a za pomocą wymienionych operatorów można zdefiniować inne, przydatne operatory logiczne: XOR (alternatywa rozłączna): a ⊕ b = (x ∨ y) ∧ ∼ (x ∧ y) implikacja: a ⇒ b = ∼ a ∨ b równoważność: a ⇔ b = ∼ (a ⊕ b) NAND (negacja AND): a NAND b = ∼ (a ∧ b)

NOR (negacja OR): a NOR b = ∼ (a ∨ b) Tabela C.1. Tablica prawdy dla operatorów alternatywy, koniunkcji, implikacji i równoważności

a

b

a∨b

a∧b

a⇒b

a⇔b

0

0

0

0

1

1

0

1

1

0

1

0

1

0

1

0

0

0

1

1

1

1

1

1

Tabela C.2. Tablica prawdy dla operatora negacji

a

~a

0

1

1

0

Operatory logiczne mają zastosowanie w białoskrzynkowych metodach opartych na logice, gdzie rozważa się wartości logiczne klauzul i predykatów w instrukcjach warunkowych. Alternatywa rozłączna jest wykorzystywana np. w algorytmie wyznaczania wartości warunków pobocznych dla warunku znaczącego w metodzie MC/DC opisanej w podrozdziale 9.7.

Zasada włączeń i wyłączeń Zasada włączeń i wyłączeń pozwala na obliczenie mocy zbioru wyrażonego jako suma innych zbiorów. Niech A = A 1 ∪ A 2 ∪ … ∪ A n i niech każdy ze zbiorów A 1, …, A n ma skończoną liczbę elementów. Jeśli zbiory A i są parami rozłączne, tzn. dla dowolnych i ≠ j zachodzi A i ∩ A j = ∅, sprawa jest prosta:

W ogólnym przypadku zbiory A i mogą mieć jednak niepuste przecięcia i sprawa się komplikuje. Poniższy wzór pozwala na obliczenie mocy zbioru A = A 1 ∪ A 2 ∪ … ∪ A n w ogólnym przypadku:

Aby obliczyć moc zbioru A = A 1 ∪ A 2 ∪ … ∪ A n, należy dodać do siebie moce zbiorów A i, następnie odjąć moce przecięć wszystkich par zbiorów A i ∩ A j, dodać przecięcia wszystkich trójek A i ∩ A j ∩ A k , odjąć przecięcia wszystkich czwórek zbiorów itd. Na przykład, dla n = 3 mamy:

|A 1 ∪ A 2 ∪ A 3| = |A 1| + |A 2| + |A 3| – |A 1 ∩ A 2| – |A 1 ∩ A 3| – |A 2 ∩ A 3| + |A 1 ∩ A 2 ∩ A 3|

Analiza matematyczna Trzy podstawowe pojęcia analizy matematycznej wykorzystywane w tej książce to: granica funkcji, pochodna funkcji oraz całka. Granica to, mówiąc nieformalnie, wartość, do której dąży funkcja, gdy jej argumenty zmierzają do nieskończoności. Formalnie, funkcja f : X → R ma w nieskończoności granicę g (co oznaczamy jako f(x) = g), jeśli ∀ ε > 0 ∃ N > 0 ∀ x ∈ X (x > N ⇒ |f(x) – g| < ε) Intuicyjnie pojęcie granicy jest przedstawione na rysunku C.1. Granicą jest taki punkt g, że jeśli weźmiemy dowolnie małe jego otoczenie ε, to od pewnego momentu N wszystkie wartości funkcji będą wpadać w pas między g – ε a g + ε.

Rysunek C.1. Granica funkcji Granicę funkcji można również zdefiniować dla określonego punktu. Mówimy, że liczba g jest granicą funkcji f w punkcie x0, jeśli dla każdego ε > 0 istnieje takie δ > 0, że dla każdego x spełniającego nierówność 0 < |x – x0| < δ zachodzi |f(x) – g| < ε.

Pochodną funkcji f(x) w punkcie x0 (i określonej w otoczeniu tego punktu) oznaczamy symbolem f′(x0) i nazywamy granicę ilorazu różnicowego (o ile istnieje):

Pochodną funkcji f w punkcie x można zinterpretować geometrycznie jako tangens kąta α nachylenia stycznej do wykresu funkcji f w punkcie x (patrz rys. C.2).

Rysunek C.2. Geometryczna interpretacja pochodnej funkcji w punkcie Pochodna funkcji f to funkcja f′, która każdemu punktowi x przypisuje wartość pochodnej funkcji f(x) w tym punkcie. Na przykład, pochodną funkcji f(x) = x2 jest funkcja f′(x) = 2x, ponieważ z definicji pochodnej

Całką (nieoznaczoną) funkcji f(x) nazywamy taką funkcję F(x), której pochodna jest równa funkcji f: F′(x) = f(x). Całkę zapisuje się jako ∫ f(x)dx. Na przykład, każda

funkcja postaci x2 + C dla dowolnej stałej C jest całką funkcji f(x) = 2x, bo (x2 + C)′ = 2x. Funkcja F nosi nazwę funkcji pierwotnej f. Całka oznaczona funkcji f(x) na przedziale [a, b] jest definiowana jako różnica wartości funkcji pierwotnej dla f w punktach b i a.

Intuicyjnie, całka jest „gęstą” sumą wartości f(x) dla x przebiegającego od a do b. Geometryczna interpretacja tej całki jest jeszcze bardziej intuicyjna: całka oznaczona

jest równa polu powierzchni pod wykresem funkcji f(x) ograniczonym prostymi x = a oraz x = b (patrz rys. C.3; szare pole to wartość całki oznaczonej dla funkcji f(x) w granicach od a do b).

Rysunek C.3. Geometryczna interpretacja całki oznaczonej Na przykład, jeśli f(x) = 2x a F(x) jest jej funkcją pierwotną, to

Rachunek prawdopodobieństwa i statystyka

Pojęciem pierwotnym rachunku prawdopodobieństwa jest zbiór zdarzeń elementarnych, oznaczany przez Ω. Jest to zbiór możliwych wyników pojedynczego eksperymentu,

zwanych

zdarzeniami

elementarnymi.

Na

przykład

dla

doświadczenia polegającego na rzucie monetą Ω ma dwa elementy: orzeł i reszka, Ω = {O, R}. Zdarzenie losowe (lub po prostu zdarzenie) to podzbiór przestrzeni zdarzeń elementarnych. Jeśli na przykład Ω = {1, 2, 3, 4, 5, 6} reprezentuje zbiór możliwych wyników rzutu kostką, to zdarzeniem losowym może być np. zdarzenie „wylosowano parzystą liczbę oczek”, czyli podzbiór {2, 4, 6} zbioru Ω. Rodzinę wszystkich możliwych zdarzeń losowych1 będziemy oznaczać przez Σ. Na przykład dla Ω = {O, R} zbiór Σ to zbiór wszystkich czterech podzbiorów Ω: Σ = {∅, {O}, {R}, {O, R}}. Prawdopodobieństwo to funkcja P: Σ → określona na Σ i przyjmująca wartości ze zbioru [0, 1], spełniająca następujące aksjomaty: 1. P(A) ≥ 0 dla każdego A ∈ Σ. 2. P(Ω) = 1. 3.

dla każdego ciągu A 1, A 2, … zdarzeń losowych parami rozłącznych, tzn. takich, że A i ∩ A j = ∅ dla i ≠ j.

Aksjomat 3. nazywa się własnością przeliczalnej addytywności. Przestrzeń probabilistyczna to trójka (Ω, P, Σ). W przestrzeni probabilistycznej jest określony rozkład prawdopodobieństwa, czyli prawdopodobieństwa poszczególnych elementów zbioru Ω. Na przykład, dla rzutu sprawiedliwą monetą mamy P(O) = P(R) = 0,5, tzn. prawdopodobieństwo wyrzucenia orła jest równe prawdopodobieństwu wyrzucenia reszki i wynosi 0,5. Prawdopodobieństwo warunkowe zdarzenia A pod warunkiem zdarzenia B, oznaczane symbolem P(A|B), to prawdopodobieństwo, że zdarzenie A zajdzie pod warunkiem, że nastąpiło zdarzenie B:

Prawdopodobieństwo warunkowe zdarzenia A pod warunkiem B można wyobrazić sobie jako prawdopodobieństwo zdarzenia A w sytuacji, gdy omegą

staje się tylko zbiór B (patrz rys. C.4). Wtedy to prawdopodobieństwo warunkowe jest równe stosunkowi jasnego szarego pola do pola całego B.

Rysunek C.4. Ilustracja prawdopodobieństwa warunkowego Zmienna losowa to funkcja przypisująca elementom Ω wartości liczbowe ze zbioru liczb rzeczywistych. Wprowadza się ją po to, aby ujednolicić sposób rozważań dla różnych przestrzeni zdarzeń elementarnych i umożliwić ilościowy ich opis. Formalnie, zmienna losowa to funkcja rzeczywista X określona na przestrzeni Ω zdarzeń elementarnych o następującej własności: dla każdej liczby rzeczywistej x zbiór zdarzeń elementarnych ω, dla których X(ω) < x jest zdarzeniem, czyli elementem zbioru Σ. Innymi słowy ∀ x ∈ {ω ∈ Ω : X(ω) < x} ∈ Σ Dana niech będzie przestrzeń probabilistyczna (Ω, P, Σ) i zmienna losowa X w tej przestrzeni. Rozkładem prawdopodobieństwa zmiennej losowej X nazywamy funkcję

PX(A) = P({ω ∈ Ω : X(ω) ∈ A}) gdzie A jest elementem rodziny zbiorów borelowskich B na prostej, tzn. najmniejszej rodziny podzbiorów prostej, spełniającej następujące własności: 1. Ω ∈ B 2. A ∈ B ⇒ Ω \ A ∈ B 3. Intuicyjnie pojęcie rozkładu można wyobrazić sobie następująco: za każdym razem, gdy „odpytujemy” zmienną losową o jej wartość, może ona zwrócić inną liczbę. Niektóre liczby może zwracać częściej, inne rzadziej. Na przykład, jeśli mamy Ω = {O, R}, a zmienna losowa orłowi przypisuje jedynkę, a reszce dwójkę to w przypadku, gdy moneta jest sprawiedliwa, średnio w połowie przypadków zmienna zwróci wartość jeden, a w połowie – wartość dwa. Istnieją dwa typy zmiennych losowych: dyskretne (typu skokowego) i ciągłe. Zmienne dyskretne to takie, które mogą przyjmować przeliczalną liczbę wartości (np. dowolny zbiór skończony, zbiór liczb naturalnych). Zmienne ciągłe są określone najczęściej na zbiorze liczb rzeczywistych. Rozkład prawdopodobieństwa zmiennej losowej w przypadku zmiennej ciągłej nazywany jest gęstością prawdopodobieństwa, a w przypadku zmiennej dyskretnej – dyskretnym rozkładem prawdopodobieństwa. Wartością oczekiwaną dyskretnej zmiennej losowej X (wartością średnią, wartością przeciętną) nazywamy wartość

(o ile istnieje), gdzie xk to poszczególne wartości, jakie może przyjąć zmienna X, a p(xk ) to odpowiadające im prawdopodobieństwa rozkładu zmiennej X. W przypadku zmiennej ciągłej wartość oczekiwana to

(o ile istnieje), gdzie f(x) jest gęstością zmiennej X. Wariancją V zmiennej losowej nazywamy wartość

(o ile istnieje) w przypadku zmiennej dyskretnej oraz

(o ile istnieje) dla zmiennej typu ciągłego. Dystrybuantą F zmiennej losowej X nazywamy funkcję F X (x) = PX ((–∞, x)) = P(X < x). W przypadku zmiennej ciągłej mamy Podamy teraz przykłady najważniejszych dyskretnych rozkładów prawdopodobieństwa: Rozkład dwupunktowy. Jest parametryzowany jednym parametrem p. W rozkładzie dwupunktowym zmienna przyjmuje tylko dwie wartości, x1 i x2 z prawdopodobieństwami odpowiednio P(x1) = p i p(x2) = 1 – p, gdzie p ∈ (0, 1). Rozkład dwumianowy. Jest parametryzowany dwoma wartościami n i p. Obrazuje on doświadczenie, w którym wykonywanych jest n prób, z których

każda może zakończyć się sukcesem z prawdopodobieństwem p lub porażką z prawdopodobieństwem 1 – p. Rozkład ten modeluje prawdopodobieństwo, że w tych n próbach dokładnie k zakończy się sukcesem:

gdzie Xn jest zmienną losową o rozkładzie dwumianowym z parametrami n i p. Wartość oczekiwana to np a wariancja to np(1 – p). Rozkład Poissona. Jest parametryzowany jednym parametrem, λ > 0. Zmienna losowa X ma rozkład Poissona z parametrem λ, jeśli

Rozkład Poissona jest wykorzystywany do modelowania liczby zdarzeń występujących w ustalonym przedziale czasowym. Jest wykorzystywany w modelach niezawodności oprogramowania. Wartość oczekiwana jest równa wariancji i wynosi λ. Oto przykłady najważniejszych ciągłych rozkładów prawdopodobieństwa:

Rozkład jednostajny. Parametryzowany jest wartościami a i b definiującymi przedział, z którego zmienna losowa może przyjmować wartości. Zmienna losowa X ma rozkład jednostajny na przedziale [a, b], jeśli jej funkcja gęstości ma postać

Wartość oczekiwana to (a + b)/2, a wariancja (b – a)2/12. Dystrybuanta rozkładu jednostajnego ma postać

Rozkład wykładniczy. Parametryzowany jednym parametrem λ. Jest często wykorzystywany do modelowania czasu między zajściem dwóch kolejnych zdarzeń. Wiele modeli niezawodności oprogramowania jest opartych na tym rozkładzie. Rozkład wykładniczy jest zadany wzorem

gdzie λ > 0. Wartość oczekiwana to 1/λ a wariancja 1/λ2. Dystrybuanta rozkładu wykładniczego (dla x ≥ 0) ma postać F(x) = 1 – e–λx Rozkład normalny. Parametryzowany dwoma parametrami, μ oznaczającym średnią i σ oznaczającym wariancję. Funkcja gęstości ma postać

gdzie σ > 0. Wartość oczekiwana to μ a wariancja σ2. Dystrybuanty rozkładu normalnego nie da się wyrazić za pomocą funkcji elementarnych. Jeśli X ma rozkład normalny o parametrach μ i σ, to często zapisuje się to jako X ∼ N(μ, σ). Rozkład normalny, dla którego μ = 0 i σ = 1, nazywa się standardowym rozkładem normalnym. Rozkład normalny jest jednym z najważniejszych rozkładów w teorii prawdopodobieństwa. Wynika to z Centralnego twierdzenia granicznego mówiącego, że jeśli Xi, i = 1, 2, …, n są niezależnymi zmiennymi losowymi o takiej samej wartości oczekiwanej μ i skończonej wariancji σ2, to zmienna losowa

zbiega

do

standardowego

rozkładu

normalnego,

gdy

n

zmierza

do

nieskończoności. Rozkład Weibulla. Parametryzowany dwoma wartościami: λ > 0, zwanym parametrem skali oraz k > 0, zwanym parametrem kształtu. Funkcja gęstości ma postać

Wartość oczekiwana to μ = λ ⋅ Γ(1 + 1/k), a wariancja to σ2 = λ2 ⋅ Γ(1 + 2/k) – μ2, gdzie Γ to funkcja gamma, czyli tzw. uciąglona silnia, zadana wzorem gdzie x jest dowolną liczbą rzeczywistą. Całkując przez części, można pokazać, że Γ(x + 1) = x ⋅ Γ(x), stąd nawiązanie do silni: dla x całkowitych Γ(x) = (x – 1)! Dystrybuanta rozkładu Weibulla ma postać

Rozkład Weibulla jest bardzo elastyczny. Dobierając odpowiednio parametry λ i k, można uzyskać wiele innych rozkładów, np. dla k = 2 otrzymujemy rozkład Rayleigha, a kładąc k = 1, dostajemy rozkład wykładniczy. Jeśli zmienna losowa X o rozkładzie Weibulla oznacza czas do awarii, to rozkład ten wyraża rozkład tych czasów w sytuacji, gdy częstość awarii jest proporcjonalna do potęgi czasu: dla k < 1 częstość awarii maleje z czasem. Dla k = 1 jest stała, a dla k > 1 rośnie wraz z upływem czasu.

Teoria języków formalnych Alfabetem nazywamy dowolny skończony i niepusty zbiór A. Słowo w nad alfabetem A to ciąg elementów ze zbioru A. Na przykład, jeśli A = {a, b}, to przykładami słów są a, b, ab, baaba. Szczególnym przypadkiem jest słowo puste ε, nie zawierające żadnych liter. Zbiór wszystkich słów nad alfabetem A nazywamy (wolnym) monoidem nad A i oznaczamy przez A*. Przez A + oznaczamy monoid z usuniętym słowem pustym: A + = A* \ {ε}.

Język L nad alfabetem A to dowolny podzbiór monoidu A*: L ⊆ A*. Przykładem języka jest zbiór wszystkich poprawnych programów napisanych w języku C++. Gramatyką nazywamy czwórkę G = (VN, VT, v0, P), gdzie VN to skończony i niepusty zbiór tzw. symboli nieterminalnych, VT to skończony i niepusty zbiór

tzw. symboli terminalnych, v0 ∈ VN to tzw. symbol początkowy, a P to zbiór produkcji przekształcający ciągi złożone z symboli terminalnych i nieterminalnych. Formalnie P jest relacją P ⊂ (VN ∪ VT)+ × (VN ∪ VT)* Jeśli dwa słowa u, w są ze sobą w relacji P, to oznaczamy to jako u → w. Jest to tzw. produkcja gramatyki, która zamienia słowo u w słowo w. Wywodem w gramatyce G nazywamy ciąg produkcji v0 → w 1 → w 2 → … → w N, gdzie v0 jest symbolem początkowym gramatyki, a w N ∈ VT* jest słowem nad alfabetem VT.

Hierarchia Chomsky’ego wyróżnia cztery podstawowe klasy języków, ze względu na postać produkcji odpowiadających im gramatyk: 1. Języki regularne. Prawa gramatyki języka regularnego są postaci N → N′T lub N → T, gdzie N, N′ ∈ VN, T ∈ VT. Języki regularne są rozpoznawane przez automaty skończenie stanowe (maszyny stanowe). Klasa tych języków jest wykorzystywana m.in. w kompilatorach (analiza leksykalna). 2. Języki bezkontekstowe. Prawa gramatyki języka bezkontekstowego są postaci N → α, gdzie α ∈ (VN ∪ VT)*. Praktycznie wszystkie języki programowania są językami bezkontekstowymi, tzn. ich gramatyki są bezkontekstowe. Języki te są rozpoznawane przez tzw. automaty ze stosem. 3. Języki kontekstowe. Prawa gramatyki kontekstowej są postaci αNβ → αγβ, gdzie N ∈ VN, α, β ∈ (VN ∪ VT)*, γ ∈ (VN ∪ VT)+ . Idea gramatyki

kontekstowej polega na tym, że nieterminal N może zostać zamieniony w ciąg γ tylko, jeśli znajduje się w kontekście α i β. Języki kontekstowe rozpoznawane są przez tzw. automaty liniowo ograniczone.

4. Języki klasy 0 (rekurencyjnie przeliczalne). Prawa gramatyki języka klasy 0 mają dowolną postać. Języki klasy 0 rozpoznawane są przez maszyny Turinga. Języki regularne można opisać za pomocą wyrażeń regularnych. Każdy język regularny można opisać wyrażeniem regularnym i na odwrót – każde wyrażenie

regularne definiuje jakiś język regularny. Wyrażenia regularne REG(A*) nad alfabetem A to zbiór wyrażeń zdefiniowanych w sposób indukcyjny: ∅ oraz ε są wyrażeniami regularnymi; dla każdego a ∈ A, a jest wyrażeniem regularnym; jeśli a i b są wyrażeniami regularnymi, to są nimi również wyrażenia (a + b), (a ⋅ b) oraz a*. Symbol ⋅ oznacza konkatenację elementów stojących po jego obu stronach. Na przykład konkatenacja a ⋅ b oznacza ciąg ab. Przykładowo, kod pocztowy można opisać wyrażeniem regularnym c ⋅ c – c ⋅ c ⋅ c, gdzie c jest wyrażeniem regularnym postaci (0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9) oznaczającym cyfrę, a – jest symbolem kreski. W tym przypadku A = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, –}. Podczas konstrukcji wyrażeń regularnych można opuszczać nawiasy, stosując znany z algebry priorytet działań (mnożenie przed dodawaniem), np. wyrażenie ((a ⋅ b) + c) jest równoważne wyrażeniu a ⋅ b + c.

1 W przypadku nieprzeliczalnego zbioru Ω zbiór Σ definiuje się nieco inaczej. Jest to tzw. σ-algebra zbiorów z Ω.

Dodatek D Informacja o certyfikacji i egzaminach

Dalej opisujemy kilka organizacji oferujących egzaminy certyfikujące w obszarze inżynierii jakości oraz testowania oprogramowania. Certyfikaty przez nie przyznawane, są rozpoznawane i uznawane na całym świecie. American Society for Quality (ASQ) – Certified Software Quality Engineer. Egzamin składa się ze 160 pytań jednokrotnego wyboru. Czas rozwiązywania testu wynosi 4 godziny. Aby uzyskać certyfikat, należy odpowiedzieć poprawnie na około 75% pytań (procedura obliczania progu zdawalności jest skomplikowana i kandydat nie dostaje swojego wyniku procentowego, lecz jedynie informację, czy zdał, czy nie). Certyfikat należy odnawiać co 3 lata albo przez ponowne przystąpienie do egzaminu, albo przez zebranie odpowiedniej liczby tzw. punktów recertyfikacyjnych przyznawanych za pracę w obszarze jakości oprogramowania, publikowanie artykułów, uczestnictwo w konferencjach itp. Strona internetowa: www.asq.org. International Software Testing Qualifications Board (ISTQB) oferuje kilka ścieżek certyfikacyjnych. Certyfikaty tej organizacji są najbardziej rozpoznawalnymi na świecie, jeśli chodzi o testowanie oprogramowania. Podstawowym certyfikatem jest Certified Tester – Foundation Level. Egzamin składa się z 40 pytań jednokrotnego wyboru. Aby zdać egzamin, należy odpowiedzieć poprawnie na co najmniej 26 pytań (60%). Można również zdać egzamin na rozszerzenie certyfikatu podstawowego. ISTQB daje obecnie dwie

możliwości takiego rozszerzenia: Tester w metodykach zwinnych (Agile Tester) oraz Testowanie oparte na modelu (Model Based Testing). Posiadacze certyfikatu podstawowego mogą zdawać egzaminy na poziomie zaawansowanym. ISTQB oferuje tutaj trzy rodzaje certyfikatów: kierownik testów (Test Manager), analityk testów (Test Analyst) oraz techniczny analityk testów (Technical Test Analyst). Dwa ostatnie różnią się tym, że w egzaminie na certyfikat Analityka Testów nacisk jest położony na techniki czarnoskrzynkowe projektowania testów, natomiast w przypadku technicznego analityka testów – na techniki białoskrzynkowe. Egzamin składa się z 65, 60 lub 45 pytań, w zależności od typu certyfikatu (kierownik, analityk, techniczny analityk). Posiadacze certyfikatu na poziomie zaawansowanym mogą przystąpić do egzaminu certyfikującego na poziomie eksperckim. ISTQB oferuje tu następujące certyfikaty: Expert Level – Test Management, Expert Level – Improving the Testing Process, Expert Level – Test Automation Engineering oraz Expert Level – Security Testing. Egzamin na poziomie eksperckim obejmuje również część pisemną (należy odpowiedzieć na kilka pytań otwartych), a certyfikat trzeba odnawiać co 3 lata. Strona internetowa: www.istqb.org. International Institute for Software Testing (IIST) oferuje od 1999 roku możliwość uzyskania certyfikatów Certified Software Test Professional na poziomach: Associate, Practitioner oraz Master, Certified Agile Software Test Professional na poziomach Practicioner oraz Master, Certified Software Test Automation Specialist, Certified Software Quality Manager oraz Certified Test Manager. Certyfikat wygasa po 3 latach i trzeba go odnawiać. Strona internetowa: www.testinginstitute.com. QAI Global Institute oferuje certyfikaty w zakresie zapewniania jakości oraz testowania: Certified Associate in Software Quality, Certified Software Quality Analyst, Certified Manager of Software Quality, Certified Associate in Software Testing, Certified Software Tester oraz Certified Manager of Software Testing. Organizacja ta oferuje również certyfikację z zakresu analizy biznesowej, zarządzania projektami oraz inżynierii procesów. Strona internetowa: www.softwarecertifications.org.

Bibliografia

[1]

ISO 9000 Quality Management Systems – Fundamentals and V

[2]

ISO/IEC 25010 Software Engineering – Software Product Qualit Evaluation – Software and Quality in Use Models, 2008.

[3]

ISO/IEC 9126 Software Engineering – Product Quality, 1991, 20

[4]

ISO/IEC/IEEE 29119-3 Software and systems engineering – Soft documentation, 2013.

[5]

IEEE 829 Standard for Software and System Test Documentatio 2008.

[6]

Słownik wyrażeń związanych z testowaniem (wersja 2.2.2), St Systemów Informatycznych, 2013.

[7]

IEEE 610 IEEE Standard Glossary of Software Engineering Term Electrical and Electronics Engineers, New York, 1990.

[8]

DO-178C (ED-12C) Software Considerations in Airborne System Certification, Radio Technical Commission for Aeronautics & E Civil Aviation Equipment, 2011.

[9]

Myers G., Projektowanie niezawodnego oprogramowania, Wyd Techniczne, Warszawa 1980.

[10]

Myers G., The Art of Software Testing, Wiley, New York 1979.

[11]

Hetzel W.C., The Complete Guide to Software Testing, John Wil

[12]

Miller E., „Introduction to Software Testing Technology” w: Tu Validation, IEEE Catalog No. EHO 180-0, s. 4–16.

[13]

ISTQB – Certified Tester, Foundation Level Syllabus, Internatio Qualifications Board, 2011.

[14]

Beizer B., Software Testing Techniques, V an Nostrand Reinhol

[15]

Butcher P., Debugowanie. Jak wyszukiwać i naprawiać błędy w Wydawnictwo Helion, Gliwice 2010.

[16]

Zeller A., Why Programs Fail. A Guide to Systematic Debugging 2009.

[17]

Ariane 501 Inquiry Board report, European Space Agency, Pari

[18]

Luo L., Software Testing Techniques. Technology Maturation a Carnegie Mellon University, Class Report for 17-939A, Pittsbur

[19]

Gelperin D., Hetzel B., „The Growth of Software Testing”, CACM

[20]

Turing A.M., „Computing machinery and intelligence”, Mind, 1

[21]

Buxton J., Randell B. (red.), „Software Engineering Techniques Committee, Belgia 1970.

[22]

A short history of the cost per defect metric, ver. 4.0, David Con

[23]

ISO/IEC 14598 – Information Technology – Software Product E

[24]

Fewster M., Graham D., Software Test Automation. Effective U Addison Wesley, 1999.

[25]

IEEE 1044 – Standard for Classification for Software Anomalie

[26]

Fenton N., Software Metrics: a Rigorous Approach, Champan &

[27]

Ammann P., Offutt J., Introduction to Software Testing, Cambr York 2008.

[28]

Pol M., Teunissen R., van V eenendaal E., Software Testing. A G Approach, Addison Wesley, 2002.

[29]

IEEE 1008 – Standard for Software Unit Testing, 1993.

[30]

Gilb T., Graham D., Software Inspection, Addison Wesley, 1993

[31]

Black R., Managing the Testing Process, Wiley Publishing, Inc.

[32]

ISO/IEC/IEEE 29119-2 Software and systems engineering – Soft processes, 2013.

[33]

Koomen T., van der Aalst L., Broekman B., V roon M., TMap N testing, UTN Publishers, 2006.

[34]

ISO/IEC 12207 – Information Technology – Software Lifecycle P

[35]

Hass M.A., Guide to Advanced Software Testing, Artech House

[36]

BS 7925-2 Software Testing – Software Component Testing, Br 1998.

[37]

Marick B., New models for test development, Reliable Software www.rstcorp.com, 1999.

[38]

Kroll P., Kruchten P., Rational Unified Process od strony prakt Naukowo-Techniczne, Warszawa 2007.

[39]

Kruchten P., Rational Unified Process od strony teoretycznej, W Techniczne, Warszawa 2007.

[40]

Cadle J., Yeates D., Zarządzanie procesem tworzenia systemów Wydawnictwa Naukowo-Techniczne, Warszawa 2004.

[41]

Pressman R., Praktyczne podejście do inżynierii oprogramowan Naukowo-Techniczne, Warszawa 2004.

[42]

Boehm B., „A Spiral Model of Software Development and Enha Software Engineering Notes, 1986, 11(4), 14–24.

[43]

Boehm B., Spiral Development: Experience, Principles, and Refi University, Pittsburgh, 2000.

[44]

„Agile Manifesto”, [Online]. Strona: http://agilemanifesto.org/

[45]

Crispin L., Gregory J., Agile Testing: A Practical Guide for Teste Addison-Wesley, Upper Saddle River 2009.

[46]

Beck K., Andres C., Wydajne programowanie – eXtreme Program 2005.

[47]

Takeuchi H., Nonaka I., „The new product development game” 1986, 64(1), 137–146.

[48]

Chrapko M., Scrum. O zwinnym zarządzaniu projektami, Wyda 2013.

[49]

Lacey M., Scrum. Praktyczny przewodnik dla początkujących

[50]

Linz T., Testing in Scrum, rocky nook, 2014.

[51]

Beck K., Test-Driven Development By Example, Addison-Wesley

[52]

„Cucumber software”, [Online]. Strona: http://en.wikipedia.org/wiki/Cucumber_%28software%29. [Da

[53]

Mills H., Dyer M., Linger R., „Cleanroom Software Engineering 4(5), 19–25.

[54]

Smith D., Wood K., Engineering Quality Software: A Review of C Standards and Guidelines Including New Methods and Develop

Applied Science, Nowy Jork 1989. [55]

Wing J., „A Specifier’s Introduction to Formal Methods”, Comp

[56]

ISO 8402 – Quality management and quality assurance, 1995.

[57]

Linger R., „Cleanroom Software Engineering for Zero-Defect So 15th International Conference on Software Engineering, 1993.

[58]

Balci O., „Principles and Techniques of Simulation V alidation, w Winter Simulation Conference Proceedings, Piscataway, 199

[59]

Gerrard P., Thompson N., Risk-Based E-business Testing, Artec

[60]

Graham D., van V eenendaal E., Evans I., Black R., Foundation Thompson Learning, 2007.

[61]

Myers G., Sandler C., Badgett T., Thomas T., Sztuka testowani Wydawnictwo Helion, Gliwice 2005.

[62]

Beizer B., Black-Box Testing, Wiley, 1995.

[63]

Cadar C., Dunbar D., Engler D., „KLEE: Unassisted and Autom Coverage Tests for Complex Systems Programs”, w: USENIX Sy Systems Design and Implementation, San Diego, USA, 2008.

[64]

Bereza B., „Testowanie z modelu”, 2014. [Online]. Strona: http://blogomotion.com/Download/MBT.pdf. [Data dostępu: 2

[65]

Utting M., Pretschner A., Legeard B., A Taxonomy of Model-Ba New Zeland, 2006.

[66]

Diller A., Z – An Introduction to Formal Methods, John Wiley &

[67]

Abrial J.-R., Modeling in Event-B: System and Software Enginee Press, 2010.

[68]

Warmer J., Kleppe A., OCL. Precyzyjne modelowanie w UML, W

Techniczne, Warszawa 2003. [69]

„ActiveState Code – Recipes”, [Online]. Strona: http://code.activestate.com/recipes/577908-implementation-ofalgorithm/. [Data dostępu: 01.2014].

[70]

Jones C., „Software Quality in 2012: A Survey of the State of th www.namcook.com, 2012.

[71]

IEEE 1028 – Standard for Software Reviews and Audits, 1997.

[72]

Fagan M., „Design and code inspections to reduce errors in pr Systems Journal, 1976, 15(3), 182–211.

[73]

Genuchten M., Cornelissen W., van Dijk C., „Supporting Inspec Meeting System”, Journal of Management Information Systems

[74]

Wiegers K., Peer Reviews in Software: A Practical Guide, Addis

[75]

Freedman D., Weinberg G., Walkthroughs, Inspections and Tec House Publishing, 1990.

[76]

Fagan M., „Reviews and inspections”, w: Proceedings of the sd Pioneers, 2002.

[77]

Westfall L., The Certified Software Quality Engineer Handbook Milwaukee 2009.

[78]

ISO/IEC 2382-1 – Data processing – V ocabulary – Part 1: Fund

[79]

ISTQB – Certyfikowany Tester. Syllabus dla Poziomu Zaawanso Jakości Systemów Informatycznych, 2007.

[80]

Black R., Mitchell J., Advanced Software Testing V ol. 3, Guide Certification as an Advanced Technical Test Analyst, Rocky No

[81]

„GNU Coding Standards”, [Online]. Strona:

http://www.gnu.org/prep/standards/standards.html. [82]

„Checkstyle”, [Online]. Strona: http://checkstyle.sourceforge.ne

[83]

McCabe T., „A complexity measure”, IEEE Transactions on Sof 308–320.

[84]

Hoare C., „An axiomatic basis for computer programming”, 1969, 12(10), 576–585.

[85]

Hoare C., „Communicating Sequential Processes”, Communicat 21(8), 666–677.

[86]

Emerson E., Clarke E., „Using branching time logic to synthesi skeletons”, Sci. Comput. Programming, 1982, 2, 241–266.

[87]

King J., „Symbolic execution and program testing”, Communica 19(7), 385–394.

[88]

Clarke L., „A System to Generate Test Data and Symbolically E Transactions on Software Engineering, 1976, 2(3), 215–222.

[89]

Havelund K., Java PathFinder User Guide, NASA Ames Researc

[90]

Saxena P., Akhawe D., Hanna S., Feng M., McCammant S., D. Execution Framework for JavaScript”, w: IEEE Symposium on S Oakland, USA, 2010.

[91]

Kernighan B., Pike R., The Practice of Programming, Addison-W

[92]

„gprof”, [Online]. Strona: www.math.utah.edu/docs/info/gprof

[93]

Black R., Advanced Software Testing Vol. 1, Guide to the ISTQB an Advanced Test Analyst, Rock Nook Inc., Santa Barbara 201

[94]

Booch G., Rumbaugh J., Jacobson I., The Unified Modeling Lan Wesley, 1999.

[95]

de Brujin N., „A Combinatorial Problem”, Koninklijke Nederlan Wetenschappen, 1946, 49, 758–764.

[96]

Chow T., „Testing software Design Modelled by Finite-State M Transactions on Software Engineering, 1978, 4(3), 178–187.

[97]

Ostrand T., Blacer M., „The category-partition method for spec functional tests”, Communications of the ACM, 1988, 31(6), 67

[98]

Grochtmann M., „Test Case Design Using Classification Trees” Proceedings, 1994.

[99]

Czarnecki K., Eisenecker U., Generative Programming Methods Addison-Wesley, 2000.

[100] Greenfield J., Short K., Cook S., Kent S., Crupi J., Software Facto Applications with Patterns, Models, Frameworks and Tools, W

[101] Perrouin G., Oster S., Sen S., Klein J., Baudry B., le Traon Y., „P product lines: comparison”, Software Quality Journal, 2012, 20 [102] Tevanlinna A., Taina J., Kauppinen R., „Product family testing Software Engineering Notes, 2004, 29(2), 12–17.

[103] Cohen D., Dalal S.R., Kajla A., Patton G., „The Automatic Effic System”, w: Proceedings of the 5th International Symposium on Engineering, 1994.

[104] Kuhn D., Reilly M., „An investigation of the applicability of des software testing”, w: Proceedings of the 27th Annual NASA Go Workshop (SEW ‘02), Waszyngton, 2002.

[105] Grindal M., Lindstroem B., Offutt A., Andler S., „An Evaluation for Test Case Selection”, 2003. [106] Lei Y., Tai K., „In-parameter-order: A test generation strategy

Proceedings of the 3rd IEEE High Assurance Systems Engineerin

[107] „CAtables”, [Online]. Strona: www.public.asu.edu/~ccolbou/sr dostępu: 2014].

[108] Hedayat A., N. Sloane, Stufken J., Orthogonal Arrays: Theory a 1999. [109] Sloane N.J., „Orthogonal Arrays”, [Online]. Strona: http://neilsloane.com/oadir/index.html. [Data dostępu: 2014].

[110] Cockburn A., Jak pisać efektywne przypadki użycia, Wydawnic Warszawa 2004.

[111] Wake B., „INV EST in Good Stories, and SMART tasks”, [Onlin http://xp123.com/articles/invest-in-good-stories-and-smart-tas

[112] Liu H., X ie X ., Yang J., Lu Y., Chen T.Y., „Adaptive random tes Software – Practice and Experience, 2011, 41, 1131–1154.

[113] Adrion W., Branstad M., Cherniabsky J., „V alidation, V erificat Computer Software”, Computing Surveys, 1982, 14(2), 159–192

[114] Chen T., Leung H., Mak I., „Adaptive Random Testing”, Lectur Science, 2004, 3321, 320–329.

[115] T.Y. Chen, Kuo F.-C.K., Merkel R.G., Tse T., „Adaptive Random Case Diversity”, HKU CS Tech Report TR-2009-07, 2009.

[116] Baron M., Probability and Statistics for Computer Scientists, T

[117] Pol M., van V eenendaal E., Teunissen R., Testen volgens TMap

[118] Chen T., Lau M., Yu Y., „MUMCUT: A fault-based strategy for specifications”, w: Proc. Asia-Pacific Software Engineering Con [119] Weyuker E., Goradia T., Singh A., „Automatically generating t

specification”, w: IEEE Transactions on Software Engineering

[120] Foster K., „Sensitive test data for logic expressions”, ACM SIGS Notes, 1984, 9(2), 120–125. [121] Tai K.-C., Su H.-K., „Test generation for Boolean expressions”, Int. Comp. Software Appl. Conf., 1987.

[122] Wiszniewski B., Bereza-Jarociński B., Teoria i praktyka testow Wydawnictwo Naukowe PWN, Warszawa 2006.

[123] White L., Wiszniewski B., „Path testing of computer programs simple loop patterns”, Software – Practice and Experience, 199

[124] Rapps S., Weyuker E., „Selecting software test data using data Transactions on Software Engineering, 1985, SE-11(4), 367-375

[125] ISO/IEC/IEEE 29119-4 Software and systems engineering – Soft techniques, 2014.

[126] Budd T., Mutation Analysis of Program Test Data, Yale Univers

[127] Acree A., Budd T., DeMillo R., Lipton R., Sayward F., Mutation Institute of Technology, Atlanta, 1979.

[128] Offutt J., Lee A., Rothermel G., Untch R., Zapf C., „An experime sufficient mutation operators”, ACM Transactions on Software 1996, 5(2), 99–118.

[129] Woodward M., Hennell M., „On the relationship between two criteria: all JJ-paths and MCDC”, Information and Software Tec 440.

[130] Hsueh M., Tsai T., Iyer R., „Fault injection techniques and too 30(4), 75–82.

[131] Ghosh S., Kelly J., „Bytecode fault injection for Java software”,

Software, 2008, 81, 2034–2043.

[132] Carreira J., Silva J., „Why do some (weird) people inject faults? Engineering Notes, 1998, 23(1), 42–43.

[133] Abbot J., Software Testing Techniques, NCC Publications, 1986 [134] Bach J., „Exploratory Testing”, w: The Testing Practitioner – 2 2004.

[135] Bach J., General Functionality and Stability Test Procedure for Windows Logo – Desktop Applications Edition, Satisfice, Inc.,

[136] Roman A., „Testowanie eksploracyjne – cudowny lek czy zwyk Developer’s Journal, listopad/grudzień 2014.

[137] Whittaker J.A., How to Break Software. A Practical Guide to T Inc., 2003.

[138] Whittaker J.A., Thompson H.H., How to Break Software Secur Security Testing, Pearson Education, Inc., 2004.

[139] Andrews M., Whittaker J., How to break Web Software. Funct of Web Applications and Web Services, Pearson Education, Bo

[140] Bach J., „Heuristic Test Strategy Model, ver. 5.2.”, Satisfice, In

[141] Jones J., Harrold M., „Test-suite reduction and prioritization fo condition/decision coverage”, IEEE Transactions on Software E 195–209.

[142] Elbaum S., Malishevsky A., Rothermel G., „Test Case Prioritiza Studies”, IEEE Transactions on Software Engineering, 2002, 28

[143] DeMillo R., Lipton R., Sayward F., „Hints on Test Data Selectio Programmer”, Computer, 1978, 11(4), 34–41.

[144] Yu Y., Lau M., „Fault-based test suite prioritization for specific Information and Software Technology, 2012, 54, 179–202.

[145] McCall J., Richards P., Walters G., Factors in Software Quality 1977. [146] ISO/IEC 25012 – Software Engineering – Software Product Qua Evaluation – Data Quality Model, 2008. [147] „Rehabilitation Act of 1973, Section 508”, www.section508.g

[148] Nielsen J., Molich R., „Heuristic evaluation of user interfaces”, Conference, 1990, 249–256.

[149] Molich R., Nielsen J., „Improving a human-computer dialogue” ACM, 1990, 33(3), 338–348.

[150] Nielsen J., „Heuristic evaluation”, w: Usability Inspection Meth & Sons, 1994.

[151] Bastien J., Scapin D., „A validation of ergonomic criteria for th computer interfaces”, International Journal of Human-Compute 183–196.

[152] Gerhardt-Powals J., „Cognitive engineering principles for enhan performance”, International Journal of Human-Computer Intera

[153] Weinschenk S., Barker D.T., Designing Effective Speech Interfac

[154] Connell I., Hammond N., „Comparing usability evaluation prin problem instances versus problem types”, w: Human-Computer ‘99. Proceedings of the 7th IFIP TC, IOS Press, 1999, 621–636.

[155] Smith S., Mosier J., Guidelines for Designing User Interface Soft [156] Porter A., Accelerated Testing and Validation, Elsevier, 2004.

[157] „SLED – Software Life Cycle Empirical/Experience Database”, C Systems Information Analysis Center, [Online]. Strona: https://sw.thecsiac.com/databases/sled/swrel.php. [Data dostę

[158] „OWASP”, [Online]. Strona: www.owasp.org. [Data dostępu: 2 [159] IEEE 1219 – Software Maintenance, 1998.

[160] Baldwin C., Clark K., „The Architecture of Participation: Does Free Riding in the Open Source Development Model?”, Manage 1116–1127.

[161] Pol M., Koomen T., Spillner A., Management und Optimierung d Praktischer Leitfaden fur erfolgreiches Software-Testen mit TPI 2000.

[162] Binder R.V ., Testowanie systemów obiektowych. Modele, wzor Wydawnictwa Naukowo-Techniczne, Warszawa 2003.

[163] Kung D., Gao J., Hsia P., Wen F., Toyoshima Y., „Change impa oriented software maintenance”, w: Proceedings of the Confere Maintenance, 1994.

[164] Jungmayr S., Improving testability of object-oriented systems, p www.dissertation.de, 2003.

[165] Roman G.-C., „A taxonomy of current issues in requirements e 1985, 18(4), 14–23.

[166] Souza W., Arakaki R., „Abstract Testability Patterns”, w: Proc International Workshop on Software Patterns and Quality, 200

[167] Rafique I., Lew P., Abbasi M., Li Z., „Information Quality Eval Extending ISO 25012 Data Quality Model”, World Academy of Technology, 2012, 6, 503–508.

[168] ISO/IEC 13335 – Guidelines for the Management of IT Security,

[169] „Oracle® Database PL/SQL User’s Guide and Reference 10g Rel Strona: http://docs.oracle.com/cd/B19306_01/appdev.102/b142 [Data dostępu: 2014].

[170] Heemstra F., Kusters R., Nijhuis R., van Rijn M., Dealing with approach to risk management in software engineering, Eindhov Technology, 1997. [171] van V eenendaal E., Practical Risk-Based Testing. The PRISMA 2012.

[172] Bryson J.M., „What to do when stakeholders matter”, Public M 6(1), 21–53.

[173] Black R., Advanced Software Testing Vol. 2, Guide to the ISTQB an Advanced Test Manager, Santa Barbara: Rock Nook Inc., 2

[174] ISO/IEC 25000 – Software engineering – Software product Qual Evaluation (SQuaRE), 2014.

[175] IEC 61508 – Functional Safety of Electrical/Electronic/Program related Systems, 2010.

[176] Craig R.D., Jaskiel S.P., Systematic Software Testing, Artech Ho 2002.

[177] Khoshgoftaar T., Allan E., Halstead R., Trio G., Flass R., „Usin Predict Software Quality”, IEEE Computer, 1998, 4, 66–72. [178] I. Q. S. BV , „Test management quick reference card”, [Online]. www.improveqs.nl. [Data dostępu: 2014].

[179] „MIL-STD-882 – Standard Practice for System Safety”, United S Department of Defense.

[180] Boehm B., Tutorial: Software Risk Management, IEEE Compute

[181] „MIL-STD-1629A, Procedures for Performing a Failure Mode, E 1980.

[182] Ericson C., Hazard Analysis Techniques for System Safety, Wile

[183] „QFD Institute”, [Online]. Strona: www.qfdi.org/. [Data dostęp

[184] Guinta L.R., Praizler N.C., The QFD Book. The Team Approach Satisfying Customers Through Quality Function Deployment, AM

[185] van der Aalst L., Baarda R., Roodenrijs E.V .J., V isser B., TMap Management, Sogeti, Nederland BV 2008. [186] de Grood D.-J., TestGoal. Result-driven testing, Springer 2010. [187] Wiegers K., Software Requirements, Microsoft Press, 2003. [188] „Bender – Requirements Based Testing Process Overview.pdf”,

[189] ISO/IEC 15288 – Systems and software engineering – System lif

[190] IEEE 1012 – Standard for Software V erification and V alidation [191] IEEE 730 – Standard for Software Quality Assurance Plans, 20

[192] ISO/IEC/IEEE 29119-1 Software and systems engineering – Soft and definitions, 2013.

[193] Bach J., „A Framework for Good Enough Testing”, IEEE Compu

[194] Perry W.E., Effective Methods for Software Testing, Wiley, India

[195] Weston B., Value through a Test Policy (white paper), 2006, https://www.stickyminds.com/sites/default/files/article/file/201

[196] ISTQB – Certified Tester, Advanced Level Syllabus – Test Manag Testing Qualifications Board, 2012.

[197] Chrissis M., Konrad M., Shrum S., CMMI. Guidelines for Proces Improvement, Addison Wesley, 2004. [198] Jones C., Estimating Software Costs, McGraw-Hill, 1995. [199] Meyer W., Concepts of Mathematical Modeling, Dover Publicat [200] Kan S., Metryki i modele w inżynierii jakości oprogramowania PWN, Warszawa 2006.

[201] Bach J., „Satisfice – Session-Based Test Management”, [Online www.satisfice.com/sbtm/. [Data dostępu: 2014]. [202] Juran J., Quality Control Handbook, McGraw-Hill, 1979.

[203] Krasner H., „Using the Cost of Quality Approach for Software” Defense Software Engineering, 1998, 11(11), 6–11.

[204] Campanella J., Principles of Quality Costs: Principles, Implemen Quality Press, Milwaukee 1990.

[205] Srivastava A., Yadav D., Pandey S., „TaaS: An Evolution of Tes Computing”, International Journal of Advanced Research in Co Technology, 2012, 1(10), 42–49.

[206] Chillarege R., Bhandari I., Chaar J., Halliday M., Moebus D., R „Orthogonal defect classification – a concept for in-process me Transactions on Software Engineering, 1992, 18(11), 943–956.

[207] „Orthogonal Defect Classification v 5.2 for Software Design an [Online]. Strona: http://researcher.watson.ibm.com/researcher/ 2.pdf. [Data dostępu: 2014].

[208] Ma L., Tian J., „Analyzing errors and referral pairs to characte improve web reliability”, w Proc. 3rd Int. Conf. on Web Enginee

[209] Lutz R., Mikulski I., „Ongoing requirements discovery in high-i Software, 2004, 21(2), 19–25.

[210] Bhandari I., Halliday M., Traver E., Brown D., Chaar J., Chillar software process improvement during development”, IEEE Tran 1993, 19(12), 1157–1170. [211] Tian J., Henshaw J., „Tree-based defect analysis in testing”, w Software Quality, McLean, 1994.

[212] „Soft Skills List”, [Online]. Strona: https://bemycareercoach.com skills.html. [Data dostępu: 2014].

[213] „ISTQB – Certified Tester, Expert Level Syllabus – Test Manage Testers and Test Stakeholders)”, International Software Testin 2011.

[214] Tuckman B., „Developmental sequence in small groups”, Psych 63(6384–399. [215] Scholtes P., Joiner B., Steibel B., The Team Handbook, Oriel, M

[216] Briggs I., Myers P., Understanding Personality Type, Davies-Bla

[217] Capretz L., „Personality types in software engineering”, Interna Computer Studies, 2003, 58, 207–214.

[218] Evans I., Achieving Software Quality through Teamwork, Artec

[219] Bath G., van V eenendaal E., Improving the Testing Process, Ro

[220] Belbin R., Management Teams: Why They Succeed or Fail, Butt [221] Wagner A., The Transactional Manager, Spiro Press, 1996.

[222] Honey P., Mumford A., The Learning Styles Helper’s Guide, Pet 2002.

[223] Satir V ., Banmen J., Gerber J., Gomori M., The Satir Model: Fam Science and Behavior Books, 1991.

[224] Marine Corps Values: A User’s Guide for Discussion Leaders, U

[225] „DO-178B (ED-12B) – Software Considerations in Airborne Syst Certification”, Radio Technical Commission for Aeronautics & Civil Aviation Equipment, 1992.

[226] Pyzdek T., Quality Engineering Handbook, Marcel Dekker, New [227] Buzan T., The Mindmap Book, BBC-Books, 1995.

[228] Helmer O., Rescher N., On the Epistemology of the Inexact Scie Corporation, 1958.

[229] Surowiecki J., The Wisdom of Crowds, Doubleday Anchor, 2005

[230] Fowles J., Handbook of futures research, Greenwood Press, Co

[231] Adler M., Ziglio E., Gazing into the oracle, Jessica Kingsley Pub

[232] Saaty T., „How to make a decision: The Analytic Hierarchy Pro Operational Research, 1990, 48, 9–26. [233] „ISTQB – Certified Tester, Expert Level Syllabus – Test Automa International Software Testing Qualifications Board, 2014.

[234] Joint Strike Fighter Air Vehicle C++ Coding Standards For the S Demonstration Program, Lockheed Martin Corporation, 2005.

[235] Keyes J., Software Configuration Management, Auerbach Public [236] FuzzBALL: V ine-based Binary Symbolic Execution, http://bitblaze.cs.berkeley.edu/fuzzball.html.

[237] Smilgin R., Piaskowy A., Dane testowe. teoria i praktyka, Wyd

2011.

[238] „Document Object Model”, [Online]. Strona: www.w3.org/TR/R [239] „X Path”, [Online]. Strona: www.w3.org/TR/xpath/. [240] Ousterhout J., „Scripting: high level programming for the 21st 1998, 31(3), 23–30.

[241] Pettichord B., „Seven steps to test automation success”, w: Pr Analysis & Review Conference (STAR), 1999.

[242] Packevičius Š., Ušaniov A., Bareiša E., „Software testing using as oracles”, w: International Conference on Computer Systems CompSysTech 07, 2007.

[243] Bjørner D., Jones C., „The V ienna Development Method: The M Notes in Computer Science, tom 61, 1978.

[244] Cristiá M., Monetti R., „Implementing and Applying the Stock Model-Based Testing”, w: Formal Methods and Software Engin Conference on Formal Engineering Methods, ICFEM 2009, Rio d

[245] Burton S., Automated Testing from Specifications, The Universi [246] R. Hierons, „Testing from a Z specification”, Software Testing 1997, 7(1), 19–33.

[247] Legeard B., Peureux F., Utting M., „Automated boundary testi Notes in Computer Science, 2002, 2391, 21–40.

[248] Ambert F., Bouquet F., Chemin S., Guenaud S., Legeard B., Peu N., „BZ-testing-tools: A tool-set for test generation from Z and programming”, w: Formal Approaches to Testing Software, FA

[249] Carrington D., Stocks P., „A tale of two paradigms: Formal me testing”, w: Z User Workshop, Cambridge, 1994.

[250] Rice M., Seidman S., „An approach to architectural analysis an Workshop on Software Architecture, New York, 1998.

[251] Aertryck L., Benveniste M., Le Metayer D., „CASTING: A forma generation method”, w: 1st Int. Conf. on Formal Engineering M Hiroshima, 1997.

[252] Butler M., Hallerstede S., „The Rodin Formal Modelling Tool”, http://deploy-eprints.ecs.soton.ac.uk/4/1/eventb.pdf. [Data dos

[253] „HP Quality Center”, [Online]. Strona: www8.hp.com/pl/pl/soft center-quality-management/. [254] „IBM Rational Quality Manager”, [Online]. Strona: www03.ibm.com/software/products/pl/ratiqualmana. [255] „TestLink Open Source Test Management”, [Online]. Strona:

[256] „HP Unified Functional Testing”, [Online]. Strona: www8.hp.c solutions/unified-functional-testing-automation/. [257] „FitNesse”, [Online]. Strona: www.fitnesse.org/. [258] „soapUI”, [Online]. Strona: www.soapui.org/.

[259] „Robot Framework”, [Online]. Strona: http://robotframework.o

[260] Moore I., „Jester: a JUnit test tester”, w: Proc. 2nd Int. Conf. o Flexible Processes in Software Engineering, XP2001, 2001.

[261] Ma Y., Offut J., Kwon Y., „MuJava. An automated Class Mutat Testing, Verification and Reliability, 2005, tom 15(2), 97–133.

[262] Kennedy D., O’Gorman J., Kearns D., Aharoni M., Metasploit. P penetracyjnych, Helion, 2013.

[263] „beStorm”, [Online]. Strona: www.beyondsecurity.com/bestorm

[264] „Spirent Studio Security”, [Online]. Strona: www.spirent.com/Products/Studio_Security.aspx.

[265] „HP Application Security Center”, [Online]. Strona: http://h304 Application-Security-Center/ct-p/sws-sc01.

[266] „Dmalloc – Debug Malloc Library”, [Online]. Strona: http://dma

[267] „The Daikon invariant detector”, [Online]. Strona: http://plse.c [268] „Gcov – a Test Coverage Program”, [Online]. Strona: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html.

[269] „Intel Thread Checker for Windows and Linux”, [Online]. Stron https://software.intel.com/en-us/articles/intel-thread-checker-d [270] „Parasoft Jtest”, [Online]. Strona: www.parasoft.com/jtest. [271] „Sonarqube”, [Online]. Strona: www.sonarqube.org/.

[272] Arapidis C.S., Sonar Code Quality Testing Essentials, Packet Pu 2012. [273] „Apache JMeter”, [Online]. Strona: http://jmeter.apache.org/.

[274] „HP Load Runner”, [Online]. Strona: www8.hp.com/pl/pl/softw load-testing/. [275] „Spirent Studio Performance”, [Online]. Strona: www.spirent.com/Products/Studio_Performance.aspx. [276] „FunkLoad”, [Online]. Strona: https://funkload.nuxeo.org/. [277] „SeleniumHQ”, [Online]. Strona: www.seleniumhq.org/.

[278] Burns D., Selenium 2 Testing Tools. Beginner’s Guide, Packet P 2012.

[279] „Abbot – Java GUI Test Framework”, [Online]. Strona: http://abbot.sourceforge.net/doc/overview.shtml. [280] „JUnit”, [Online]. Strona: http://junit.org/.

[281] Beust C., Suleiman H., Next Generation JAVA Testing. TestNG a Boston: Pearson Education, 2008. [282] „Cucumber”, [Online]. Strona: http://cukes.info/. [283] „jbehave”, [Online]. Strona: http://jbehave.org/. [284] „RSpec”, [Online]. Strona: http://rspec.info/.

[285] „Spec Explorer”, [Online]. Strona: http://msdn.microsoft.com/e [Data dostępu: 2014].

[286] Jaffuel E., Legeard B., „LEIRIOS Test Generator: Automated Te Models”, Lecture Notes in Computer Science, 2006, 4355, 277–

[287] Cristiá M., Albertengo P., Monetti P., „Fastest: a Model-Based Z Notation”, www.fceia.unr.edu.ar/~mcristia/publicaciones/fa

[288] „MaTeLo – Markov Test Logic”, [Online]. Strona: www.all4tec based-testing/20-markov-test-logic-matelo.

[289] ConformicSoftware, „Generation, Conformic Qtronic Semantics Conformic Software Whitepaper, 2008. [290] „Bugzilla”, [Online]. Strona: www.bugzilla.org/.

[291] „JIRA”, [Online]. Strona: https://www.atlassian.com/software

[292] „Mantis Bug Tracker”, [Online]. Strona: https://www.mantisb [293] „Trac”, [Online]. Strona: http://trac.edgewall.org/. [294] „IBM Rational ClearQuest”, [Online]. Strona: www-

03.ibm.com/software/products/pl/clearquest. [295] „Redmine”, [Online]. Strona: www.redmine.org/. [296] ISTQB – Certified Tester, Expert Level Syllabus – Improving the International Software Testing Qualifications Board, 2011.

[297] van V eenendaal E., „Test Improvement Manifesto”, Testing Ex

[298] Thierry H., Organization and Management, Stenfert Kroese, 197

[299] Trienekens J., van V eenendaal E., van Solingen R., Punter T., va Quality from a Business Perspective, Kluwer, 1997.

[300] Garvin D., „What does product quality really mean?”, Sloan M 26(1), 25–43.

[301] Deming W., Out of the Crisis, MIT Center for Advanced Enginee

[302] ISO/IEC 15504 – Information Technology – Software Process A

[303] McFeeley B., IDEAL: A User’s Guide for Software Process Impro

[304] „Fundamental Concepts of Excellence”, [Online]. Strona: www model/fundamental-concepts. [Data dostępu: 2014].

[305] van V eenendaal E., Wells B., Test Maturity Model integration – 2012.

[306] Sogeti, TPI Next – Business Driven Test Process Improvement, U

[307] Koomen T., Pol M., Test Process Improvement, Addison-Wesley

[308] „Test Improvement Model”, [Online]. Strona: www.lucas.lth.se/events/doc2003/0113A.pdf. [Data dostępu: 2 [309] Black R., Critical Testing Processes, Addison-Wesley, 2003. [310] „Test Organization Maturity”, [Online]. Strona:

http://gerrardconsulting.com/tom/tom200.pdf. [Data dostępu:

[311] Basili V ., Software Modeling and Measurement: The Goal Ques University of Maryland, 1992. [312] Humphrey W., Introduction to the Personal Software Process

[313] Curtis B., Hefley W., Miller S., The People Capability Maturity M [314] Senge P., The Fifth Discipline, Currency Doubleday, 2006.

[315] Urbaniak M., Zarządzanie jakością. Teoria i praktyka, Difin, 2

[316] „Model doskonałości EFQM”, [Online]. Strona: www.efqm.pl/i doskonalosci-efqm. [Data dostępu: 2014]. [317] Eckes G., Rewolucja Six Sigma, MT Biznes, Warszawa 2010. [318] George M.L., Lean Six Sigma, McGraw-Hill, 2002.

[319] Anderson D., Lessons in Agile Management: On the Road to Ka 2012.

[320] van V eenendaal E., Cannegieter J., The Little TMMi, UTN Publi

[321] ITIL, Best Practice for Service Support, Office of Government Co

[322] Humphrey W., Introduction to the Team Software Process, SEI,

[323] Kuvaja P., Koch P., Mila L., Krzanik A., Bicego S., Saukkonen G Assessment and Improvement: Bootstrap Approach, Oxford: Bl

[324] ITGI, „Identifying and aligning business goals and IT-goals”, I Governance Research Institute of the University of Antwerp M 2008, www.isaca.org. [325] Burnstein I., Practical Software Testing, Springer-V erlag, New

[326] van V eenendaal E., Cannegieter J., „Test Maturity Model Integ

TMMi Benchmark – Where Are We Today?”, w: EuroSTAR e-bo [327] Robson M., Problem Solving in Groups, Gower, 1995.

[328] Basili V ., Weiss D., „A Methodology for Collecting V alid Softw IEEE Transactions on Software Engineering, 1984, 10(6), 728–7

[329] van Solingen R., Berghout E., The Goal/Question/Metric Method quality improvement of software development, McGraw-Hill, 19

[330] Pulford K., Kuntzmann-Combelles A., Shirlaw S., A Quantitativ Management – The AMI Handbook, Addison-Wesley, 1995.

[331] Basili V ., Caldiera C., Rombach H., „Goal Question Metric Para Software Engineering, John Wiley & Sons, 1994, 528–532.

[332] Basili V ., Rombach H., „The TAME Project: Towards Improvem Environments”, IEEE Transactions on Software Engineering, 19

[333] Kaplan R., Norton D., Strategiczna Karta Wyników, Wydawnic Warszawa 2001.

[334] Wawak S., Zarządzanie jakością. Teoria i praktyka, Helion, Gl

[335] Weinberg G., Quality Software Management, V ol. 1, Dorset Ho

[336] Gladwell M., The Tipping Point: How Little Things Can Make a University Press, 2002.

[337] Cannegieter J., Software Process Improvement (po duńsku), SDU

[338] Zandhuis J., „Improvement team relay, agile improvements (po Conference, 2009.

[339] Mayer J., Emotional Intelligence: Key readings on the Mayer an Publishing, 2004.

[340] Bar-On R., „The Bar-On model of emotional intelligence: A vali

model”, Organizations and People, 2007, 14, 27–34.

[341] Kano N., Seraku N., Takahashi F., Tsuji S., „Attractive quality Journal of the Japanese Society for Quality Control, 1984, 14(2)

[342] Jones C., Software Assessments, Benchmarks, and Best Practic Boston 2000.

[343] ISO 14598 – Information technology – Software product evalua

[344] IEEE 1061 – Standard for Software Quality Metrics Methodolo

[345] Anscombe F., „Graphs in Statistical Analysis”, American Stati

[346] Babbie E., The Practice of Social Research, Wadsworth, Belmo

[347] Card D., Agresti W., „Resolving the Software Science Anomaly Software, 1987, tom 7, 29–35. [348] Tague N., The Quality Toolbox, ASQ Quality Press, 2005.

[349] Nelson L., „The Shewhart Control Chart – Tests for Special Cau Technology, 1984, 16, 237–239. [350] Aczel A., Statystyka w zarządzaniu, Wydawnictwo Naukowe

[351] Basili V ., Perricone B., „Software Errors and Complexity: An E Communications of the ACM, 1984, 27(1), 42–52.

[352] Withrow C., „Error Density and Size in Ada Software”, IEEE So

[353] Malayia Y., Denton J., „Module size distribution and defect den Symposium on Software Reliability Engineering, 2000, 62–71.

[354] „Software Productivity Research”, [Online]. Strona: www.spr.c languages-table.html. [Data uzyskania dostępu: 2014].

[355] Fenton N., Pfleeger S., Software Metrics, PWS Publishing Comp

[356] Karner G., Resource Estimation for Objectory Projects, 1993, www.larmel.net/upload/Karner%20%20Resource%20Estimation%20for%20Objectory%20Projects

[357] Costagliola G., Ferrucci F., Tortora G., V itiello G., „Class Point Estimation of Object-Oriented Systems”, IEEE Transactions on 31(1), 52–74.

[358] Albrecht A., IBM CIS & A Guideline 313, AD/M Productivity Me Validation, IBM, 1984.

[359] „Function Point Counting Practices Manual”, International Fu 1999.

[360] Banker R., Kauffman R., Kumar R., „An Empirical Test of Obje Measurement Metrics in a Computer-Aided Software Engineeri of Management Information Systems, 1991, 8(3), 127–150.

[361] Stensrud E., Estimating with Enhanced Object Points vs. Functi Consulting, 1998.

[362] Halstead M., Elements of Software Science, Elsevier North Hol [363] „Aivosto – Complexity metrics”, [Online]. Strona: www.aivost complexity.html. [Data dostępu: 2014].

[364] Gill G., Kemerer C., „Cyclomatic complexity density and softwa productivity”, IEEE Transactions on Software Engineering, 199

[365] Parikh G., Zvegintzov N. (red.), Tutorial on Software Maintenan 1983.

[366] Lo B., „Syntactical Construct Based APAR Projection”, IBM Sa Technical Report, Kalifornia, 1992. [367] McClure C., „A Model for Program Complexity Analysis”, w:

Konferencja Inżynierów Oprogramowania, 1978.

[368] Henry S., Kafura D., „Software Structure Metrics Based on Info Transactions on Software Engineering, 1981, 7, 510–518.

[369] Yourdon E., Constantine L., Structured Design, Prentice-Hall, E

[370] Myers G., Composite Structured Design, V an Nostrand Reinhol

[371] Kitchenham B., Pickard L., Linkman S., „An evaluation of som Engineering Journal, 1990, 5(10), 50–58.

[372] Laird L., Brennan C., Software Measurement and Estimation. A Wiley & Sons, New Jersey 2006.

[373] Welker K., Oman P., „Software maintainability metrics models Journal of Defense Software Engineering, 1995, 8(11), 19–23.

[374] Coleman D., Ash D., Lowther B., Oman P., „Using metrics to e maintainability”, IEEE Computer, 1994, 27(8), 44–49.

[375] Card D., Agresti W., „Measuring software design complexity”, Software, 1988, 8(3), 195–197.

[376] Card D., Glass R., Measuring Software Design Quality, Prentice [377] Lorenz M., Object-Oriented Software Development: A Practical Englewood Cliffs 1993.

[378] Chidamber S., Kemerer C., „A Metrics Suite for Object-Oriented on Software Engineering, 1994, 20, 476–493.

[379] Cormen T., Leiserson C., Rivest R., Stein C., Wprowadzenie do a Naukowe PWN, Warszawa 2013.

[380] Reifer D., „Estimating web development COSTS: there are differ 2002, 13–17.

[381] McGibbon T., Modern Cost Estimation Tools and Modern Empi Estimation Tools, A DACS State-of-the-Art Report, 1997, http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.3 [382] „COCOMO II – Model Definition Manual”, 2000.

[383] Remus H., Zilles S., „Prediction and Management of Program Q Międzynarodowa Konferencja Inżynierów Oprogramowania, 19

[384] Dunn R., „The Quest for Software Reliability”, w: Handbook of Assurance (red. G. Schulmeyer, J. McManus), V an Nostrand Re 342–384.

[385] Nagappan N., Ball T., „Use of Relative Code Churn Measures t Density”, w: ICSE’05 Proc. 27th Int. Conf. on Software Engineer

[386] Putnam C., Myers W., Measures for Excellence: Reliability Softw Budget, Yourdan Press, New Jersey 1992.

[387] Thangarajan M., Biswas B., „Mathematical Model for Defect P Development Cycle”, w: The SEPG (Software Engineering Proces 2000. [388] Hollenbach C., Quantitatively measured process improvements 2001, www.dtic.mil/ndia/2003CMMI/Holl.ppt.

[389] Chulani S., Boehm B., „Modeling software defect introduction University of Southern California Center for Software Engineeri

[390] Devnani-Chulani S., „Bayesian analysis of software cost and qu doktorska)”, University of Southern California, 1999.

[391] Babiarz R., „Metryki stylometryczne predykcji jakości oprogra Jagielloński, praca magisterska, opiekun: A. Roman, 2014. [392] Sommerville I., Software Engineering, Addison-Wesley, 2001.

[393] Musa J., Iannino A., Okumoto K., Software Reliability – Measu Application, McGraw-Hill, 1987.

[394] Marcus E., Stern H., Blueprints for High Availability, Wiley, No

[395] Bernstein L., „Software fault tolerance”, w: Advances in Compu Zelkowitz), Academic Press, San Diego 2003.

[396] Jelinski Z., Moranda P., „Software Reliability Research”, w: St Performance Evaluation, Academic Press, New York 1972, s. 4

[397] Goel A., Okumoto K., „A Time-Dependent Error-Detection Rate Reliability and Other Performance Measures”, IEEE Transactio 206–211.

[398] Musa J., Okumoto K., „A Logarithmic Poisson Execution Time Reliability Measurement”, w: 7. Międzynarodowa Konferencja Oprogramowania, Los Angeles, 1983.

[399] Yamada S., Ohba M., Osaki S., „S-Shaped Reliability Growth M Detection”, IEEE Transactions on Reliability, 1983, 32, 475–47

[400] Schneidewind N., „Analysis of error processes in computer sof on Reliable Software, 1975.

[401] Musa J., „A Theory of Software Reliability and Its Application Software Engineering, 1975, 1(3), 312–327. [402] Ohba M., „Software Reliability Analysis Models”, IBM Journal Development, 1984, 21(4), 428–443.

[403] Duane J., „Learning Curve Approach to Reliability Monitoring” Aerospace, 1964, 2, 563–566.

[404] Moranda P., „Predictions of Software Reliability During Debug Reliability and Maintainability Symposium, Waszyngton, 1975

[405] Littlewood B., V errall J., „A Bayesian Reliability Growth Mode Journal of the Royal Statistical Society, series C, 1973, tom 22

[406] Worwa K., Modelowanie i ocena wzrostu niezawodności oprog testowania, Warszawa: Oficyna Wydawnicza Politechniki War

[407] Jones C., Applied Software Measurement: Assuring Productivity Nowy Jork 1991.

[408] Hoisington S., Huang T., Suther T., Cousins T., „Customer V iew Machine Enterprise. Part I – Methodology and Results”, IBM R [409] Tufte E., Envisioning Information, Graphics Press, 1997. [410] Tufte E., Visual Explanations, Graphics Press, 1990.

[411] Hopcroft J., Motwani R., Ullman J., Wprowadzenie do teorii au i obliczeń, Wydawnictwo Naukowe PWN, 2005. [412] Eveleens J., V erhoef C., „The Rise and Fall of the Chaos Report 2010, 27, 30–36.

[413] Glass R., „IT Failure Rates – 70% or 10-15%”, IEEE Software, t 2005.

[414] Glass R., „The Standish Report: Does It Really Describe a Softw 2006, 49(8), 15–16.

[415] Jorgensen M., Molokken K., „How Large Are Software Cost Ov Software Technology, 2006, 48(8), 297–301.

[416] Zvegintzov N., „Frequently Begged Questions and How to Ans 1998, 20(2), 93–96.

[417] Rothman J., „It depends: Deciding on the Correct Ratio of Deve www.jrothman.com/2000/01/it-depends-deciding-on-the-correc testers/.