SlideShare une entreprise Scribd logo
1  sur  63
Télécharger pour lire hors ligne
Od redaktora
Jak ten czas leci, zanim się obejrzeliśmy, a tu juŜ 10 – jubileuszowy – numer naszego
kwartalnika. Mam nadzieje, ze numer jubileuszowy jest równie ciekawy jak 9 poprzednich.
W tym numerze cztery ciekawe artykuły:
1. Joanna Droździel o zarządzaniu zmianą – jest to kontynuacja jej pracy
z poprzedniego numeru
2. Moty Aharonovitz o tym jak skutecznie usprawnić testowanie oparte na
wymaganiach
3. Mateusz Bukowski i Paweł Paterek o obiektach pozornych, ciekawe rozwiązanie
związane z metodologią TDD; autorzy bardzo dokładnie na przykładzie opisują na
czym to polega
4. Maciej Dusza o pisaniu dokumentacji – coś czego większość z nas nie lubi, autor
pokazuje jak robić to dobrze
Zgodnie z rozpoczętym w numerze 8 kwartalnika cyklem zamieszczamy w tym numerze
sprawozdanie z konferencji, które odbyły się ostatnio: Łukasz śebrowski był w Düsseldorfie na

SQS Software & Systems Quality Conferences i relacjonuje nam to wydarzenie.
W czasie tworzenia tego numeru odbyła się IV Konferencja Jakości Systemów
Informatycznych. Na gorąco moŜna jedynie powiedziec, ze była bardzo ciekawa – więcej o niej
w następnym numerze Testera.PL
Równocześnie chciałbym – kolejny raz - gorąco zachęcić wszystkich czytelników tego
periodyku do aktywnej współpracy. Cały czas czekamy na Państwa uwagi, artykuły, felietony –
wszystko, co Was interesuje, co chcielibyście przeczytać, czego się dowiedzieć. JeŜeli tylko
będziemy mogli, postaramy się zrealizować Państwa postulaty.
TESTER.PL

Zarządzanie problemem i incydentem
Joanna Droździel

Joanna Droździel jest absolwentem Informatyki na
Wydziale Elektrycznym Politechniki Warszawskiej. Obroniła pracę
magisterską z zakresu metodyki ITIL. Posiada certyfikat ISEB
Foundation.
Od października 2006 roku prowadzi blok wykładów
„Zarządzanie usługami IT” na Podyplomowym Studium
Prowadzenia Projektów Informatycznych na Politechnice
Waszawskiej. Obecnie pracuje w firmie CS Stars na stanowisku
starszego analityka do spraw zapewnienia jakości, biorąc udział w
projektach dla klientów zagranicznych.

Strona 2 z 63
TESTER.PL

Zarządzanie zmianą
Joanna Droździel

Tak jak Ŝycie społeczne i cywilizacja poddane są nieustannej ewolucji, tak równieŜ i w
działalności firmy zmiany informatyczne są konieczne. To czy siły wywołujące zmiany mają
charakter ekonomiczny, techniczny, społeczny czy polityczny nie ma szczególnego znaczenia.
Zmiany wdraŜane w sektorze informatycznym dotyczą przede wszystkim infrastruktury oraz
oprogramowania, powstają najczęściej w trakcie standaryzacji działań produkcyjnych lub teŜ
wynikają z potrzeby zapanowania nad zgłoszeniami uŜytkowników. JeŜeli jeszcze klika lat temu
pomysł wykorzystania informatyki w projektach elektronicznej administracji spotykał się z
ogólnym niedowierzaniem, to teraz pomysł ten juŜ nikogo nie dziwi. Wręcz przeciwnie, uwaŜa
się Ŝe wykorzystanie informatyki w administracji przedsiębiorstwa nie będzie efektywne, jeŜeli w
projektach elektronicznej administracji nie będą uwzględnione zmiany, jakim we współczesnym
świecie ulega firma.

Proces zarządzania zmianą
Bez efektywnego procesu zarządzania zmianą dalszy rozwój cywilizacyjny byłby
niemoŜliwy. Dlatego jeŜeli w jakiejś dziedzinie Ŝycia nie spotkaliśmy się z koniecznością
wprowadzania zmian, to wcześniej czy później będziemy do tego zmuszeni. W wyniku
zachodzących

na

świecie

przemian:

społecznych,

cywilizacyjnych,

ekonomicznych

czy

kulturowych zmieniają się takŜe obszary podległe działalności organizacji. Proces zarządzania
zmianami w sektorze usług informatycznych jest nieodłącznym elementem działalności
biznesowej przedsiębiorstwa. Gdy proces ten przebiega źle, wówczas oddziałuje to negatywnie
równieŜ na stronę biznesową. Bez efektywnie przeprowadzonych zmian informatycznych
zakłócona zostaje działalność biznesowa firmy. MenedŜer działu informatycznego często
twierdzi, Ŝe kierowany przez niego zespół jest przygotowany na realizację procesu zmian. Z
badań wynika, Ŝe nierzadko jest inaczej. MenedŜerowie radzą sobie z oceną i monitorowaniem
efektów wprowadzonych zmian. Gorzej jest jednak z wywołaniem kryzysu, czyli stworzeniem
impulsu do zmiany, czy teŜ z wprowadzeniem nowego porządku rzeczy, gdy sytuacja tego
wymaga.

Strona 3 z 63
TESTER.PL
KaŜda zmiana pociąga za sobą lawinę kolejnych. Dlatego ci, którzy ponieśli straty
finansowe, wiedzą jak waŜny jest dobrze przeprowadzony proces zarządzania zmianą. PomoŜe
on zbadać ryzyko wprowadzenia zmiany, oszacować ilość odpowiednich zasobów oraz
sprawdzić, czy czas wprowadzenia zmiany został właściwie określony. Mając na celu przede
wszystkim minimalizację liczby incydentów i problemów wynikających ze źle wprowadzonych
zmian. W pracach nad pozostałymi procesami menedŜerowie mogli skorzystać z pomocy
konsultantów, w przypadku procesu zarządzania zmianą pomoc ta moŜe okazać się
bezwartościowa. Wynika to z faktu iŜ tylko osoba przebywająca w danej organizacji jest w stanie
poznać realia rządzące firmą i dzięki temu określić kierunek koniecznych zmian.
Poznając procesy metodyki ITIL zauwaŜamy, iŜ kolejny proces zaleŜy od poprzedniego i
wpływa na następny, podobnie jest równieŜ z procesem zarządzania zmianą, który w większym
lub mniejszym stopniu korzysta, oddziałuje, wpływa na pozostałe procesy. Konieczność
zwiększenia pojemności usług lub teŜ utrzymanie jej na dotychczasowym poziomie (ale przy
większej liczbie uŜytkowników, bądź ilości danych oraz liczby transakcji), moŜe wymusić
konieczność wprowadzenia zmian.
Procesy te nie będą działać efektywnie bez poprawnie przeprowadzonego procesu
zarządzania zmianą, który w duŜej mierze zaleŜy od procesów zarządzania problemem oraz
incydentem. Wynika to z ilości zmian zgłoszonych przez uŜytkowników na skutek wykrytych
awarii, jak równieŜ powstałych na skutek działań proaktywnych w procesach zarządzania
incydentem i problemem.
Podstawą procesu zarządzania zmianą jest prośba o zmianę (RfC), od której rozpoczyna
się proces. Kluczem do efektywnego wdroŜenia zmiany jest odpowiednio wypełniony formularz
w wersji papierowej bądź elektronicznej. Wersja papierowa bardzo szybko okazała się
niepraktyczna, zwłaszcza w firmach, gdzie liczba zgłaszanych zmian jest bardzo duŜa.
Poszczególne etapy procesu odbywają się drogą elektroniczną za pomocą odpowiedniej
aplikacji.
Przystępując

do

procesu

zarządzania

zmianą

warto

tuŜ

przed

wdroŜeniem

zaplanowanego modelu postępowania zabezpieczyć aktualny stan aplikacji, sprzętu czy
konfiguracji. Z kaŜdej nieudanej zmiany moŜna się wycofać, ale tylko wtedy, gdy dysponujemy
szczegółami sprzed wprowadzenia zmian. Są jednak zmiany, które aby mogły być
zaimplementowane, wymagają jednak wielu konsultacji. NaleŜy wówczas skorzystać z pomocy
organu Rada Doradcza (CAB), w skład której wchodzą nie tylko menedŜer zmiany i
przedstawiciele zarządu, ale równieŜ osoby odpowiedzialne za pozostałe procesy metodyki ITIL.
Rozpoczynając wdroŜenie procesu w firmie warto rozpocząć od niewielkich, lekko
skomplikowanych zmian i dopiero wraz z upływem czasu sięgać po zmiany bardziej złoŜone.

Strona 4 z 63
TESTER.PL
Dzięki takiej postawie, menedŜerowie opanują zasady, jakimi kieruje się proces zarządzania
zmianą na tyle dobrze, by móc go przeprowadzać w sposób mechaniczny. Proces zarządzania
zmianą został przedstawiony na rysunku poniŜej.

Rysunek 1. Proces zarządzania zmianą.

Proces zarządzania zmianą funkcjonuje dzięki osobie odpowiedzialnej za jego poprawne
zaplanowanie oraz wdroŜenie. Tą osobą jest menedŜer zmiany a zarazem właściciel procesu.
MenedŜer zmiany odpowiada za realizację juŜ zaplanowanych zmian, planowanie następnych,
nadzór nad zmianami przeprowadzonymi, a więc generowanie raportów dla strony biznesowej
oraz przedstawienie argumentów potwierdzających potrzebę planowania zmian. Autorzy
metodyki ITIL warunkują sukces we wdroŜeniu metodyki od indywidualnych działań właściciela
procesu. Z reguły ilość pracy przekracza jednak moŜliwości jednej osoby. Dlatego teŜ waŜne jest
aby menedŜer dowolnego procesu zaangaŜował do pracy tyle osób, ile jest w danej chwili
niezbędnych (uwzględniając moŜliwości finansowe przedsiębiorstwa). Z reguły na etapie
planowania menedŜer korzysta z pomocy analityków a potrzebne informacje czerpie z Bazy
Konfiguracji. Natomiast na etapie wdroŜenia zmiany korzysta z pomocy programistów,
administratorów, testerów, a więc osób dostępnych w ramach codziennej działalności
organizacji.
Jednak nie wszystkie zmiany są wynikiem efektywnej pracy menedŜera zmiany, wiele
próśb o zmianę pochodzi bezpośrednio od uŜytkowników. Zaleca się by co pewien czas zapytać
uŜytkowników jakich zmian oczekują. Nawet jeŜeli w danej chwili brakuje moŜliwości ich

Strona 5 z 63
TESTER.PL
realizacji, mogą stać się pomocne na etapie planowania przyszłych zmian. Bardzo wiele zgłoszeń
nie moŜe zostać zrealizowanych ze względu na ograniczenia ustalone w umowie SLA pomiędzy
klientem a dostawcą usług informatycznych. Warto jednak wszystkie zgłoszenia zachowywać
poniewaŜ mogą one stać się źródłem bardzo cennej wiedzy w chwili wykonywania kolejnego
przeglądu umowy. Równie istotne dla procesu zmian są wyniki badań opinii klientów gdyŜ to ich
zdanie w decydującym stopniu powinno wyznaczać kierunek wewnętrznych zmian. Powinny to
być badania systematyczne, a ich rezultaty muszą być moŜliwie szybko uwzględniane w
projektach zmian, tak aby w efekcie przyniosły klientowi satysfakcję wcześniej, niŜ działania
rynkowe konkurencji

Komunikacja oraz informacja
W procesie zarządzania zmianą kluczową rolę odgrywa informacja oraz komunikacja. W
aspekcie związanym z informacją waŜne jest, by osoba odpowiedzialna za realizację procesu
dysponowała gruntownym przygotowaniem merytorycznym. MenedŜer odpowiedzialny za
proces zarządzania zmianą podejmując decyzję musi kierować się regułą: im więcej informacji
tym lepiej. Podobnie jest z drugą istotną kwestią w procesie zarządzania zmianą - komunikacją.
W przypadku niewielkiej zmiany, której wpływu uŜytkownik nie odczuje w swojej codziennej
pracy, aspekt ten nie odgrywa tak waŜnej roli. Nie chodzi bynajmniej o uświadomienie
uŜytkownika, Ŝe zmiana jest planowana, ale równieŜ o czynny jego udział w pracach nad
procesem. Tak naprawdę ludzie znacznie częściej przeciwstawiają się zmianom, niŜ je akceptują.
Dlatego by wywołać reakcje pozytywne, naleŜy zaangaŜować większą grupę pracowników w
proces zarządzania zmianą. Dzięki takiej postawie uzyskamy zrozumienie konieczności
wprowadzania zmian, a co za tym idzie, wiarę w powodzenie całej misji. By uniknąć
negatywnych reakcji uŜytkowników na wprowadzenie zmian bądź ich brak naleŜy poprawić
komunikację na linii uŜytkownik - zarząd. Tylko dzięki pełnej jasności i klarowności działań
uzyskamy lepsze zrozumienie sytuacji. Czasami jednak radykalna zmiana jest łatwiejsza do
zaakceptowania dlatego, Ŝe niewiele elementów nowej strategii przypomina stare procedury.
Istotnym czynnikiem wpływającym na reakcję na zmianę jest czas. Im wolniej przebiegają
zmiany, tym dotkliwiej są one odczuwane przez pracowników.

Ryzyko wprowadzanych zmian
Wielu dyrektorów słysząc słowo „zmiana” reaguje bardzo nieufnie. Zmiana kojarzy im się
ze stresem oraz chaosem w działalności przedsiębiorstwa. Tak być nie musi, wystarczy, Ŝe
więcej uwagi skierują na etap planowania, a nie jak to było zazwyczaj dopiero na etap realizacji.

Strona 6 z 63
TESTER.PL
W praktyce jednak rzadko zdarza się by wprowadzenie zmian było poprzedzone
formalnym procesem. Być moŜe jest to jedną z głównych przyczyn późniejszych negatywnych
skutków innowacji. Aby uniknąć niespodzianek warto przed wdroŜeniem zaplanować krok po
kroku sposób wprowadzania zmiany oraz opracować wszystkie warianty postępowania na
wypadek awarii.
Realizując proces zarządzania zmianą nie sposób nie otrzeć się o ryzyko poraŜki, kaŜda
zmiana moŜe bowiem zakończyć się niepowodzeniem i kaŜdy menedŜer zmiany musi mieć tego
świadomość. Co zrobić by tego uniknąć? Przede wszystkim wyeliminować sytuacje, w których
działamy w pośpiechu i w warunkach improwizacji. Zmiany powinny być planowane z
wyprzedzeniem. MenedŜer zmiany powinien opracować Plan Zmian (FSC) na okres np. jednego
miesiąca, w którym wskaŜe zarówno elementy konfiguracji i infrastruktury, jak i osoby, których
wprowadzane zmiany będą bezpośrednio dotyczyły. Tylko takie postępowanie daje szansę
uniknięcia

ewentualnej

poraŜki.

Celem

menedŜera

zmian

jest

zablokowanie

zmian

krótkowzrocznych, a więc takich, które w pierwszej chwili wydają się być dobrym rozwiązaniem,
jednak w szerszej perspektywie nie są nim.
Oczywiście takie postępowanie moŜe być skuteczne w przypadku zmian standardowych
czyli takich, które nie są wymuszone krótkim terminem wykonania. Co zrobić w przypadku zmian
pilnych? Jak wówczas uchronić organizację przed ryzykiem poraŜki? Przede wszystkim naleŜy
zachować zimną krew; nawet w krótkim okresie realizacji procesu zarządzania zmianą nie
powinno dojść do pominięcia Ŝadnego z etapów procesu. Pominięcie któregokolwiek z kroków
powoduje automatycznie wzrost ryzyka.

Słownik
CAB - Change Advisory Board
FSC - Forward Schedule of Changes
ITIL - Information Technology Infrastructure Library
SLA - Service Level Agreement
RfC - Request for Change

Strona 7 z 63
TESTER.PL

Jak skutecznie usprawnić testowanie oparte na
wymaganiach (Requirements Based Testing - RBT)
Moty Aharonovitz – Borland Software Corporation

Moty Aharonovitz pracuje jako senior director of Product Strategy w firmie Borland.
Ma ponad 15 lat doświadczenia w pracy nad rozwojem oprogramowania, dzięki czemu aktywnie
wspiera

i rozwija

wizję

Optymalizacji

Dostarczania

Oprogramowania

(SDO)

w

przedsiębiorstwach i firmach informatycznych oraz czynnie wspiera rozwiązania z zakresu
Zarządzania Jakością w Cyklu śycia aplikacji.
Kontakt: moty.aharonovitz@borland.com.

Strona 8 z 63
TESTER.PL

Jak skutecznie usprawnić testowanie oparte na
wymaganiach (Requirements Based Testing - RBT)
Moty Aharonovitz – Borland Software Corporation

Naszym głównym zadaniem jako menedŜerów ds. jakości oraz testerów jest znajdowanie
błędów w tworzonym oprogramowaniu. Na szczęście obecnie praca wielu z nas wykracza juŜ
poza ujawnianie oraz śledzenie „pluskw” i obejmuje bardziej krytyczne zagadnienia związane ze
sprawdzaniem, czy oprogramowanie które będzie wytwarzać nasza firma (jeszcze przez jego
finalnym wyprodukowaniem) spełni oczekiwania uŜytkowników. Aby tego dokonać wiele
organizacji zaczęło wykorzystywać Testowanie Oparte Na Wymaganiach (RBT).

Dzięki zastosowaniu RBT praca zespołów testowych staje się efektywniejsza - testy
bezpośrednio nawiązujące do konkretnych wymagań funkcjonalnych umoŜliwiają skuteczne
dotarcie do źródła problemu poprzez bezpośrednie powiązanie z wymaganiami na dowolnym
etapie cyklu Ŝycia aplikacji. W rezultacie otrzymujemy systematyczne i efektywne pokrycie
obszaru testów, co sprawia, Ŝe w zasadzie testujemy to, co ma największe znaczenie dla
naszego klienta.

Wyzwanie jakie napotykamy, wdraŜając RBT w naszej organizacji, to dopasowanie go do
istniejących procesów Działu Jakości i dotychczasowych praktyk testowania. Z własnego
doświadczenia mogę zaproponować trzy praktyczne sugestie, jak ulepszyć podejście RBT:

1.

Testujmy wcześnie i często, tak aby testowanie stało się czynnością równoległą do
procesu tworzenia, rozciągało na wszystkie role, uświadamiając wszystkim uczestnikom i
sponsorom projektu znaczenie jakości.

2. Testujmy z głową, nie instynktownie - zapewniając metodyczność i powtarzalność w
planie testów zwiększamy przewidywalność i mierzalność procesu testowania.
3. Testujmy z wykorzystaniem metryk - pozwoli to określić status produkcji i działalności
IT, umoŜliwiając zarządowi wgląd w stan wszystkich projektów IT w firmie oraz właściwie
ocenić nasz wkład i zaangaŜowanie.

Strona 9 z 63
TESTER.PL
Kryzys jakości
Wiele analiz, w tym te opracowane przez Standish Group Chaos Report, opisuje dotychczasowe
standardy w przemyśle IT: większość projektów IT wykracza poza załoŜony czas oraz budŜet.
Niewystarczająca jakość wytwarzanego oprogramowania jest najwaŜniejszym czynnikiem
odpowiedzialnym za niepowodzenia i często prowadzi do przebudowania kodu, wpływając na
zakres i jakość produktu finalnego. Ponowne praca nad tymi samymi fragmentami kodu wydłuŜa
czas powstawania aplikacji i znacząco pochłania zasoby, takŜe finansowe. Aby zmniejszyć
zagroŜenia wynikające z błędów, koniecznie musimy zdać sobie sprawę z coraz silniejszych
tendencji dbania o jakość powstających aplikacji juŜ od samego początku ich Ŝycia.

Doświadczenia firm z branŜy IT oraz badania trendów rynkowych jasno wskazują na dwa
podstawowe powody niskiej jakości tworzonych aplikacji:
1.) źle sformułowane wymagania
oraz
2.) niewłaściwe ich pokrycie przez testy.

Wady w specyfikacji wymagań
Wielu z nas często spotyka się ze skargami uŜytkowników stwierdzających ewidentne braki w
oprogramowaniu, które przeszło przez rygorystyczną kontrolę działu jakości i szereg testów.
Najczęstszy powód? Wymagania były niewłaściwie sformułowane juŜ od samego początku
projektu.

Wymagania do rozbudowanych systemów często są ustalane w dwóch równoległych procesach,
które następnie ewoluują równolegle w całym cyklu Ŝycia aplikacji. Wspomniane dialogi
powstają w wyniku postawienia sobie dwóch pytań: "co potrzebujemy zbudować?” oraz "co

moŜemy zbudować?” Jakość tych dialogów często określa ostateczną jakość budowanej
aplikacji.

Badania

wykonane

przez

Jamesa

Martina1

pokazują,

Ŝe

56

procent

wszystkich

zidentyfikowanych błędów w projektach informatycznych ma swoje korzenie w fazie tworzenia
wymagań. To samo badanie wykazuje, Ŝe około połowę błędów powstaje w wyniku niewłaściwie
napisanych, dwuznacznych, niejasnych i niepoprawnych wymagań. Druga połowa wad
oprogramowania moŜe zostać przypisana niewystarczającej specyfikacji (np. braku wymagań,

1

James Martin, “An Information Systems Manifesto”

Strona 10 z 63
TESTER.PL
które po prostu zostały pominięte w fazie analizy).

Wykres 1: Dystrybucja Wad w Projektach Oprogramowania

Inne studia ujawniają podobne odkrycia:
 82 procent przypadków wielokrotnie tworzonego kodu jest związanych się z błędami w
wymaganiach2
 Problemy w obszarze wymagań w 44 procentach przypadków są powodem odwołania
projektu3
 Tylko 54 procent początkowych wymagań projektowych jest właściwie zrozumiane4
 Tylko 45 procent zebranych wymagań zostaje właściwie uŜyte5

Podsumowując moŜemy stwierdzić, Ŝe dwa najwaŜniejsze problemy związane z jakością
wymagań to:
•

wymagania i specyfikacje są niekompletne (z powodu braku informacji od uŜytkowników
i sponsorów)

•

zebrane wymagania są nienajlepszej jakości (głównie w wyniku braku zrozumienia
potrzeb obu stron, znalezienia wspólnego języka oraz metod skutecznej weryfikacji
zebranych załoŜeń).

Problemy z pokryciem wymagań testami

2

Martin & Leffinwell
Standish Group: Chaos Report
4
Standish Group: Chaos Report
5
Jacobs
3

Strona 11 z 63
TESTER.PL
Kiedy temat jakości pojawia się na końcowym etapie cyklu wytwarzania aplikacji, testowanie
wymaga uprzedniego zakończenia fazy kodowania. W tym momencie zespół testerów znajduje
się pod presją czasu, a ich zadaniem staje się jak najszybsze zweryfikowanie poprawności
funkcjonalnej aplikacji. Ten etap często postrzegany jest jako wąskie gardło, które opóźnia
wdroŜenie aplikacji. W takich warunkach nie tylko trudnym jest upewnienie się, co do
poprawności wymagań, ale przede wszystkim ułoŜenie takiego test planu, który zapewni
poprawność i pełne pokrycie wymagań, jak równieŜ widoczność róŜnych aspektów jakościowych
testowanej aplikacji. Oczywiste jest, Ŝe praca testera przy powyŜszych załoŜeniach staje się
nieefektywna i, co tu ukrywać, frustrująca.

Osobnym wyzwaniem jest osiągnięcie zadowalającego poziomu pokrycia testami. ZłoŜoność
dzisiejszych aplikacji stanowczo nam w tym nie pomaga. Coraz trudniejsze staje się wykonanie
wszystkich moŜliwych scenariuszy, głównie ze względu na liczbę alternatywnych ścieŜek
przechodzenia przez aplikację. Jakakolwiek próba usystematyzowania (lub zautomatyzowania)
procesu połączenia wszystkich moŜliwych przypadków doprowadza nas po prostu to tak duŜej
liczby kombinacji scenariuszy testowych, Ŝe testowanie staje się bardzo trudnym i
długotrwałym, a przez to i nieopłacalnym z punktu widzenia organizacji zadaniem.

W dodatku, w wielu przedsiębiorstwach zarządzanie zmianą, głównie ze względu na
częstotliwość i skalę zmian w wymaganiach, staje się coraz trudniejsze. Stąd bez odpowiedniego
wsparcia w zakresie zarządzania zmianą, często gubimy się jeśli chodzi o śledzenie powiązań
pomiędzy zmieniającymi się wymaganiami oraz odzwierciedlenia tych zmian w przypadkach
testowych.

Sprawdzone praktyki RBT
Podejście RBT, jeśli zostało właściwie zdefiniowane, adresuje bezpośrednio zaleŜność
pomiędzy testami funkcjonalnymi a źródłem znalezionych błędów. Nawet, jeśli nasza organizacja
nie uŜywa podobnych praktyk dzisiaj, większości z nas zapewne nie są obce procesy
uwzględnione na poniŜszym wykresie.

Strona 12 z 63
TESTER.PL

Wykres 2: Przebieg procesu RBT

Równolegle z aktualnymi procesami testowymi, organizacje mogą spróbować zaadoptować
następujące praktyki RBT:

1. Testujmy wcześnie i często
W momencie, gdy mamy przygotowane wymagania oraz gotowy projekt i fragmenty kodu,
przejrzyjmy je pod kątem celów biznesowych, przypadków uŜycia (use cases) i przypadków
testowych (test cases). Starajmy się testować nasz projekt na jak najwcześniejszym etapie,
poniewaŜ usterki zlokalizowane we wczesnych fazach rozwoju projektu są tańsze i łatwiejsze do
usunięcia, w wyniku czego moŜemy się spodziewać znacząco mniejszej ilości „niespodzianek” na
dalszych jego etapach. Testowanie powinno stać się czynnością równoległą do procesu
tworzenia aplikacji. W ten sposób sponsorzy projektu w większym stopniu uświadomią sobie rolę
jakości w całym procesie. Dzięki takiemu podejściu, testowanie będzie postrzegane juŜ nie jako
tzw. „wąskie gardło” w produkcji oprogramowania, a bardziej jako kluczowy czynnik w ogólnym
procesie zapewnienia jakości powstających systemów.

PoniewaŜ sukces projektu informatycznego moŜe być bezpośrednio powiązany z solidnym
zrozumieniem i zdefiniowaniem wymagań, promowanie RBT staje się doskonałą wizytówką firm
dbających o jakość swoich produktów. Najlepsze praktyki w procesie Jakości Wymagań RBT
zawierają:

Strona 13 z 63
TESTER.PL

•

Walidacja wymagań w odniesieniu do celów biznesowych -

optymalizacja zakresu

projektu przez zapewnianie, Ŝe kaŜde wymaganie zaspokaja przynajmniej jeden
biznesowy cel. JeŜeli brakuje powiązań pomiędzy wymaganiami a celami biznesowymi,
konieczna jest weryfikacja tych pierwszych.
•

Przegląd wymagań przez uŜytkowników - zmiany dokonywane w wymaganiach powinny
być przeglądane i akceptowane przez uŜytkowników i odbiorców końcowych. W ten
sposób upewnimy się, Ŝe dodatkowa praca wynikająca z modyfikacji wymagań nie
będzie zmarnowana.

•

Tworzenie przypadków uŜycia - kaŜdy przypadek uŜycia powinien być powiązany z
odpowiednim wymaganiem. Jeśli któryś z przypadków uŜycia nie ma takiego
przyporządkowania oznacza, Ŝe wymagania są niekompletne.

•

Wykorzystanie technik analizy języka - wyszukiwanie i poprawianie problematycznych
wyraŜeń / sformułowań w opisie wymagań, pozwala na uniknięcie ich wieloznaczności i
niejasności oraz wyeliminowanie pomyłek. Pozostawienie takich fraz bez poprawy
powoduje iŜ mogą być one w późniejszych krokach interpretowane w róŜny sposób przez
róŜne osoby, co z kolei moŜe prowadzić do zakłopotania i błędów w kolejnych etapach.
Dwuznaczne określenia produkują równieŜ „nietestowalne” przypadki uŜycia.

2. Testujmy z głową, nie instynktownie
Większość przedsiębiorstw ceni doświadczonych testerów, licząc, Ŝe „wyłapią” błędy które inni,
mniej doświadczeni testerzy mogliby przeoczyć. O ile jednak pojedynczy „ekspert ds. jakości”
moŜe być kuszącym ograniczeniem kosztów firmy, o tyle sytuacja taka zwiększa ryzyko
polegania na instynkcie jednej osoby zamiast na grupowym racjonalnym podejściu.

Obecnie systematyczne i rygorystyczne planowanie przypadków testowych nie jest powszechnie
stosowaną praktyką. Częściej spotykamy się raczej z podejściem bardziej intuicyjnym,
polegającym na wyczuciu, co jednak w efekcie moŜe prowadzić do nieprzewidywalnej jakości
produktów końcowych. Doświadczenia firm stosujących podejście bazujące na RBT wskazują na
fakt, Ŝe stosowanie metodycznego i systematycznego projektowania przypadków testowych jest
najefektywniejszą polityką w zakresie jakości, zwłaszcza w perspektywie rozwoju firmy.
Rygorystyczne i systematyczne zasady projektowania przypadków testowych uniezaleŜniają

Strona 14 z 63
TESTER.PL
proces kontroli jakości od konkretnych testerów. W zamian wnoszą metodyczną powtarzalność
do procesu planowania testów, zapewniając tym samym przewidywalną powtarzalność pokrycia
testami. Organizacje bazujące na tej metodzie duŜo łatwiej mogą wprowadzać techniki
optymalizacji

procesu

testowania,

redukując

przypadki

testowe

do

liczby

efektywnie

pokrywającej wymagania, dzięki czemu uzyskują przyspieszenie cyklu testowego oraz utrzymują
ten proces na poziomie ułatwiającym zarządzanie nim.

W większości organizacji niemal wszyscy analitycy biznesowi uŜywają „naturalnego” języka do
opisu wymagań. Wiemy, iŜ uŜywanie takiego języka moŜe utrudnić osiągnięcie pełnego pokrycia
aplikacji testami, poniewaŜ testerzy bazując na potocznie sformułowanych wymaganiach muszą
odwoływać się do własnej intuicji, aby określić stopień pokrycia testami obszaru tak
zdefiniowanych wymagań. Innymi słowy, pracując z potocznie sformułowanymi wymaganiami
organizacje nie mają moŜliwości dokładnej kontroli pokrycia ich przypadkami testowymi, co
skutkuje w późniejszych błędach w wypuszczonej wersji aplikacji.

W celu usystematyzowania pokrycia wymagań odpowiadającymi im testami firmy i działy
produkujące oprogramowanie powinny skupić się na sformalizowaniu reprezentacji wymagań.
Kiedy uda się to osiągnąć, będzie moŜliwe stworzenie szkieletu przypadków testowych
zapewniających optymalne pokrycie wymagać, docelowo zaś powstaną z nich juŜ właściwe
testy, które będziemy mogli uruchamiać i przeprowadzać.

Aby wprowadzić usystematyzowanie i odpowiednią strukturę do wymagań sformułowanych przy
pomocy naturalnego języka opisu moŜna skorzystać z wielu dostępnych technik. Ich celem jest
odkrycie

związków

przyczynowo-skutkowych

zawartych

w

wymaganiach,

a

przez

to

przedstawienie ich w postaci zestawu warunków (przyczyn) oraz wynikających z nich akcji
(skutków). Tabele przyczynowo skutkowe są jedną z takich technik. Innym sposobem na
osiągnięcie podobnego rezultatu jest przedstawienie wymagań w postaci wykresów przepływu
(flowchart), które w naturalny sposób pokazują związki przyczynowo-skutkowe, jak równieŜ
gałęzie warunkowe odpowiednich akcji.

To swoiste „tłumaczenie” wymagań pozwala na duŜo łatwiejsze zbudowanie w oparciu o nie
konkretnych przypadków testowych. Logiczny zestaw tych ostatnich moŜe być stworzony
automatycznie bądź „ręcznie” tak, by stanowił odbicie zdefiniowanych i przetłumaczonych w
powyŜszy sposób wymagań. NaleŜy pamiętać, Ŝe tak stworzony zestaw przypadków testowych
moŜe zawierać nachodzące na siebie testy. W celu zoptymalizowania ich liczby przy zachowaniu

Strona 15 z 63
TESTER.PL
pełnego pokrycia wymagań moŜemy skorzystać z tablic decyzyjnych (w przypadku zastosowania
tabel przyczynowo-skutkowych na etapie strukturyzowania wymagań). W przypadku, jeśli w tym
celu opracowane zostały uprzednio wykresy zamiast tabel, optymalizacja sprowadza się do
znalezienia unikatowych ścieŜek przepływu na wykresach. Do tego celu moŜna wykorzystać
wiele sprawdzonych algorytmów.

Nawet po zaprojektowaniu przypadków testowych, wciąŜ będziemy mieli do czynienia z
prawdopodobieństwem występowania w nich błędów. Aby wykryć potencjalne błędy,
organizacje stosujące podejście RBT angaŜują zarówno analityków jak i uŜytkowników
końcowych do weryfikacji przypadków testowych zanim powstaną na ich podstawie właściwe
testy. To podejście pozwala wszystkim zainteresowanym ponownie zapoznać się z wymaganiami
i przypadkami testowymi, co daje moŜliwość ich dokładniejszej weryfikacji, jak równieŜ
wyłapania błędów powstałych w procesie przenoszenia wymagań z języka opisowego na
systematyczny.
Dodatkowo, firmy charakteryzujące się podejściem do cyklu tworzenia aplikacji jako całości,
starają się interaktywnie włączyć zadania Działu Jakości w prace analityków i programistów, co
staje się moŜliwe właśnie dzięki przejściu na usystematyzowane i zoptymalizowane przypadki
testowe wypracowane w poprzednich fazach.

W opisanym przedsiębiorstwie projekt aplikacji bywa przeglądany pod kątem przypadków
testowych, poniewaŜ stanowią one niejako inną formę zdefiniowanych wymagań. W ten sposób
zespoły projektowe nabierają pewności, Ŝe projekt spełnia załoŜenia i oczekiwania w postaci
wymagań. JeŜeli model ich nie spełnia moŜe to być oznaką, Ŝe albo nie odpowiada tymŜe, i
wymaga dalszej pracy, albo Ŝe jest problem w samych wymaganiach, co pociąga za sobą
dodatkową pracę analityków.

Aby uniknąć kosztownych przeróbek, przypadki testowe powinny być przeglądane równieŜ przez
programistów tworzących kod. To zapewni im dobre zrozumienie, które elementy oraz w jakim
zakresie będą testowane, a jednocześnie da strukturalny zestaw wymagań.

Ostatecznie

pojedyncze

moduły

kodu

powinny

zostać

zweryfikowane

pod

kątem

uporządkowanych wymagań tak, aby upewnić się, Ŝe powstały kod w pełni odpowiada
załoŜeniom. Praktyka pokazuje, Ŝe duŜo łatwiej jest nam dopasować postać algorytmiczną kodu
do uporządkowanych wymagań niŜ do ich niestrukturalnej postaci.

Strona 16 z 63
TESTER.PL
3. Nie zapominajmy o miarach i usprawnieniach
Istnieje opinia, Ŝe czego nie da się zmierzyć, nie istnieje. UŜywając metody RBT firmy mogą nie
tylko zarządzać procesem zarządzania jakością, ale i go usprawniać. W procesie RBT moŜe być
stosowanych wiele róŜnych miar kwantyfikujących status projektu oraz aktywność jego
członków. Stanowi to duŜą pomoc dla menedŜerów działu oraz menedŜerów projektu,
pozwalając na dokładny wgląd w zakres całego portfolio IT.

Przykłady informacji, które powinny być mierzone:
 procent wymagań przejrzanych przez projektantów i programistów
 procent wymagań, które zawierają dwuznaczności
 procent wymagań o strukturalnej formie
 procent formalnych wymagań pokrytych przypadkami testowymi
 logiczne i faktyczne pokrycie kodu

Rola śledzenia zmian w RBT
Śledzenie zmian odgrywa takŜe istotną rolę w organizacjach wykorzystujących RBT - od
podtrzymywania stałego przepływu informacji o zmianach w stosunku do wymagań,
przypadków testowych i testów jest krytyczne. Te informacje są niezbędne dla prawidłowego
monitorowania postępu i stanu projektu, jak równieŜ do właściwego zarządzania zmianami
wymagań. Bez tej wiedzy trudne jest dokładne określenie, które przypadki testowe i testy
powinny zostać zmodyfikowane w przypadku zmiany w wymaganiach.

Nawet jeśli rozumiemy znaczenie śledzenia zmian, w wielu firmach tworzących oprogramowanie
ta wiedza wciąŜ pozostaje bardzo trudna do uchwycenia we właściwy sposób. Podstawowym
powodem tego jest to, Ŝe większość narzędzi dostępnych obecnie na rynku wymaga od niemal
wszystkich członków zespołów projektowych ręcznego wprowadzania i zarządzania śledzeniem
zmian. Z oczywistych powodów takie podejście jest raczej niemoŜliwe do zaakceptowania. Aby
podołać temu wyzwaniu, naleŜy powaŜnie zastanowić się nad rozwiązaniami umoŜliwiającymi
połączenia pomiędzy produktami poszczególnych faz wytwarzania oprogramowania.

Pierwszy krok do Zarządzania Jakością Cyklu śycia Aplikacji
Stosowanie solidnych praktyk RBT przez organizacje tworzące oprogramowanie bardzo szybko
dostarcza im odpowiednie narzędzia i procesy umoŜliwiające maksymalizację wartości
biznesowej działalności.

Strona 17 z 63
TESTER.PL
Zespoły wdraŜające podejście RBT poprzez fakt, Ŝe robią ten pierwszy krok w celu zwiększenia
jakości tworzonych produktów, stają się tym samym inicjatorami szeregu zmian prowadzących
do implementacji Zarządzania Jakością w Cyklu śycia Aplikacji (Lifecycle Quality Management –
LQM). Poprzez połoŜenie nacisku na zapewnienie jakości na wszystkich etapach wytwarzania
oprogramowania, a nie koncentrowanie się na jakości samego kodu, działania z obszaru LQM
zapewniają firmom podniesienie norm jakości i usług oraz systematyczną redukcję kosztów
związanych z wielokrotnym powtarzaniem tej samej pracy lub prób opanowania złoŜonych
projektów.

Ponadto, działania LQM znacząco przyspieszą ścieŜkę do Optymalizacji Procesu Dostarczania
Oprogramowania (Software Delivery Optimization - SDO), która z kolei pomaga przekształcić
tworzenie i rozwój oprogramowania w zarządzalny proces biznesowy, zapewniając tym samym
zwiększoną kontrolę, przewidywalność oraz wydajność w całym procesie dostarczania
oprogramowania.

Strona 18 z 63
TESTER.PL

Obiekt pozorny. A moŜe coś innego?
Kierunek rozwoju testów jednostkowych
Mateusz Bukowski i Paweł Paterek

Mateusz Bukowski jest absolwentem Akademii Górniczo
Hutniczej w Krakowie, kierunku Informatyka na Wydziale
Elektrotechniki, Automatyki, Informatyki i Elektroniki. Obecnie jest
pracownikiem Motorola Polska Electronics Sp. z o.o. gdzie zajmuje się
testowaniem systemu bezpieczeństwa publicznego TETRA. Jego
zainteresowania to Ŝeglarstwo, cross country i rozwiązywanie
łamigłówek logicznych.

Paweł Paterek jest absolwentem Wydziału Elektrotechniki,
Automatyki, Informatyki i Elektroniki Akademii Górniczo-Hutniczej w
Krakowie, kierunek Elektronika i Telekomunikacja. Obecnie jest
pracownikiem firmy Motorola Polska Electronics Sp. z o.o., w której
zajmuje się testowaniem oprogramowania systemów czasu
rzeczywistego oraz systemów wbudowanych dla stacji bazowych w
standardzie TETRA. Jego dziedziną zainteresowań jest modelowanie i
ocena efektywności pracy sieci komputerowych.

Strona 19 z 63
TESTER.PL

Obiekt pozorny. A moŜe coś innego?
Kierunek rozwoju testów jednostkowych
Mateusz Bukowski i Paweł Paterek

W dzisiejszym świecie, dostarczenie produktu na czas, nie jest juŜ najwaŜniejszym
celem. DuŜo większe znaczenie, zaczyna odgrywać jakość dostarczanego oprogramowania.
PoniewaŜ testowanie zabiera coraz więcej czasu i wysiłku, niezbędne jest uproszczenie technik
testerskich przy jednoczesnym zachowaniu poziomu znajdowanych defektów. W tym artykule
zapoznamy się z obiektami pozornymi i zaślepkami, które wnoszą nową jakość do testów oraz
pokaŜemy, dlaczego warto korzystać z tych pierwszych.

Zanim przedstawimy nowe sposoby testowania, przypomnijmy, czego dotyczą testy
jednostkowe oraz czym jest programowanie sterowane testami.
Test jednostkowy (Unit test) to procedura mająca na celu walidację poprawności
działania danej jednostki kodu źródłowego. Przez jednostkę kodu źródłowego rozumiemy
najmniejszą i moŜliwą do przetestowania część oprogramowania. W programowaniu obiektowym
(ang. Object Oriented Programming) taką jednostkę kodu stanowi klasa [8]. Testy jednostkowe
odgrywają znaczącą rolę w procesie wytwarzania oprogramowania. UmoŜliwiają one wykrycie
wielu błędów w kodzie juŜ na etapie implementacji lub we wstępnej fazie testów. Pisane są
najczęściej przez te same osoby, które zajmują się tworzeniem danego fragmentu
oprogramowania.
Testy jednostkowe z załoŜenia są proste i szybkie w uruchomieniu. Testują
wyodrębnioną część funkcjonalności w całkowitej izolacji od reszty systemu. Pozwala to na
zautomatyzowanie znaczącej części procesu testowania. Tym samym pozwala to na częste
sprawdzanie czy wprowadzane zmiany w istniejącym kodzie nie powodują błędów.
Testy jednostkowe nie weryfikują wszystkich wymagań funkcjonalnych danego systemu.
Jest to zadanie testów akceptacyjnych wykonywanych przez odbiorcę oprogramowania [4],[10].
Ten rodzaj testów nie ma za zadania sprawdzać interakcji między róŜnymi obiektami, co wynika
wprost z ich definicji. Nie mogą wobec tego zastąpić testów integracyjnych i systemowych w
procesie testowania. Są jednak ich bardzo dobrym uzupełnieniem.

Strona 20 z 63
TESTER.PL
Bardzo często testowane klasy posiadają powiązania do innych klas. Utworzenie
referencji do takich obiektów w środowisku testowym niejednokrotnie jest niemoŜliwe. Jednym z
rozwiązań tego problemu są właśnie obiekty zastępcze powszechnie zwane obiektami pozornymi
(mock). Są to specjalnie przygotowane przez nas implementacje interfejsów mogące zastąpić
problematyczne części kodu oraz ułatwić wykonanie testów.
Programowanie sterowane testami (Test Driven Develomnent TDD) zakłada, Ŝe
Ŝaden fragment oprogramowania nie powstanie zanim nie napiszemy do niego odpowiedniego
testu. Pisany kod musi spełnić wymagania testu, a następnie moŜna przeprowadzić na nim
odpowiedni refactoring1, który pozwoli na ustalenie ostatecznego nazewnictwa metod czy
obiektów. Stosowanie tej metodologii ma róŜne zalety, m.in. pisząc pełny zbiór testów przed
napisaniem właściwego kodu piszemy zarazem specyfikację jego działania, po czym sama
walidacja jest juŜ automatyczna. PoniewaŜ przed napisaniem kodu wszystkie testy kończą się
błędem, a w miarę jak nasz kod spełnia coraz więcej wymagań tych błędów jest mniej, to
otrzymujemy tym samym miarę, w jakim stopniu nasz kod realizuje załoŜenia projektowe
[6],[1].
TDD pozwala małymi krokami (poprzez napisanie najmniejszej części kodu, która
powoduje pomyślne przejście testu) na sukcesywne i skuteczne realizowanie danego projektu.
Jednocześnie tak napisany kod jest mniej skomplikowany, dostajemy wysokie pokrycie testami,
a odnajdywanie błędów staje się znacznie prostsze. Jednak, kiedy test dotyczy kodu
odwołującego się do baz danych, zdalnych obiektów lub połączeń sieciowych napisanie go moŜe
być bardzo problematyczne. Chcąc jednak być w zgodzie z koncepcją metodologii TDD i
testować równieŜ takie obiekty, musimy zastąpić obiekty, do których odwołuje się testowany
kod i zaimitować ich obecność w testowanym kodzie. Jednym z moŜliwych sposobów testowania
takiego kodu jest wykorzystanie wspomnianych wyŜej lekkich obiektów pozornych lub duŜo
cięŜszych zaślepek [11],[14].
W naszych rozwaŜaniach będziemy posługiwać się następującym przykładem. Mamy
bazę danych ksiąŜek. KaŜdy element opisany jest następującymi parametrami: autor, tytuł oraz
identyfikator. Ponadto w bazie przechowywana jest równieŜ liczba dostępnych egzemplarzy
kaŜdej ksiąŜki. Z bazą danych moŜemy połączyć się poprzez BookManager’a, z którego korzysta
BookClient.

Strona 21 z 63
TESTER.PL

Rys. 1 Schemat systemu zamawiania ksiąŜek

public interface BookManager {
public boolean connect();
public boolean disconnect();
public TreeMap<Book, Integer> bookList();
public boolean order(BookOrder bookOrder) throws BookNotFoundException,
NotEnoughBooksException;
}
public class BookClient {
public static final String CONNECT_ERROR = "Database connect error";
public static final String DISCONNECT_ERROR = "Database disconnect error";
public static final String BOOK_NOT_FOUND_ERROR = "Book not found error";
public static final String NOT_ENOUGH_BOOKS_ERROR = "Not enough books
error";
public static final String AVAILABLE_BOOKS = "Available books:";
public static final String ORDERED_BOOKS = "Ordered books:";
public static final String BOOK_ORDER_FAIL = "Book order failed error";
private BookManager bookManager;
private String message;
public BookClient() {
bookManager = new DefaultBookManager();
message = "";
}
public BookClient(BookManager bookManager) {
this.bookManager = bookManager;
message = "";
}
public String getMessage() {

Strona 22 z 63
TESTER.PL
return message;
}
public void getBookList() {
if (!bookManager.connect()) {
message = CONNECT_ERROR;
return;
}
message = AVAILABLE_BOOKS + bookListToString(bookManager.bookList());
if (!bookManager.disconnect()) {
message = DISCONNECT_ERROR;
return;
}
}
public void orderBooks(BookOrder bookOrder) {
if (!bookManager.connect()) {
message = CONNECT_ERROR;
return;
}
try {
if (bookManager.order(bookOrder)) {
message = ORDERED_BOOKS +
bookListToString(bookOrder.getBookList());
} else {
message = BOOK_ORDER_FAIL;
}
} catch (BookNotFoundException e) {
message = BOOK_NOT_FOUND_ERROR;
} catch (NotEnoughBooksException e) {
message = NOT_ENOUGH_BOOKS_ERROR;
}
if (!bookManager.disconnect()) {
message = DISCONNECT_ERROR;
return;
}
}
public static String bookListToString(TreeMap<Book, Integer> bookList) {
StringBuilder result;
result = new StringBuilder();
for (Book book: bookList.keySet()) {
result.append("n");
result.append(book.getId());
result.append(" ");
result.append(book.getAuthor());
result.append(" ");
result.append(book.getTitle());
result.append(" ");
result.append(bookList.get(book));
}
return result.toString();
}

Strona 23 z 63
TESTER.PL
}

Implementacja

pozostałych

NotEnoughBooksException

oraz

klas:

Book,

BookOrder,

BookNotFoundException

nie

jest

DefaultBookManager,
istotna

w

naszych

rozwaŜaniach. Niezbędna jest implementacja funkcji equals, hashCode oraz compareTo w klasie

Book, poniewaŜ będziemy uŜywać tych obiektów jako kluczy w TreeMap’ie.
Testowaną

klasą

jest

BookClient.

BookManager

jest

interfejsem,

który

będą

implementować konkretne obiekty. Domyślnie jest to klasa DefaultBookManager. Na potrzeby
testów dodaliśmy drugi konstruktor, w którym przekazujemy specjalnie spreparowany

BookManager. Inną stosowaną praktyką jest równieŜ korzystanie z metod ‘set’.
Wspomniane wcześniej obiekty pozorne nie są jedyną moŜliwością na zastąpienie
prawdziwych obiektów w obiekcie testowanym. Często teŜ bywają mylone z podobnymi do nich
w zastosowaniu, lecz róŜniącymi się funkcjonalnie obiektami atrapami (ang. dummy

objects), obiektami falsyfikatami (ang. fake objects) oraz zaślepkami (ang. stubs).
Obiekty atrapy przekazywane są do obiektu testowanego, ale nigdzie nie są uŜywane i
zazwyczaj słuŜą jedynie do wypełnienia listy parametrów testowanej metody.
Falsyfikaty (w Ŝargonie informatycznym często nazywane fake’ami) posiadają
namiastkę działającej implementacji, najczęściej stworzoną ręcznie w ramach kodu testów (na
przykład w postaci klasy anonimowej). Implementacja ta jest zazwyczaj skrócona do minimum,
co sprawia, Ŝe nie nadają się one do uŜycia w prawdziwym kodzie. Wadami falsyfikatów są: czas
poświęcony na ich stworzenie, zwiększenie zawartości całego projektu, a takŜe konieczność ich
utrzymywania, w trakcie zmiany interfejsów kodu, z których one korzystają.
Zaślepki mogą być napisane ręcznie lub generowane automatycznie i są najczęściej
stosowane wtedy, gdy kod nie jest znany, nie jest gotowy na etapie testowania danego obiektu
lub, gdy nie ma moŜliwości dostępu do danej części kodu, co czasami znacznie ułatwia i
przyspiesza proces tworzenia oprogramowania [2], [5]. Zaślepki symulują zachowanie
prawdziwego kodu. W naszym przykładzie będzie łączył się z prawdziwą bazą danych. Innym
razem w celu dogłębnego przetestowania z uŜyciem tej techniki musielibyśmy uruchomić serwer
http. Zaślepki są bardzo kosztowne w utrzymaniu. Jeśli są stosowane we wczesnych fazach
testowania mogą później słuŜyć jako zaląŜki dla implementacji prawdziwych klas.
Obiektami pozornymi zajmiemy się dokładniej w dalszej części.
Implementacja BookManager’a moŜe być jednym z powyŜszych typów: falsyfikat,
zaślepka, obiekt pozorny.
Przyjrzyjmy się teraz konkretnym falsyfikatom i zaślepkom oraz odpowiadającym im
testom jednostkowym.

Strona 24 z 63
TESTER.PL

public class FakeBookManager implements BookManager {
private TreeMap<Book, Integer> bookList;
private int call;
public FakeBookManager() {
bookList = new TreeMap<Book, Integer>();
bookList.put(new Book(101, "Ernest Hemingway", "The Old Man and the
Sea"), 3);
bookList.put(new Book(102, "Gabriel Garcia Marquez", "One Hundred
Years of Solitude"), 5);
bookList.put(new Book(103, "Joanne Kathleen Rowling", "Harry Potter
and the Deathly Hallows"), 27);
call = 0;
}
public boolean connect() {
return true;
}
public boolean disconnect() {
return true;
}
public TreeMap<Book, Integer> bookList() {
return bookList;
}
public boolean order(BookOrder bookOrder) throws BookNotFoundException,
NotEnoughBooksException {
call++;
switch (call % 8) {
case 0:
case 2:
case 4:
return true;
case 1:
case 3:
case 5:
return false;
case 6:
throw new BookNotFoundException();
case 7:
throw new NotEnoughBooksException();
default:
return true;
}
}
}

Strona 25 z 63
TESTER.PL
Jak widzimy nasz falsyfikat ma zaszytą namiastkę logiki. Wiedząc, który raz wywołujemy metodę

order(), moŜemy sprawdzić czy BookClient zachowuje się w poprawny sposób.
public class BookClientTestFakeBookManager extends TestCase {
private TreeMap<Book, Integer> bookList;
private static final Book QUO_VADIS = new Book(1001, "Henryk Sienkiewicz",
"Quo Vadis");
private static final Book PAN_TADEUSZ = new Book(2001, "Adam Mickiewicz",
"Pan Tadeusz");
private static final Book CHLOPI = new Book(3001, "Wladyslaw Reymont",
"Chlopi");
@Override
protected void setUp() throws Exception {
super.setUp();
bookList = new TreeMap<Book, Integer>();
bookList.put(QUO_VADIS, 2);
bookList.put(PAN_TADEUSZ, 3);
bookList.put(CHLOPI, 5);
}
public void testOrderGlobal() {
BookClient client;
FakeBookManager fake;
BookOrder bookOrder;
String message;
fake = new FakeBookManager();
client = new BookClient(fake);
bookOrder = new BookOrder();
bookOrder.addBook(CHLOPI);
client.orderBooks(bookOrder);
message = BookClient.BOOK_ORDER_FAIL;
assertEquals(message, client.getMessage());
client.orderBooks(bookOrder);
message = BookClient.ORDERED_BOOKS +
BookClient.bookListToString(bookOrder.getBookList());
assertEquals(message, client.getMessage());
client.orderBooks(bookOrder);
client.orderBooks(bookOrder);
client.orderBooks(bookOrder);
client.orderBooks(bookOrder);
message = BookClient.BOOK_NOT_FOUND_ERROR;
assertEquals(message, client.getMessage());
client.orderBooks(bookOrder);
message = BookClient.NOT_ENOUGH_BOOKS_ERROR;
assertEquals(message, client.getMessage());

Strona 26 z 63
TESTER.PL
}
}

W przykładzie z zaślepką uŜyliśmy najprostszego rozwiązania. Jako bazy danych uŜyliśmy
Accessa. UŜywanie tej aplikacji nie wymaga obszernej wiedzy z zakresu zarządzania bazami
danych. Access jest łatwy do konfiguracji, prosty w obsłudze i świetnie nadaje się do celów
testowych. Gdybyśmy chcieli symulować serwer http nie musimy mieć postawionego Apache’a.
MoŜemy uŜyć prostego symulatora Jetty, który jest prostą aplikacją javową.
public class StubBookManager implements BookManager {
private Connection conn;
private String url;
private String username;
private String password;
public StubBookManager(String location) {
url = "jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=" +
location;
username = "";
password = "";
}
public boolean connect() {
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
conn = DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
return false;
} catch (SQLException e) {
return false;
}
return true;
}
public boolean disconnect() {
try {
conn.close();
} catch (SQLException e) {
return false;
}
return true;
}
public TreeMap<Book, Integer> bookList() {
TreeMap<Book, Integer> result;
Statement st;
ResultSet rs;
Book book;
int quantity;

Strona 27 z 63
TESTER.PL
result = new TreeMap<Book, Integer>();
try {
st = conn.createStatement();
rs = st.executeQuery("SELECT * FROM books");
while (rs.next()) {
book = new Book(rs.getInt("id"), rs.getString("author"),
rs.getString("title"));
quantity = rs.getInt("quantity");
result.put(book, quantity);
}
} catch (SQLException e) {
return new TreeMap<Book, Integer>();
}
return result;
}
public synchronized boolean order(BookOrder bookOrder) throws
BookNotFoundException, NotEnoughBooksException {
TreeMap<Book, Integer> bookList;
Statement st;
int quantity;
bookList = bookList();
for (Book book: bookOrder.getBookList().keySet()) {
if (!bookList.containsKey(book)) {
throw new BookNotFoundException();
}
quantity = bookList.get(book) - bookOrder.getBookList().get(book);
if (quantity < 0) {
throw new NotEnoughBooksException();
}
try {
st = conn.createStatement();
st.executeUpdate(
"UPDATE books" +
" SET quantity = " + quantity +
" WHERE id = " + book.getId() +
" AND author = '" + book.getAuthor() + "'" +
" AND title = '" + book.getTitle() + "'");
} catch (SQLException e) {
return false;
}
}
return true;
}
}

Do poprawnego uruchomienia poniŜszego testu konieczne jest dodanie katalogu ‘data’ z
odpowiednią plikiem bazy danych ‘book.mdb’.

Strona 28 z 63
TESTER.PL

public class BookClientTestStubBookManager extends TestCase {
public void testConnectOk() {
BookClient client;
StubBookManager stub;
String message;
stub = new StubBookManager("./data/book.mdb");
client = new BookClient(stub);
client.getBookList();
message = BookClient.AVAILABLE_BOOKS;
assertEquals(message, client.getMessage());
}
public void testConnectFail() {
BookClient client;
StubBookManager stub;
String message;
stub = new StubBookManager("dummy.mdb");
client = new BookClient(stub);
client.getBookList();
message = BookClient.CONNECT_ERROR;
assertEquals(message, client.getMessage());
}
}

Obiekt pozorny (mock) jest imitacją stworzoną na potrzeby testu jednostkowego w
celu sprawdzenia poprawności współpracy testowanego obiektu z jego najbliŜszym otoczeniem
[9]. Bardziej formalne interpretacje tego pojęcia to:
a) obiekt pozorny jest pewną atrapą zastępującą prawdziwy obiekt, który jest niedostępny
lub trudny do uŜycia w scenariuszu testowym
b) obiekt pozorny musi posiadać mechanizm do automatycznej walidacji ustawionych dla
niego wcześniej oczekiwanych zachowań [4],[10].
Obiekty te powinny zawsze zwracać z góry określone dane, umoŜliwiając testowanie logiki
aplikacji w jednoznaczny sposób, bez konieczności odwoływania się do prawdziwych obiektów,
bądź baz danych. Ich logika oraz implementacja powinna być najprostsza jak to tylko moŜliwe
oraz niezaleŜna od innych obiektów pozornych, bądź fragmentów testowanej aplikacji.
Mechanizm automatycznej walidacji powinien zadziałać w momencie wystąpienia błędu,
umoŜliwiając szybkie przerwanie testu i łatwą lokalizację przyczyny problemu [4],[9].

Strona 29 z 63
TESTER.PL
Program

zorientowany

wzajemnie
przetestować

ze

sobą
działanie

obiektowo

jest

siecią

współpracujących.
obiektu,

naleŜy

obiektów

Aby

dobrze

przetestować

poprawność jego współpracy z obiektami, z którymi wymienia
informacje w prawdziwej aplikacji. PoniewaŜ unit test polega
na testowaniu obiektu w izolacji od innych obiektów
istniejących w aplikacji, obiekty wymieniające informacje z
obiektem testowanym muszą zostać podmienione na obiekty
Rys. 2 Wymiana informacji
Test – Obiekt – Obiekt Pozorny

pozorne, patrz rys. 2 [6]. W chwili, kiedy na obiekcie
zastępczym wystąpi inne wywołanie metody niŜ oczekiwane,

lub wywołanie nastąpi z niewłaściwymi parametrami lub teŜ, gdy wywołanie wystąpi, a nie było
ono jawnie oczekiwane test jest automatycznie przerywany i jego wynik ustawiany jest na
negatywny [7].
Powody, dla których uŜycie obiektów pozornych moŜe być nieocenione, są przeróŜne.
Prawdziwy obiekt wykazuje zachowanie niedeterministyczne. Jest trudno konfigurowalny. Jego
działanie jest znacznie wolniejsze (niepotrzebnie wydłuŜając test). Część jego zdarzeń jest
trudna do wywołania (szczególnie sytuacje dotyczące występowania błędów). Prawdziwy obiekt
moŜe mieć lub stanowić interfejs uŜytkownika. NajwaŜniejsze jest to, Ŝe prawdziwy obiekt moŜe
jeszcze nie być w ogóle stworzony lub test moŜe chcieć wywołać na nim specyficzne zapytanie,
które nie jest dostępne w prawdziwym obiekcie w danej chwili [13],[10].
Korzystanie z obiektów pozornych posiada charakterystyczny wzorzec, który moŜna
stosować podczas tworzenia testów. Najpierw tworzymy instancję obiektu pozornego, następnie
ustawiamy ich stan (parametry i atrybuty uŜywane przez testowany obiekt), a na koniec
ustawiamy ich oczekiwane zachowania (oraz ewentualne wartości zwracane), które powinny być
wywołane przez testowany obiekt. Testowany kod wywołujemy podając jako parametry
wcześniej przygotowane obiekty zastępcze i sprawdzamy czy wszystkie wywołania, które się do
nich odnoszą są zgodne z naszymi oczekiwaniami [3].
Praktycznym ułatwieniem stosowania tej techniki moŜe być stworzenie interfejsu
opisującego dany obiekt, a następnie implementacja tego interfejsu zarówno dla kodu, jak i w
celu stworzenia obiektu zastępczego uŜywanego podczas testu. Jednym z typowych przykładów
zastosowania techniki obiektów pozornych, moŜe być testowanie logiki biznesowej, odwołującej
się do zdalnej bazy danych (rys. 3). Sterownik bazy danych implementuje najczęściej
standardowy interfejs dostępu do bazy danych. Ten sam interfejs moŜna uŜyć do stworzenia
obiektu pozornego, dzięki czemu nie musimy mieć dostępu do prawdziwej bazy danych.

Strona 30 z 63
TESTER.PL
Ze względu na sposób tworzenia (uŜytą technikę
programowania) obiektów pozornych moŜna podzielić na dwa
rodzaje:

obiektów

pozornych

statycznych

oraz

obiektów

pozornych dynamicznych. Obiekty pozorne statyczne moŜemy
tworzyć na dwa róŜne sposoby, poprzez zwykłą ręczną
implementację dodatkowej klasy lub moŜna je wygenerować
automatycznie (na etapie kompilacji bądź uruchomienia kodu

Rys. 3 Przykład zastosowania
obiektu pozornego

testu). Obiekty pozorne dynamiczne równieŜ moŜna tworzyć na

dwa sposoby: za pomocą interfejsu zwanego proxy lub za pomocą bardziej zaawansowanych
technik programistycznych. Obiekty pozorne statyczne moŜemy uŜyć w przypadku pojedynczych
testów, bądź niewielkich i niezbyt skomplikowanych projektów, natomiast w przypadku duŜych
projektów (duŜa liczba klas, które musielibyśmy napisać w przypadku pozorantów statycznych),
gdzie najczęściej podmieniane obiekty mają rozbudowane funkcjonalności bardziej odpowiednie
staje się uŜycie obiektów pozornych dynamicznych [12].
W odróŜnieniu od technik opisanych powyŜej obiekty pozorne są najczęściej
automatycznie

generowanymi

obiektami

zastępczymi

z

zaprogramowanymi

wcześniej

oczekiwaniami wywołań z obiektu testowanego, które są jednocześnie specyfikacją zachowań
tego obiektu. Obiekty te mogą zapisywać wykonane wywołania z obiektu testowanego w celu
porównania tej informacji z oczekiwaniami wynikającymi ze scenariusza testu. Z wszystkich
wymienionych technik tylko obiekty zastępcze weryfikują zachowanie testowanego obiektu, a
pozostałe weryfikują jedynie stany, w których znajduje się testowany obiekt [5]. Obiekty
pozorne są najczęściej generowani dynamicznie w trakcie uruchamiania testów, a więc nie ma
potrzeby tworzenia dodatkowych klas z kodem, a zarazem utrzymywania i aktualizacji ich kodu,
co znacznie odróŜnia je od pozostałych technik. Całość logiki obiektów zastępczych, a więc zbiór
oczekiwanych zachowań jest trzymany razem z kodem testów [2].

Wszystkie przedstawione powyŜej techniki mają teŜ wspólne cechy, takie jak: moŜliwość
eliminacji wielu zaleŜności w kodzie, inaczej mówiąc moŜliwość testowania wybranych
fragmentów kodu w izolacji, czyli bez środowiska, w którym ten kod pracuje, moŜliwość
testowania scenariuszy hipotetycznych lub rzadko występujących w prawdziwym działaniu kodu,
moŜliwość znacznego przyspieszenia samego procesu testowania, pozwalając tym samym na
częste uruchamianie testów jednostkowych, a jednocześnie na realizację idei testów
jednostkowych.
PoniŜej przedstawiamy róŜne podejścia do obiektów pozornych. Na początek obiekt pozorny
napisany od początku do końca przez nas.

Strona 31 z 63
TESTER.PL

public class MyMockBookManager implements BookManager {
private TreeMap<Book, Integer> bookList;
private boolean connect;
private boolean disconnect;
private int expectedOrderCount;
private int orderCount;
private int expectedConnectCount;
private int connectCount;
private int expectedDisconnectCount;
private int disconnectCount;
public MyMockBookManager() {
bookList = new TreeMap<Book, Integer>();
connect = false;
disconnect = false;
expectedOrderCount = 0;
expectedConnectCount = 0;
expectedDisconnectCount = 0;
orderCount = 0;
connectCount = 0;
disconnectCount = 0;
}
public boolean connect() {
connectCount++;
return connect;
}
public boolean disconnect() {
disconnectCount++;
return disconnect;
}
public TreeMap<Book, Integer> bookList() {
return bookList;
}
public boolean order(BookOrder bookOrder) throws BookNotFoundException,
NotEnoughBooksException {
orderCount++;
for (Book book: bookOrder.getBookList().keySet()) {
if (!bookList.containsKey(book)) {
throw new BookNotFoundException();
}
if (bookList.get(book) < bookOrder.getBookList().get(book)) {
throw new NotEnoughBooksException();

Strona 32 z 63
TESTER.PL
}
}
return true;
}
public void setConnect(boolean connect) {
this.connect = connect;
}
public void setDisconnect(boolean disconnect) {
this.disconnect = disconnect;
}
public void setBookList(TreeMap<Book, Integer> bookList) {
this.bookList = bookList;
}
public void setExpectedConnectCount(int expectedConnectCount) {
this.expectedConnectCount = expectedConnectCount;
}
public void setExpectedDisconnectCount(int expectedDisconnectCount) {
this.expectedDisconnectCount = expectedDisconnectCount;
}
public void setExpectedOrderCount(int expectedOrderCount) {
this.expectedOrderCount = expectedOrderCount;
}
public void verify() {
if (expectedOrderCount != orderCount) {
Assert.fail("Wrong number of performed orders");
}
if (expectedConnectCount != connectCount) {
Assert.fail("Wrong number of performed connects");
}
if (expectedDisconnectCount != disconnectCount) {
Assert.fail("Wrong number of performed disconnects");
}
}
}

public class BookClientTestMyMockBookManager extends TestCase {
private TreeMap<Book, Integer> bookList;
private static final Book QUO_VADIS = new Book(1001, "Henryk Sienkiewicz",
"Quo Vadis");
private static final Book PAN_TADEUSZ = new Book(2001, "Adam Mickiewicz",
"Pan Tadeusz");
private static final Book CHLOPI = new Book(3001, "Wladyslaw Reymont",
"Chlopi");
@Override

Strona 33 z 63
TESTER.PL
protected void setUp() throws Exception {
super.setUp();
bookList = new TreeMap<Book, Integer>();
bookList.put(QUO_VADIS, 2);
bookList.put(PAN_TADEUSZ, 3);
bookList.put(CHLOPI, 5);
}
public void testOrderOk() {
BookClient client;
MyMockBookManager mock;
BookOrder bookOrder;
String message;
mock = new MyMockBookManager();
mock.setBookList(bookList);
mock.setConnect(true);
mock.setDisconnect(true);
mock.setExpectedConnectCount(1);
mock.setExpectedDisconnectCount(1);
mock.setExpectedOrderCount(1);
client = new BookClient(mock);
bookOrder = new BookOrder();
bookOrder.addBook(QUO_VADIS);
bookOrder.addBook(PAN_TADEUSZ);
client.orderBooks(bookOrder);
message = BookClient.ORDERED_BOOKS +
BookClient.bookListToString(bookOrder.getBookList());
assertEquals(message, client.getMessage());
mock.verify();
}
public void testOrderFail() {
BookClient client;
MyMockBookManager mock;
BookOrder bookOrder;
String message;
mock = new MyMockBookManager();
mock.setBookList(bookList);
mock.setConnect(true);
mock.setDisconnect(true);
mock.setExpectedConnectCount(1);
mock.setExpectedDisconnectCount(1);
mock.setExpectedOrderCount(1);
client = new BookClient(mock);
bookOrder = new BookOrder();
bookOrder.addBook(QUO_VADIS);

Strona 34 z 63
TESTER.PL
bookOrder.addBook(PAN_TADEUSZ);
bookOrder.addBook(PAN_TADEUSZ);
bookOrder.addBook(PAN_TADEUSZ);
bookOrder.addBook(PAN_TADEUSZ);
client.orderBooks(bookOrder);
message = BookClient.NOT_ENOUGH_BOOKS_ERROR;
assertEquals(message, client.getMessage());
mock.verify();
}
}

Jak łatwo zauwaŜyć klasa MyMockBookManager jest dość obszerna. Wyobraźmy sobie ile
musielibyśmy napisać takich klas w przypadku duŜego systemu. Na szczęście z pomocą
przychodzi nam MockMaker. Jest to biblioteka słuŜąca do generacji obiektów pozornych
z podanego interfejsu. Wygenerowany obiekt pozorny jest bardzo przyjazny dla uŜytkownika,
a testy z jego uŜyciem pisze się bardzo sprawnie. Co więcej moŜe on być włączony jako wtyczka
do

eclipse’a.

Więcej

szczegółów

moŜemy

znaleźć

na

stronie

domowej

projektu

http://mockmaker.sourceforge.net/ oraz w [15].
public class MockBookManager implements BookManager{
private ExpectationCounter myConnectCalls = new
ExpectationCounter("example.book.BookManager ConnectCalls");
private ReturnValues myActualConnectReturnValues = new ReturnValues(false);
private ExpectationCounter myDisconnectCalls = new
ExpectationCounter("example.book.BookManager DisconnectCalls");
private ReturnValues myActualDisconnectReturnValues = new
ReturnValues(false);
private ExpectationCounter myBookListCalls = new
ExpectationCounter("example.book.BookManager BookListCalls");
private ReturnValues myActualBookListReturnValues = new
ReturnValues(false);
private ExpectationCounter myOrderCalls = new
ExpectationCounter("example.book.BookManager OrderCalls");
private ReturnValues myActualOrderReturnValues = new ReturnValues(false);
private ExpectationList myOrderParameter0Values = new
ExpectationList("example.book.BookManager example.book.BookOrder");
public void setExpectedConnectCalls(int calls){
myConnectCalls.setExpected(calls);
}
public boolean connect(){
myConnectCalls.inc();
Object nextReturnValue = myActualConnectReturnValues.getNext();
if (nextReturnValue instanceof ExceptionalReturnValue &&
((ExceptionalReturnValue)nextReturnValue).getException() instanceof
RuntimeException)
throw
(RuntimeException)((ExceptionalReturnValue)nextReturnValue).getException();
return ((Boolean) nextReturnValue).booleanValue();

Strona 35 z 63
TESTER.PL
}
public void setupExceptionConnect(Throwable arg){
myActualConnectReturnValues.add(new ExceptionalReturnValue(arg));
}
public void setupConnect(boolean arg){
myActualConnectReturnValues.add(new Boolean(arg));
}
public void setExpectedDisconnectCalls(int calls){
myDisconnectCalls.setExpected(calls);
}
public boolean disconnect(){
myDisconnectCalls.inc();
Object nextReturnValue = myActualDisconnectReturnValues.getNext();
if (nextReturnValue instanceof ExceptionalReturnValue &&
((ExceptionalReturnValue)nextReturnValue).getException() instanceof
RuntimeException)
throw
(RuntimeException)((ExceptionalReturnValue)nextReturnValue).getException();
return ((Boolean) nextReturnValue).booleanValue();
}
public void setupExceptionDisconnect(Throwable arg){
myActualDisconnectReturnValues.add(new ExceptionalReturnValue(arg));
}
public void setupDisconnect(boolean arg){
myActualDisconnectReturnValues.add(new Boolean(arg));
}
public void setExpectedBookListCalls(int calls){
myBookListCalls.setExpected(calls);
}
public TreeMap<Book,Integer> bookList(){
myBookListCalls.inc();
Object nextReturnValue = myActualBookListReturnValues.getNext();
if (nextReturnValue instanceof ExceptionalReturnValue &&
((ExceptionalReturnValue)nextReturnValue).getException() instanceof
RuntimeException)
throw
(RuntimeException)((ExceptionalReturnValue)nextReturnValue).getException();
return (TreeMap<Book,Integer>) nextReturnValue;
}
public void setupExceptionBookList(Throwable arg){
myActualBookListReturnValues.add(new ExceptionalReturnValue(arg));
}
public void setupBookList(TreeMap<Book,Integer> arg){
myActualBookListReturnValues.add(arg);
}
public void setExpectedOrderCalls(int calls){
myOrderCalls.setExpected(calls);
}
public void addExpectedOrderValues(BookOrder arg0){

Strona 36 z 63
TESTER.PL
myOrderParameter0Values.addExpected(arg0);
}
public boolean order(BookOrder arg0) throws BookNotFoundException,
NotEnoughBooksException{
myOrderCalls.inc();
myOrderParameter0Values.addActual(arg0);
Object nextReturnValue = myActualOrderReturnValues.getNext();
if (nextReturnValue instanceof ExceptionalReturnValue &&
((ExceptionalReturnValue)nextReturnValue).getException() instanceof
BookNotFoundException)
throw
(BookNotFoundException)((ExceptionalReturnValue)nextReturnValue).getException(
);
if (nextReturnValue instanceof ExceptionalReturnValue &&
((ExceptionalReturnValue)nextReturnValue).getException() instanceof
NotEnoughBooksException)
throw
(NotEnoughBooksException)((ExceptionalReturnValue)nextReturnValue).getExceptio
n();
if (nextReturnValue instanceof ExceptionalReturnValue &&
((ExceptionalReturnValue)nextReturnValue).getException() instanceof
RuntimeException)
throw
(RuntimeException)((ExceptionalReturnValue)nextReturnValue).getException();
return ((Boolean) nextReturnValue).booleanValue();
}
public void setupExceptionOrder(Throwable arg){
myActualOrderReturnValues.add(new ExceptionalReturnValue(arg));
}
public void setupOrder(boolean arg){
myActualOrderReturnValues.add(new Boolean(arg));
}
public void verify(){
myConnectCalls.verify();
myDisconnectCalls.verify();
myBookListCalls.verify();
myOrderCalls.verify();
myOrderParameter0Values.verify();
}
}
public class BookClientTestMockBookManager extends TestCase {
private TreeMap<Book, Integer> bookList;
private static final Book QUO_VADIS = new Book(1001, "Henryk Sienkiewicz",
"Quo Vadis");
private static final Book PAN_TADEUSZ = new Book(2001, "Adam Mickiewicz",
"Pan Tadeusz");
private static final Book CHLOPI = new Book(3001, "Wladyslaw Reymont",
"Chlopi");
private BookClient client;

Strona 37 z 63
TESTER.PL
private MockBookManager mock;
@Override
protected void setUp() throws Exception {
super.setUp();
bookList = new TreeMap<Book, Integer>();
bookList.put(QUO_VADIS, 2);
bookList.put(PAN_TADEUSZ, 3);
mock = new MockBookManager();
client = new BookClient(mock);
}
public void testSecondConnectFail() {
String message;
BookOrder bookOrder;
mock.setExpectedConnectCalls(2);
mock.setExpectedDisconnectCalls(1);
mock.setExpectedBookListCalls(1);
mock.setExpectedOrderCalls(0);
mock.setupConnect(true);
mock.setupConnect(false);
mock.setupDisconnect(true);
mock.setupBookList(bookList);
client.getBookList();
message = BookClient.AVAILABLE_BOOKS +
BookClient.bookListToString(bookList);
assertEquals(message, client.getMessage());
bookOrder = new BookOrder();
client.orderBooks(bookOrder);
message = BookClient.CONNECT_ERROR;
assertEquals(message, client.getMessage());
mock.verify();
}
public void testSecondOrderFailWithBookNotFoundException() {
String message;
BookOrder first;
BookOrder second;
first = new BookOrder();
first.addBook(QUO_VADIS);
first.addBook(QUO_VADIS);
second = new BookOrder();
second.addBook(CHLOPI);
mock.setExpectedConnectCalls(2);
mock.setExpectedDisconnectCalls(2);
mock.setExpectedOrderCalls(2);

Strona 38 z 63
TESTER.PL
mock.setupConnect(true);
mock.setupConnect(true);
mock.setupDisconnect(true);
mock.setupDisconnect(true);
mock.setupBookList(bookList);
mock.addExpectedOrderValues(first);
mock.addExpectedOrderValues(second);
mock.setupOrder(true);
mock.setupExceptionOrder(new BookNotFoundException());
client.orderBooks(first);
message = BookClient.ORDERED_BOOKS +
BookClient.bookListToString(first.getBookList());
assertEquals(message, client.getMessage());
client.orderBooks(second);
message = BookClient.BOOK_NOT_FOUND_ERROR;
assertEquals(message, client.getMessage());
mock.verify();
}
}

Inne podejście do obiektów pozornych prezentują biblioteki takie jak EasyMock i jMock.
Tworzą one pewnego rodzaju proxy na podstawie przedstawionego interfejsu. Mechanizm
działania takiego testu wygląda w następujący sposób. Najpierw nagrywamy zachowanie
obiektu pozornego:
•

jakie metody powinny być uruchomione i ile razy

•

jakie wartości powinny być zwrócone lub rzucone wyjątki

Następnie uruchamiamy test i weryfikujemy zachowanie obiektu testowanego.

Na poniŜszych wydrukach kodu, te przedstawione metody są najbardziej ekonomiczne i
przyjazne w uŜyciu przez uŜytkownika.

public class BookClientTestEasyMock extends TestCase {
private TreeMap<Book, Integer> bookList;
private static final Book QUO_VADIS = new Book(1001, "Henryk Sienkiewicz",
"Quo Vadis");
private static final Book PAN_TADEUSZ = new Book(2001, "Adam Mickiewicz",
"Pan Tadeusz");
private static final Book CHLOPI = new Book(3001, "Wladyslaw Reymont",
"Chlopi");

Strona 39 z 63
TESTER.PL

private BookClient client;
private BookManager mock;
@Override
protected void setUp() throws Exception {
super.setUp();
bookList = new TreeMap<Book, Integer>();
bookList.put(QUO_VADIS, 2);
bookList.put(PAN_TADEUSZ, 3);
bookList.put(CHLOPI, 5);
mock = EasyMock.createMock(BookManager.class);
client = new BookClient(mock);
}
public void testBookList() {
String message;
EasyMock.expect(mock.connect()).andReturn(true);
EasyMock.expect(mock.bookList()).andReturn(bookList);
EasyMock.expect(mock.disconnect()).andReturn(true);
EasyMock.replay(mock);
client.getBookList();
message = BookClient.AVAILABLE_BOOKS +
BookClient.bookListToString(bookList);
assertEquals(message, client.getMessage());
EasyMock.verify(mock);
}
public void testNotEnoughBooks() {
String message;
BookOrder bookOrder;
bookOrder = new BookOrder();
bookOrder.addBook(QUO_VADIS);
EasyMock.expect(mock.connect()).andReturn(true);
EasyMock.expectLastCall().times(3);
try {
EasyMock.expect(mock.order(EasyMock.eq(bookOrder))).
andReturn(true).
andReturn(true).
andThrow(new NotEnoughBooksException());
} catch (BookNotFoundException e) {
} catch (NotEnoughBooksException e) {
}
EasyMock.expect(mock.disconnect()).andReturn(true).times(3);
EasyMock.replay(mock);
client.orderBooks(bookOrder);

Strona 40 z 63
TESTER.PL
message = BookClient.ORDERED_BOOKS +
BookClient.bookListToString(bookOrder.getBookList());
assertEquals(message, client.getMessage());
client.orderBooks(bookOrder);
message = BookClient.ORDERED_BOOKS +
BookClient.bookListToString(bookOrder.getBookList());
assertEquals(message, client.getMessage());
client.orderBooks(bookOrder);
message = BookClient.NOT_ENOUGH_BOOKS_ERROR;
assertEquals(message, client.getMessage());
EasyMock.verify(mock);
}
}

public class BookClientTestJMock extends TestCase {
private TreeMap<Book, Integer> bookList;
private static final Book QUO_VADIS = new Book(1001, "Henryk Sienkiewicz",
"Quo Vadis");
private static final Book PAN_TADEUSZ = new Book(2001, "Adam Mickiewicz",
"Pan Tadeusz");
private static final Book CHLOPI = new Book(3001, "Wladyslaw Reymont",
"Chlopi");
private BookClient client;
private BookManager mock;
private Mockery context;
@Override
protected void setUp() throws Exception {
super.setUp();
bookList = new TreeMap<Book, Integer>();
bookList.put(QUO_VADIS, 2);
bookList.put(PAN_TADEUSZ, 3);
context = new Mockery();
mock = context.mock(BookManager.class);
client = new BookClient(mock);
}
public void testDisconnectFail() {
String message;
context.checking(new Expectations() {{
one (mock).connect(); will(returnValue(true));
one (mock).connect(); will(returnValue(true));

Strona 41 z 63
TESTER.PL
one (mock).bookList(); will(returnValue(bookList));
one (mock).bookList(); will(returnValue(bookList));
one (mock).disconnect(); will(returnValue(true));
one (mock).disconnect(); will(returnValue(false));
}});
client.getBookList();
message = BookClient.AVAILABLE_BOOKS +
BookClient.bookListToString(bookList);
assertEquals(message, client.getMessage());
client.getBookList();
message = BookClient.DISCONNECT_ERROR;
assertEquals(message, client.getMessage());
context.assertIsSatisfied();
}
public void testForException() {
String message;
final BookOrder bookOrder;
bookOrder = new BookOrder();
bookOrder.addBook(CHLOPI);
try {
context.checking(new Expectations() {{
one (mock).connect(); will(returnValue(true));
one (mock).order(bookOrder); will(throwException(new
BookNotFoundException()));
one (mock).disconnect(); will(returnValue(true));
}});
} catch (BookNotFoundException e) {
} catch (NotEnoughBooksException e) {
}
client.orderBooks(bookOrder);
message = BookClient.BOOK_NOT_FOUND_ERROR;
assertEquals(message, client.getMessage());
context.assertIsSatisfied();
}
}

Główną zaletą uŜycia techniki obiektów zastępczych w porównaniu z innymi technikami
testowania jest moŜliwość weryfikacji zachowań (zarówno prawidłowości wywołań bądź ich
braku, liczby wywołań jak i wartości zwracanych) obiektu testowanego względem środowiska, w
którym pracuje, a nie jedynie weryfikacji zmiany samych stanów testowanego obiektu [4].
Dzięki jej zastosowaniu staramy się lepiej zrozumieć wymagania, które mają być realizowane
przez nasz kod, a tym samym mamy większą wiedzę na temat tego, co powinniśmy zrobić w
najbliŜszym etapie pracy.

Strona 42 z 63
TESTER.PL
Inne drugoplanowe zalety obiektów pozornych, pozwalające lepiej realizować koncepcję
testów jednostkowych to moŜliwość:
•

testowania jeszcze mniejszych części kodu niŜ w przypadku pozostałych technik,

•

tworzenia testów niezaleŜnych od siebie nawzajem,

•

późniejszego podjęcia decyzji o pewnych fragmentach infrastruktury oraz zmniejszenie
ilości kodu testowego, kosztem nieco większej jego złoŜoności.

Zbyt ścisłe zrównoleglenie z kodem testowanym i potrzeba synchronizacji pomiędzy nimi jest
czasem uznawane za jedna z wad, choć w rzeczywistości zapewnia egzekwowanie
przestrzegania ścisłej zgodności z procesem tworzenia testów jednostkowych. Wadą moŜe być
za to bardzo wąski obszar danego scenariusza testowego z uŜyciem mocków, co w przypadku
nawet drobnych zmian w testowanym kodzie moŜe prowadzić do tego, Ŝe test szybciej stanie się
przestarzały i będzie wymagał od nas poprawek [3].
Obiekty zastępcze pozwalają w znacznym stopniu zautomatyzować tworzenie testów
jednostkowych (zwłaszcza, Ŝe istnieje wiele róŜnych narzędzi do ich tworzenia w wielu językach
programowania), a tym samym zwiększyć wydajność oraz przyspieszyć proces tworzenia testów,
powodując, Ŝe powstające oprogramowanie będzie znacznie lepszej jakości. W szczególności
technika ta pozwoli nam na większe zaufanie, co do jakości indywidualnych części naszego
oprogramowania. Pewnymi niedogodnościami mogą być narzędzia do tworzenia obiektów
pozornych (szczególnie dla rzadziej uŜywanych języków programowania), poniewaŜ są stale
rozwijane ze względu to, Ŝe sama idea powstała w miarę niedawno [2]. Oczywiste jest, Ŝe nie
ma jedynego słusznego sposobu na wykonanie testów, a tym samym nie zawsze istnieje
moŜliwość skorzystania z tej wybranej techniki, ale tych wyjątków w przypadku obiektów
zastępczych jest naprawdę niewiele.
Podsumowując obiekty pozorne są i będą waŜną gałęzią testów. Powinny one w
niedługim czasie znacznie się rozpowszechnić, co w połączeniu z technikami zwinnymi
(agile’owymi) będzie potęŜnym narzędziem w tworzeniu oprogramowania.

Strona 43 z 63
TESTER.PL

Bibliografia:
[1] 2006. Techniques for Successful Evolutionary/Agile Database Development, artykuł internetowy,
http://www.agiledata.org/essays/tdd.html
[2] Bain S. 2006. Mocks, Fakes, and Stubs. Net Objectives, Vol. 3, No. 4, pp. 2-14.
[3] Brown M., Tapolcsanyi E. 2003. Mock Object Patterns. Proceedings of The 10th Conference on Pattern
Languages of Programs, Sept 8-12, USA.
[4] Burke E. M., Coyner B. M. 2003. Java Extreme Programming Cookbook. O'Reilly and Associates.
[5] Fowler M. 2007. Mocks Aren't Stubs. Artykuł ze strony internetowej autora,
http://martinfowler.com/articles/mocksArentStubs.html.
[6] Freeman S., Mackinnon T., Pryce N., Walnes J. 2004. Mock Roles Not Objects.
Proceedings of 19th Ann. ACM SIGPLAN Conf. Object-Oriented Programming, Systems,
Languages, and Applications (OOPSLA '04), ACM Press, pp. 236–246.
[7] Freeman S., Pryce N. 2006. Evolving an embedded domain-specific language in Java. Proceedings of the 21th
Annual ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications,
OOPSLA 2006, October 22-26, 2006, Portland, Oregon, USA, pp. 855-865.
[8] 1999. IEEE Standard for Software Unit Testing: An American National Standard, ANSI/IEEE Std 1008-1987 in
IEEE Standards: Software Engineering, Volume Two: Process Standards; The Institute of Electrical and Electronics
Engineers, Inc. Software Engineering Technical Committee of the IEEE Computer Society.
[9] Mackinnon T., Freeman S., Craig P. 2001. Endo-testing: Unit testing with mock objects. Proceedings of XP2000.
Extreme Programming Examined, Addison-Wesley, Boston, MA, pp. 287-301.
[10] Massol V., Husted T. 2003. JUnit in Action. Manning Publications.
[11] Ryu H.-Y., Sohn B.-K., Park J.-H. 2005. Mock objects framework for TDD in the network environment.
Proceedings of the Fourth Annual ACIS International Conference on Computer and Information Science (ICIS’05),
pp. 430- 434.
[12] Stewart S. 2004. Approaches to Mocking. An article from O'Reilly Network Site,
http://www.onjava.com/pub/a/onjava/2004/02/11/mocks.html.
[13] Thomas D., Hunt A. 2002. Mock Objects. IEEE Software, Vol. 19, No. 3, pp. 22-24.
[14] Wirfs-Brock R.J. 2007. Driven to … Discovering Your Design Values. IEEE Software, Vol. 24, No. 1, pp. 9-11.
[15] Keld H. Hansen Using Mock Objects in Java. Artykuł ze strony internetowej,
http://javaboutique.internet.com/tutorials/mock_objects/
[16] EasyMock 2.2 Readme, http://www.easymock.org/EasyMock2_2_Documentation.html
[17] The jMock Cookbook, http://www.jmock.org/cookbook.html

Strona 44 z 63
TESTER.PL

Dokumentacja – jak to się je?
Maciej Dusza

Maciej Dusza jest absolwentem informatyki na wydziale Matematyki,
Informatyki i Mechaniki Uniwersytetu Warszawskiego. W 1993r.
obronił

pracę

magisterską

na

temat

"Tworzenie

programów

współbieŜnych o sterowaniu zadanym strukturami przyczynowoskutkowymi". Od tego czasu pracował w kilku firmach w Polsce i USA,
pełniąc funkcje programisty, analityka, projektanta i testera, a przez
ostatnie trzy lata pracował w IMPAQ Sp. z o.o., na stanowisku
Starszego Analityka ds. Zapewnienia Jakości.
Kawaler. Jego hobby to nurkowanie.

Strona 45 z 63
TESTER.PL

Dokumentacja – jak to się je?
Maciej Dusza

Pisanie dokumentacji często traktowane jest przez programistów jak cięŜka kara i dopust
boŜy. Z nieco większym zrozumieniem odnoszą się do tego kierownicy projektów, zwłaszcza ci,
którzy

kiedyś

w

połowie

projektu

stracili

pracownika

będącego

„guru

od

systemu/programu/modułu”, i musieli w trybie nagłym wdraŜać do pracy nową osobę,
przegryzając się przez tysiące linii nieudokumentowanego, i często pozbawionego komentarzy
kodu.
Ja

osobiście

lubię

pisać

dokumentację.

Sprawia

mi

przyjemność

opisywanie

poszczególnych elementów systemu w sposób dokładny, precyzyjny, i zrozumiały dla odbiorcy.
PoniŜej przedstawiam zbiór porad i uwag, które – mam nadzieję – przydadzą się zarówno
osobom piszącym „z potrzeby serca”, jak i tym, dla których jest to przykrym obowiązkiem.
Moje

uwagi

przedstawiam

w

postaci

krótkich

akapitów,

odnoszących

się

do

poszczególnych zagadnień związanych z pisaniem dokumentacji. Opracowałem je na podstawie
własnych doświadczeń, oraz lektury kilku ksiąŜek, których tytuły wymieniam na końcu.
Cechy dobrej dokumentacji:
•

jest dokładna i bezbłędna

•

jest kompletna

•

jest spójna

•

jest napisana w sposób klarowny i jednoznaczny

•

uŜytkownik moŜe łatwo znaleźć informację, której potrzebuje

•

ma porządny, ładny wygląd.

Etapy tworzenia dokumentacji:
•

zbieranie informacji na temat opisywanego systemu

•

tworzenie projektu dokumentacji

•

pisanie dokumentacji

•

weryfikacja dokumentacji

•

dokonywanie poprawek i uzupełnianie braków.

Dwa ostatnie etapy są zwykle powtarzane kilkakrotnie, aŜ do usunięcia wszystkich usterek.
Informacje na temat opisywanego systemu, moŜna zbierać poprzez:
•

czytanie juŜ istniejących fragmentów dokumentacji

•

pracę z programem i analizę kodu źródłowego

Strona 46 z 63
TESTER.PL
•

konsultacje (zazwyczaj z developerami, którzy stworzyli system).

Projekt dokumentacji powinien zawierać następujące elementy:
•

tytuł dokumentacji

•

rodzaj dokumentacji (np. podręcznik uŜytkownika)

•

przewidywana data zakończenia prac nad dokumentacją

•

imię i nazwisko dokumentalisty

•

krótki opis tego, co dokumentacja będzie zawierać

•

szacunkowa liczba stron (najlepiej określić to poprzez porównanie z podobnymi,
istniejącymi juŜ dokumentami)

•

spis głównych rozdziałów

•

terminy kamieni milowych.

Kamień milowy przy pisaniu dokumentacji, to moment kiedy dokumentacja idzie do weryfikacji.
Dobrym miejscem na ustanowienie pierwszego kamienia milowego, jest moment gdy:
•

większość (dwie trzecie lub trzy czwarte) tekstu jest juŜ napisane

•

zaznaczone są miejsca, w których wstawione zostaną rysunki i zrzuty ekranów.

Dobrym miejscem na ustanowienie drugiego kamienia milowego, jest moment gdy:
•

praktycznie cały tekst jest juŜ napisany

•

prawie wszystkie rysunki i zrzuty ekranów są juŜ w dokumentacji

•

poprawione zostały usterki, które wyszły na jaw w czasie weryfikacji po pierwszym
kamieniu milowym.

Przy tworzeniu dokumentacji, zwykle występują co najmniej dwa kamienie milowe, i często nie
ma ich więcej. Dlatego w projekcie dokumentacji podaje się najczęściej terminy dwóch kamieni.
Weryfikacja dokumentacji oznacza:
•

dokładne przeczytanie dokumentacji w poszukiwaniu błędów

•

sprawdzenie czy zrzuty ekranu odpowiadają temu, co rzeczywiście w opisywanym
momencie widać na ekranie

•

wykonanie procedur (instrukcji) zawartych w dokumentacji, i sprawdzenie czy
rzeczywiście generują Ŝądane rezultaty

•

przekazanie dokumentacji recenzentom i zebranie ich uwag.

PoŜyteczną metodą weryfikacji, jest równieŜ poproszenie kogoś, kto nie posiada zbyt
głębokiej wiedzy na temat opisywanego systemu, o przeczytanie dokumentacji, i upewnienie
się, Ŝe wszystko zrozumiał. Powód: dokumentalista moŜe zapomnieć o napisaniu czegoś, co jest
dla niego "oczywiste", a recenzent, zwykle dobrze znający system, moŜe nie zauwaŜyć takiego
braku.

Strona 47 z 63
TESTER.PL
Dwa waŜne pytania, jakie naleŜy sobie zadać przed rozpoczęciem pracy nad dokumentacją:
•

dla kogo ta dokumentacja jest przeznaczona? Inaczej pisze się np. dla sprzedawcy, który
będzie jedynie korzystał z systemu, a inaczej dla programisty, który będzie ten system
rozwijał. Nie chodzi tu tylko o treść, chodzi równieŜ o styl pisania (np. uŜywanie
słownictwa technicznego).

•

jakie przyjąć podejście: opisowe czy zadaniowe? Opisowe podejście do pisania
dokumentacji polega na tym, Ŝe po prostu opisujemy kolejne elementy systemu,
troszcząc się jedynie o to, Ŝeby zrobić to dobrze. Podejście zadaniowe polega na tym, Ŝe
przy pisaniu bierzemy pod uwagę zadania, które wykonuje odbiorca dokumentacji.

Przykładowo: załóŜmy, Ŝe opisujemy interfejs uŜytkownika, a odbiorcą dokumentacji ma być
osoba zajmująca się wystawianiem faktur. Jeśli przyjmiemy podejście zadaniowe, to opcje
systemu opiszemy nie w takiej kolejności, w jakiej są ułoŜone w menu, tylko w takiej, w jakiej
korzysta się z nich przy wystawianiu faktur.
MoŜe się zdarzyć, Ŝe z dokumentacji mają korzystać dwie róŜne grupy uŜytkowników. Na
przykład sprzedajemy system wspomagający sprzedaŜ firmie, która posiada dział sprzedaŜy i
dział informatyki. Sprzedawcy będą uŜywać tego systemu korzystając z interfejsu uŜytkownika,
a informatycy otrzymają kod źródłowy, i będą go na własną rękę rozwijać. W takiej sytuacji,
najlepszym rozwiązaniem jest napisanie dwóch oddzielnych dokumentacji: jednej dla
sprzedawców, i jednej dla informatyków. Jest to oczywiście bardziej praco- i czasochłonne, ale
znacznie wygodniejsze z punktu widzenia odbiorców dokumentacji. Jeśli jednak nie chcemy
pisać dwóch oddzielnych dokumentacji (bo np. nie ma na to czasu), i decydujemy się włoŜyć
wszystkie informacje do jednej dokumentacji, wówczas naleŜy wyraźnie zaznaczyć, które sekcje
dokumentacji są przeznaczone dla której grupy odbiorców.
Pisanie dokumentacji jednym ciągiem, od początku do końca, nie jest dobrym pomysłem. Po
pierwsze, nieraz będą się zdarzać sytuacje, kiedy napisanie kolejnego fragmentu nie jest
moŜliwe (bo np. developer, z którym trzeba się skonsultować, nie jest chwilowo dostępny). Po
drugie, po zakończeniu moŜe się okazać, Ŝe dokumentacja wprawdzie zawiera wszystko co
trzeba, ale jej układ powinien być inny. Dlatego lepsze wyniki przynosi następujące podejście:
•

określamy jakie rozdziały będzie mieć dokumentacja

•

dla kaŜdego rozdziału określamy jakie będzie mieć podrozdziały

•

dla kaŜdego podrozdziału określamy jakie będzie mieć pod-podrozdziały

•

i tak dalej...

•

kiedy szczegółowa struktura rozdziałów jest juŜ gotowa, zaczynamy wypełniać treścią
kolejne pod-...-rozdziały. Jeśli w jakimś momencie napotkamy problem, z którym nie

Strona 48 z 63
TESTER.PL
moŜemy sobie od razu poradzić, wtedy zaznaczamy Ŝe to miejsce wymaga uzupełnienia,
i po prostu zabieramy się za wypełnianie treścią kolejnego elementu struktury.
Dodatkową zaletą takiego podejścia jest to, Ŝe na pierwszy rzut oka widoczny jest stan prac
nad dokumentacją - co konkretnie jest juŜ gotowe, co wymaga uzupełnienia, a co nie jest
jeszcze rozpoczęte.

Na początku dokumentacji powinny się znaleźć:
•

copyright (najlepiej zwrócić się do działu prawnego firmy o opracowanie dokładnego
sformułowania)

•

spis treści

•

opcjonalnie: spis ilustracji (dla uŜytkownika moŜe być wygodne, jeśli będzie mógł znaleźć
konkretny diagram, bez zastanawiania się w jakim powinien być rozdziale)

•

wstęp.

Wstęp powinien informować:
•

jaki produkt opisuje dokumentacja

•

dla kogo dokumentacja jest przeznaczona

•

co dokumentacja zawiera (bardzo ogólnie)

•

jakie konwencje są stosowane w dokumentacji (np. jaka czcionka jest stosowana dla
zamieszczonych w dokumentacji fragmentów kodu źródłowego).

Na końcu dokumentacji powinny się znaleźć:
•

załączniki

•

słowniczek terminów specyficznych dla opisywanego systemu, oraz terminów i skrótów
które są uŜywane w dokumentacji, a których uŜytkownik moŜe nie znać

•

indeks.

Załączniki powinny zawierać informacje, które uŜytkownik moŜe chcieć uzyskać, ale które nie
są niezbędne do zrozumienia dokumentacji (np. definicje pól w bazie danych). Uwaga: nie
naleŜy traktować załączników jako miejsce, do którego dokumentalista moŜe wrzucić wszystkie
informacje, co do których nie ma pewności gdzie je umieścić.
Indeks jest bardzo waŜnym elementem dokumentacji. To w duŜym stopniu od niego zaleŜy,
na ile dokumentacja będzie uŜyteczna dla uŜytkownika.
Kiedy prace nad dokumentacją dojdą do kamienia milowego, przy którym wskazane jest
przeprowadzenie weryfikacji tego, co zostało dotychczas stworzone, dokumentalista powinien
najpierw sam sprawdzić swoją pracę, a następnie poprosić kilka osób o recenzję. Naturalnymi
kandydatami na recenzentów są developerzy pracujący nad opisywanym systemem, i szef

Strona 49 z 63
TESTER.PL
dokumentalisty. Warto zwrócić się o to równieŜ do ludzi, którzy mają codzienny kontakt z
klientem - odbiorcą dokumentacji (czyli do pracowników działu sprzedaŜy, działu marketingu, i
działu pomocy technicznej).
UwaŜne

przeczytanie

dokumentacji

wymaga

czasu.

Dlatego

recenzentów

naleŜy

z wyprzedzeniem zawiadomić o tym, Ŝe danego dnia przyśle im się dokumentację do
zrecenzowania.

Istnieją trzy metody zbierania opinii od recenzentów.
Pierwsza, najbardziej efektywna, polega na tym, Ŝe dokumentalista i wszyscy
recenzenci spotykają się razem, siadają przy stole, przechodzą po kolei przez wszystkie rozdziały
i akapity dokumentacji, kaŜdy moŜe zgłaszać na głos swoje uwagi, a pozostali od razu
ustosunkowują się do tych uwag. Dzięki temu, kaŜda sugestia, jaką ktoś moŜe mieć, jest
rozpatrywana przez cały zespół recenzentów.
Uwaga: na takim spotkaniu nieraz zdarza się, Ŝe rozmowa zaczyna odbiegać od tematu
(np. developer zaczyna mówić o pracach nad modułem, który nie wchodzi w zakres systemu
objęty dokumentacją). Im więcej ludzi bierze udział w spotkaniu, tym częściej zdarzają się takie
mimowolne odejścia od tematu. Dlatego, jeśli recenzentów jest więcej niŜ kilku, warto poprosić
kogoś, kto sam nie bierze udziału w recenzowaniu, o pełnienie funkcji moderatora. Moderator
jest obecny na zebraniu, ale nie zabiera głosu w dyskusji. Jedyne, czym się zajmuje, to
słuchanie tego, co mówią inni, i przerywanie ludziom, którzy zaczynają odchodzić od tematu.
Druga metoda zbierania opinii, polega na rozesłaniu dokumentacji do wszystkich
recenzentów, odebraniu opinii od kaŜdego oddzielnie, i skonsolidowaniu otrzymanych uwag.
Szczególną uwagę dokumentalista musi tu zwrócić na sprzeczne sugestie, które mogą nadejść
od róŜnych recenzentów. W takim przypadku naleŜy zawiadomić autorów tych sugestii o
zaistniałej sytuacji, i poprosić o przemyślenie stanowiska drugiej osoby. Jeśli Ŝaden z
recenzentów nie chce ustąpić, a dokumentalista nie ma własnego zdania w tej kwestii, właściwe
wydaje się uwzględnienie propozycji recenzenta, zajmującego wyŜsze stanowisko w hierarchii
firmy. Jeśli jednak dokumentalista skłania się ku rozwiązaniu proponowanemu przez niŜej
stojącego recenzenta, powinien skonsultować się ze swoim szefem.
Trzecia metoda zbierania opinii, polega na wysłaniu dokumentacji do jednego
z recenzentów, następnie, razem z naniesionymi przez niego uwagami, do drugiego, potem z
uwagami naniesionymi przez tych dwóch do trzeciego, i tak dalej. Dzięki temu kaŜdy (prócz
pierwszego) recenzent, moŜe odnieść się nie tylko do samej dokumentacji, ale równieŜ do uwag
poprzednich recenzentów. Metoda ta zabiera oczywiście więcej czasu, niŜ dwie pozostałe
metody.

Strona 50 z 63
TESTER.PL
Jeśli przekazuje się do recenzji dokument, który był juŜ kiedyś recenzowany, naleŜy
zaznaczyć co zostało dodane lub zmienione od czasu poprzedniej recenzji, Ŝeby recenzenci nie
musieli ponownie czytać wszystkiego od A do Z.

Kiedy dokumentacja zostaje oddana do produkcji, następuje jej "zamroŜenie". Oznacza
to, Ŝe Ŝadne zmiany nie będą juŜ wprowadzane. Jeśli przed przekazaniem dokumentacji
odbiorcy, okaŜe się Ŝe coś wymaga zmiany, do dokumentacji zostanie dołączona errata (np. plik
README na CD z dokumentacją), a potrzebne zmiany zostaną wprowadzone dopiero
w następnej wersji dokumentacji.
WaŜną rzeczą jest bardzo dokładne zebranie uwag od odbiorców dokumentacji, po
dostarczeniu im pierwszej gotowej części. Na podstawie takich uwag moŜna nie tylko stworzyć
wskazówki przydatne w dalszych pracach nad dokumentacją, ale moŜe to równieŜ prowadzić do
zmiany przyjętych standardów (najprostszy przykład: odbiorca prosi o pisanie dokumentacji przy
uŜyciu większej czcionki, bo ma słaby wzrok). Opinie odbiorców naleŜy zbierać po przekazaniu
im kaŜdej części dokumentacji, jednak szczególnie waŜne jest to na początku, z powodów
wymienionych wyŜej.
Przed rozpoczęciem pisania dokumentacji, naleŜy ustalić symbol / ciąg znaków, który
będzie stosowany w miejscach wymagających uzupełnienia. Takim symbolem moŜe być np. ciąg
liter TBD (od ang. to be determined). Jeśli osoba pisząca dokumentację, nie będzie pewna co
napisać w jakimś miejscu (bo np. wymaga to konsultacji z developerem), to moŜe kontynuować
pisanie pod warunkiem wstawienia w to miejsce takiego symbolu. Obowiązuje zakaz oznaczania
miejsc wymagających uzupełnienia w jakikolwiek inny sposób (np. pisząc: do uzupełnienia).
Powód jest oczywisty: chodzi o to, Ŝeby w kaŜdej chwili moŜna było jednym find'em znaleźć
wszystkie niedokończone miejsca.
Jeśli wskazane jest umieszczenie w dokumentacji zrzutu jakiegoś ekranu, to ekran ten
powinien zawierać konkretne dane, a nie tylko puste pola. Przykładowo, jeśli na ekranie jest
okno słuŜące do wprowadzania informacji personalnych, to naleŜy do odpowiednich pól
wprowadzić zmyślone imię, nazwisko, adres, telefon itd. Uwaga: niedopuszczalne jest
wprowadzanie danych Ŝyjących osób (np. przepisywanie z ksiąŜki telefonicznej), moŜe to grozić
procesem sądowym.
Kiedy pisze się dokumentację, precyzja i klarowność sformułowań są waŜniejsze od
elegancji. Jeśli np. w jednym zdaniu pojawi się dziesięć razy słowo system, to nie ma w tym nic
niewłaściwego. Nie naleŜy pisać na zmianę system i np. program, bo moŜe to jedynie wprawić w
zakłopotanie odbiorcę dokumentacji, który zacznie się zastanawiać czy na pewno chodzi o to
samo. Na tej samej zasadzie, naleŜy trzymać się raz przyjętych terminów na określenie pewnych

Strona 51 z 63
TESTER.PL
rzeczy czy czynności. Jeśli np. w pierwszym rozdziale piszemy o klikaniu myszą, to juŜ do końca
powinniśmy konsekwentnie uŜywać tego określenia, a nie zastępować go w innym rozdziale
słowem tupanie myszą.
Czasem moŜe się zdarzyć, Ŝe trzeba wybierać między ścisłym trzymaniem się zasad
pisowni, a klarownością przekazu. W takim przypadku lepiej złamać zasady, Ŝeby w zamian za
to uzyskać jednoznaczny i zrozumiały przekaz. Przykładowo, weźmy następujące zdanie:
„systemy: A, B, C i D”. Czytelnik moŜe mieć wątpliwości, czy chodzi tu o cztery systemy, czy teŜ
o trzy, z których ostatni ma złoŜoną nazwę „C i D”. Jeśli dodamy przecinek po „C”, usuniemy
niejednoznaczność.
NaleŜy pamiętać, Ŝe dokumentacja jest przeznaczona dla człowieka, a nie dla komputera,
i dlatego mogą się zdarzać sytuacje, w których uzasadnione jest odstąpienie od przyjętych
standardów. Przykładowo, jeśli chcemy umieścić w dokumentacji diagram przejścia stanów, ale
widzimy Ŝe taki diagram byłby bardzo skomplikowany, czytelnik niewiele by z niego rozumiał, i
tylko co chwila by błądził nawigując po plątaninie strzałek, to lepiej w ogóle zrezygnować z
diagramu, a poszczególne stany i warunki przejścia między nimi, opisać słownie.
Jeśli dokumentalista przejmuje dokumentację napisaną przez kogoś innego, to zanim zacznie
dopisywać nowe rozdziały, powinien:
•

przeczytać to, co juŜ jest napisane

•

jeśli jest to moŜliwe, porozmawiać z poprzednim autorem dokumentacji, i dowiedzieć się
czego nie zdąŜył napisać, co zamierzał zmienić, itp.

•

usiąść nad dokumentacją razem z developerami, upewnić się Ŝe w dokumentacji nie ma
błędów, oraz dowiedzieć się co nowego pojawiło się w systemie

•

samemu obejrzeć opisywany system, i stwierdzić na ile pasuje do dokumentacji

•

dowiedzieć się, czy odbiorcy dokumentacji zgłaszali jakieś uwagi lub prosili o zmiany w
dokumentacji.

Kiedy dokumentalista po raz pierwszy uruchamia system, który ma opisywać, i próbuje
działać jako uŜytkownik, powinien zapisywać wszystkie rzeczy, które sprawiły mu trudność. Jest
bardzo prawdopodobne, Ŝe rzeczywisty uŜytkownik natknie się na takie same trudności, a co za
tym idzie jest to okazja do zlokalizowania miejsc, które warto opisać w dokumentacji szczególnie
dokładnie.
Kiedy dokumentalista rozpoczyna opisywanie systemu, i otrzymuje dostęp do kodu
źródłowego, powinien upewnić się, Ŝe jest to najnowsza wersja kodu rozwojowego, nad którym
prowadzone są prace. Nie moŜe to być stara wersja kodu, a w szczególności nie moŜe to być
kod okrojonej wersji demonstracyjnej. Musi to być aktualny kod, nawet jeśli codziennie miałby
się zmieniać.

Strona 52 z 63
Tester.pl - Numer 10
Tester.pl - Numer 10
Tester.pl - Numer 10
Tester.pl - Numer 10
Tester.pl - Numer 10
Tester.pl - Numer 10
Tester.pl - Numer 10
Tester.pl - Numer 10
Tester.pl - Numer 10
Tester.pl - Numer 10
Tester.pl - Numer 10

Contenu connexe

Similaire à Tester.pl - Numer 10

Elektroniczny Obieg Dokumentów [smartBusiness]
Elektroniczny Obieg Dokumentów [smartBusiness]Elektroniczny Obieg Dokumentów [smartBusiness]
Elektroniczny Obieg Dokumentów [smartBusiness]smartBusiness
 
8 kroków do optymalnej inwestycji IT
8 kroków do optymalnej inwestycji IT8 kroków do optymalnej inwestycji IT
8 kroków do optymalnej inwestycji ITIdeo Sp. z o.o.
 
8 kroków do optymalnej inwestycji
8 kroków do optymalnej inwestycji8 kroków do optymalnej inwestycji
8 kroków do optymalnej inwestycjiIdeo Sp. z o. o.
 
Dziewięć głównych wyzwań w przeprowadzeniu transformacji cyfrowej w instytucj...
Dziewięć głównych wyzwań w przeprowadzeniu transformacji cyfrowej w instytucj...Dziewięć głównych wyzwań w przeprowadzeniu transformacji cyfrowej w instytucj...
Dziewięć głównych wyzwań w przeprowadzeniu transformacji cyfrowej w instytucj...Deloitte Polska
 
Trendy w rozwoju pracowników na 2021 rok
Trendy w rozwoju pracowników na 2021 rokTrendy w rozwoju pracowników na 2021 rok
Trendy w rozwoju pracowników na 2021 rokHouse of Skills
 
Webinarium 3 istotne wyzwania pracownicze 15-11-2021 | PwC
Webinarium 3 istotne wyzwania pracownicze 15-11-2021 | PwCWebinarium 3 istotne wyzwania pracownicze 15-11-2021 | PwC
Webinarium 3 istotne wyzwania pracownicze 15-11-2021 | PwCPwC Polska
 
(R)ewolucja w firmie
(R)ewolucja w firmie(R)ewolucja w firmie
(R)ewolucja w firmieAnna Kutyła
 
Trwała luka wdrożeniowa w polskich organizacjach. Wyniki badania Blue Fox.pdf
Trwała luka wdrożeniowa w polskich organizacjach. Wyniki badania Blue Fox.pdfTrwała luka wdrożeniowa w polskich organizacjach. Wyniki badania Blue Fox.pdf
Trwała luka wdrożeniowa w polskich organizacjach. Wyniki badania Blue Fox.pdfagatadrynko
 
Sukcesja czy rozwój biznesu?
Sukcesja czy rozwój biznesu?Sukcesja czy rozwój biznesu?
Sukcesja czy rozwój biznesu?Jacek Bajson
 
Raport_trwała_luka_wdrożeniowa_BlueFox_wersja2
Raport_trwała_luka_wdrożeniowa_BlueFox_wersja2Raport_trwała_luka_wdrożeniowa_BlueFox_wersja2
Raport_trwała_luka_wdrożeniowa_BlueFox_wersja2agatadrynko
 
Diaphane software 20.06.17
Diaphane software 20.06.17Diaphane software 20.06.17
Diaphane software 20.06.17Diaphane
 
Bpm eois pm_14012015_pdf
Bpm eois pm_14012015_pdfBpm eois pm_14012015_pdf
Bpm eois pm_14012015_pdfPiotr Merkel
 
Wnioski z Raportu OBZZ_2016
Wnioski z Raportu OBZZ_2016Wnioski z Raportu OBZZ_2016
Wnioski z Raportu OBZZ_2016Jarosław Rubin
 
Perspektywy Rozwoju NarzęDzi Informatycznych ZarząDzania
Perspektywy Rozwoju NarzęDzi Informatycznych ZarząDzaniaPerspektywy Rozwoju NarzęDzi Informatycznych ZarząDzania
Perspektywy Rozwoju NarzęDzi Informatycznych ZarząDzaniaguestcbb224
 
Magazyn akceleratora KPT ScaleUP
Magazyn akceleratora KPT ScaleUPMagazyn akceleratora KPT ScaleUP
Magazyn akceleratora KPT ScaleUPWielka Radość
 

Similaire à Tester.pl - Numer 10 (20)

Elektroniczny Obieg Dokumentów [smartBusiness]
Elektroniczny Obieg Dokumentów [smartBusiness]Elektroniczny Obieg Dokumentów [smartBusiness]
Elektroniczny Obieg Dokumentów [smartBusiness]
 
8 kroków do optymalnej inwestycji IT
8 kroków do optymalnej inwestycji IT8 kroków do optymalnej inwestycji IT
8 kroków do optymalnej inwestycji IT
 
8 kroków do optymalnej inwestycji
8 kroków do optymalnej inwestycji8 kroków do optymalnej inwestycji
8 kroków do optymalnej inwestycji
 
Optymalizacja procesów biznesowych
Optymalizacja procesów biznesowychOptymalizacja procesów biznesowych
Optymalizacja procesów biznesowych
 
Droga Rozwoju Digitalnego
Droga Rozwoju Digitalnego Droga Rozwoju Digitalnego
Droga Rozwoju Digitalnego
 
Dziewięć głównych wyzwań w przeprowadzeniu transformacji cyfrowej w instytucj...
Dziewięć głównych wyzwań w przeprowadzeniu transformacji cyfrowej w instytucj...Dziewięć głównych wyzwań w przeprowadzeniu transformacji cyfrowej w instytucj...
Dziewięć głównych wyzwań w przeprowadzeniu transformacji cyfrowej w instytucj...
 
LEAN w farmacji
LEAN w farmacjiLEAN w farmacji
LEAN w farmacji
 
Trendy w rozwoju pracowników na 2021 rok
Trendy w rozwoju pracowników na 2021 rokTrendy w rozwoju pracowników na 2021 rok
Trendy w rozwoju pracowników na 2021 rok
 
Webinarium 3 istotne wyzwania pracownicze 15-11-2021 | PwC
Webinarium 3 istotne wyzwania pracownicze 15-11-2021 | PwCWebinarium 3 istotne wyzwania pracownicze 15-11-2021 | PwC
Webinarium 3 istotne wyzwania pracownicze 15-11-2021 | PwC
 
5
55
5
 
(R)ewolucja w firmie
(R)ewolucja w firmie(R)ewolucja w firmie
(R)ewolucja w firmie
 
Trwała luka wdrożeniowa w polskich organizacjach. Wyniki badania Blue Fox.pdf
Trwała luka wdrożeniowa w polskich organizacjach. Wyniki badania Blue Fox.pdfTrwała luka wdrożeniowa w polskich organizacjach. Wyniki badania Blue Fox.pdf
Trwała luka wdrożeniowa w polskich organizacjach. Wyniki badania Blue Fox.pdf
 
Sukcesja czy rozwój biznesu?
Sukcesja czy rozwój biznesu?Sukcesja czy rozwój biznesu?
Sukcesja czy rozwój biznesu?
 
Raport_trwała_luka_wdrożeniowa_BlueFox_wersja2
Raport_trwała_luka_wdrożeniowa_BlueFox_wersja2Raport_trwała_luka_wdrożeniowa_BlueFox_wersja2
Raport_trwała_luka_wdrożeniowa_BlueFox_wersja2
 
Diaphane software 20.06.17
Diaphane software 20.06.17Diaphane software 20.06.17
Diaphane software 20.06.17
 
ERP jako system systemów
ERP jako system systemówERP jako system systemów
ERP jako system systemów
 
Bpm eois pm_14012015_pdf
Bpm eois pm_14012015_pdfBpm eois pm_14012015_pdf
Bpm eois pm_14012015_pdf
 
Wnioski z Raportu OBZZ_2016
Wnioski z Raportu OBZZ_2016Wnioski z Raportu OBZZ_2016
Wnioski z Raportu OBZZ_2016
 
Perspektywy Rozwoju NarzęDzi Informatycznych ZarząDzania
Perspektywy Rozwoju NarzęDzi Informatycznych ZarząDzaniaPerspektywy Rozwoju NarzęDzi Informatycznych ZarząDzania
Perspektywy Rozwoju NarzęDzi Informatycznych ZarząDzania
 
Magazyn akceleratora KPT ScaleUP
Magazyn akceleratora KPT ScaleUPMagazyn akceleratora KPT ScaleUP
Magazyn akceleratora KPT ScaleUP
 

Plus de Stowarzyszenie Jakości Systemów Informatycznych (SJSI)

Plus de Stowarzyszenie Jakości Systemów Informatycznych (SJSI) (20)

Star Trek: BDD Enterprise
Star Trek: BDD EnterpriseStar Trek: BDD Enterprise
Star Trek: BDD Enterprise
 
Model based testing as a BA tool
Model based testing as a BA toolModel based testing as a BA tool
Model based testing as a BA tool
 
Communication - Language of Leader
Communication - Language of LeaderCommunication - Language of Leader
Communication - Language of Leader
 
Miękkie umiejętności w pracy analityka biznesu
Miękkie umiejętności w pracy analityka biznesuMiękkie umiejętności w pracy analityka biznesu
Miękkie umiejętności w pracy analityka biznesu
 
Błędy w analizie z praktyki (nowe wydanie  )
Błędy w analizie z praktyki (nowe wydanie  )Błędy w analizie z praktyki (nowe wydanie  )
Błędy w analizie z praktyki (nowe wydanie  )
 
7 Skills for highly effective teams - workshop
7 Skills for highly effective teams - workshop7 Skills for highly effective teams - workshop
7 Skills for highly effective teams - workshop
 
Dancing with the devil - how to cooperate with a problematic customer
Dancing with the devil - how to cooperate with a problematic customerDancing with the devil - how to cooperate with a problematic customer
Dancing with the devil - how to cooperate with a problematic customer
 
Cosmic truths about software requirements
Cosmic truths about software requirementsCosmic truths about software requirements
Cosmic truths about software requirements
 
Zagraj w zaangażowanie
Zagraj w zaangażowanieZagraj w zaangażowanie
Zagraj w zaangażowanie
 
Analiza prawdziwie biznesowa - skąd biorą się projekty
Analiza prawdziwie biznesowa - skąd biorą się projektyAnaliza prawdziwie biznesowa - skąd biorą się projekty
Analiza prawdziwie biznesowa - skąd biorą się projekty
 
Internet of Things loves data - analysis of Industry 4.0
Internet of Things loves data - analysis of Industry 4.0Internet of Things loves data - analysis of Industry 4.0
Internet of Things loves data - analysis of Industry 4.0
 
Start with Accessibility: Why, How and What
Start with Accessibility: Why, How and WhatStart with Accessibility: Why, How and What
Start with Accessibility: Why, How and What
 
Agile business analyst
Agile business analystAgile business analyst
Agile business analyst
 
Analityk i architekt w czasach automatyzacji i robotyzacji biznesu
Analityk i architekt w czasach automatyzacji i robotyzacji biznesuAnalityk i architekt w czasach automatyzacji i robotyzacji biznesu
Analityk i architekt w czasach automatyzacji i robotyzacji biznesu
 
Jak sprzedać swój pomysł w 5 minut, czyli pitch deck dla BA
Jak sprzedać swój pomysł w 5 minut, czyli pitch deck dla BAJak sprzedać swój pomysł w 5 minut, czyli pitch deck dla BA
Jak sprzedać swój pomysł w 5 minut, czyli pitch deck dla BA
 
7 Skills for highly effective teams
7 Skills for highly effective teams7 Skills for highly effective teams
7 Skills for highly effective teams
 
[TestWarez 2017] Skomplikowane testowanie, skomplikowane terminy. Testowanie ...
[TestWarez 2017] Skomplikowane testowanie, skomplikowane terminy. Testowanie ...[TestWarez 2017] Skomplikowane testowanie, skomplikowane terminy. Testowanie ...
[TestWarez 2017] Skomplikowane testowanie, skomplikowane terminy. Testowanie ...
 
[TestWarez 2017] Przychodzi tester na rozmowę...
[TestWarez 2017] Przychodzi tester na rozmowę...[TestWarez 2017] Przychodzi tester na rozmowę...
[TestWarez 2017] Przychodzi tester na rozmowę...
 
[TestWarez 2017] A proper gun makes testing fun
[TestWarez 2017] A proper gun makes testing fun[TestWarez 2017] A proper gun makes testing fun
[TestWarez 2017] A proper gun makes testing fun
 
[TestWarez 2017] Zen testów wydajnościowych
[TestWarez 2017] Zen testów wydajnościowych[TestWarez 2017] Zen testów wydajnościowych
[TestWarez 2017] Zen testów wydajnościowych
 

Tester.pl - Numer 10

  • 1. Od redaktora Jak ten czas leci, zanim się obejrzeliśmy, a tu juŜ 10 – jubileuszowy – numer naszego kwartalnika. Mam nadzieje, ze numer jubileuszowy jest równie ciekawy jak 9 poprzednich. W tym numerze cztery ciekawe artykuły: 1. Joanna Droździel o zarządzaniu zmianą – jest to kontynuacja jej pracy z poprzedniego numeru 2. Moty Aharonovitz o tym jak skutecznie usprawnić testowanie oparte na wymaganiach 3. Mateusz Bukowski i Paweł Paterek o obiektach pozornych, ciekawe rozwiązanie związane z metodologią TDD; autorzy bardzo dokładnie na przykładzie opisują na czym to polega 4. Maciej Dusza o pisaniu dokumentacji – coś czego większość z nas nie lubi, autor pokazuje jak robić to dobrze Zgodnie z rozpoczętym w numerze 8 kwartalnika cyklem zamieszczamy w tym numerze sprawozdanie z konferencji, które odbyły się ostatnio: Łukasz śebrowski był w Düsseldorfie na SQS Software & Systems Quality Conferences i relacjonuje nam to wydarzenie. W czasie tworzenia tego numeru odbyła się IV Konferencja Jakości Systemów Informatycznych. Na gorąco moŜna jedynie powiedziec, ze była bardzo ciekawa – więcej o niej w następnym numerze Testera.PL Równocześnie chciałbym – kolejny raz - gorąco zachęcić wszystkich czytelników tego periodyku do aktywnej współpracy. Cały czas czekamy na Państwa uwagi, artykuły, felietony – wszystko, co Was interesuje, co chcielibyście przeczytać, czego się dowiedzieć. JeŜeli tylko będziemy mogli, postaramy się zrealizować Państwa postulaty.
  • 2. TESTER.PL Zarządzanie problemem i incydentem Joanna Droździel Joanna Droździel jest absolwentem Informatyki na Wydziale Elektrycznym Politechniki Warszawskiej. Obroniła pracę magisterską z zakresu metodyki ITIL. Posiada certyfikat ISEB Foundation. Od października 2006 roku prowadzi blok wykładów „Zarządzanie usługami IT” na Podyplomowym Studium Prowadzenia Projektów Informatycznych na Politechnice Waszawskiej. Obecnie pracuje w firmie CS Stars na stanowisku starszego analityka do spraw zapewnienia jakości, biorąc udział w projektach dla klientów zagranicznych. Strona 2 z 63
  • 3. TESTER.PL Zarządzanie zmianą Joanna Droździel Tak jak Ŝycie społeczne i cywilizacja poddane są nieustannej ewolucji, tak równieŜ i w działalności firmy zmiany informatyczne są konieczne. To czy siły wywołujące zmiany mają charakter ekonomiczny, techniczny, społeczny czy polityczny nie ma szczególnego znaczenia. Zmiany wdraŜane w sektorze informatycznym dotyczą przede wszystkim infrastruktury oraz oprogramowania, powstają najczęściej w trakcie standaryzacji działań produkcyjnych lub teŜ wynikają z potrzeby zapanowania nad zgłoszeniami uŜytkowników. JeŜeli jeszcze klika lat temu pomysł wykorzystania informatyki w projektach elektronicznej administracji spotykał się z ogólnym niedowierzaniem, to teraz pomysł ten juŜ nikogo nie dziwi. Wręcz przeciwnie, uwaŜa się Ŝe wykorzystanie informatyki w administracji przedsiębiorstwa nie będzie efektywne, jeŜeli w projektach elektronicznej administracji nie będą uwzględnione zmiany, jakim we współczesnym świecie ulega firma. Proces zarządzania zmianą Bez efektywnego procesu zarządzania zmianą dalszy rozwój cywilizacyjny byłby niemoŜliwy. Dlatego jeŜeli w jakiejś dziedzinie Ŝycia nie spotkaliśmy się z koniecznością wprowadzania zmian, to wcześniej czy później będziemy do tego zmuszeni. W wyniku zachodzących na świecie przemian: społecznych, cywilizacyjnych, ekonomicznych czy kulturowych zmieniają się takŜe obszary podległe działalności organizacji. Proces zarządzania zmianami w sektorze usług informatycznych jest nieodłącznym elementem działalności biznesowej przedsiębiorstwa. Gdy proces ten przebiega źle, wówczas oddziałuje to negatywnie równieŜ na stronę biznesową. Bez efektywnie przeprowadzonych zmian informatycznych zakłócona zostaje działalność biznesowa firmy. MenedŜer działu informatycznego często twierdzi, Ŝe kierowany przez niego zespół jest przygotowany na realizację procesu zmian. Z badań wynika, Ŝe nierzadko jest inaczej. MenedŜerowie radzą sobie z oceną i monitorowaniem efektów wprowadzonych zmian. Gorzej jest jednak z wywołaniem kryzysu, czyli stworzeniem impulsu do zmiany, czy teŜ z wprowadzeniem nowego porządku rzeczy, gdy sytuacja tego wymaga. Strona 3 z 63
  • 4. TESTER.PL KaŜda zmiana pociąga za sobą lawinę kolejnych. Dlatego ci, którzy ponieśli straty finansowe, wiedzą jak waŜny jest dobrze przeprowadzony proces zarządzania zmianą. PomoŜe on zbadać ryzyko wprowadzenia zmiany, oszacować ilość odpowiednich zasobów oraz sprawdzić, czy czas wprowadzenia zmiany został właściwie określony. Mając na celu przede wszystkim minimalizację liczby incydentów i problemów wynikających ze źle wprowadzonych zmian. W pracach nad pozostałymi procesami menedŜerowie mogli skorzystać z pomocy konsultantów, w przypadku procesu zarządzania zmianą pomoc ta moŜe okazać się bezwartościowa. Wynika to z faktu iŜ tylko osoba przebywająca w danej organizacji jest w stanie poznać realia rządzące firmą i dzięki temu określić kierunek koniecznych zmian. Poznając procesy metodyki ITIL zauwaŜamy, iŜ kolejny proces zaleŜy od poprzedniego i wpływa na następny, podobnie jest równieŜ z procesem zarządzania zmianą, który w większym lub mniejszym stopniu korzysta, oddziałuje, wpływa na pozostałe procesy. Konieczność zwiększenia pojemności usług lub teŜ utrzymanie jej na dotychczasowym poziomie (ale przy większej liczbie uŜytkowników, bądź ilości danych oraz liczby transakcji), moŜe wymusić konieczność wprowadzenia zmian. Procesy te nie będą działać efektywnie bez poprawnie przeprowadzonego procesu zarządzania zmianą, który w duŜej mierze zaleŜy od procesów zarządzania problemem oraz incydentem. Wynika to z ilości zmian zgłoszonych przez uŜytkowników na skutek wykrytych awarii, jak równieŜ powstałych na skutek działań proaktywnych w procesach zarządzania incydentem i problemem. Podstawą procesu zarządzania zmianą jest prośba o zmianę (RfC), od której rozpoczyna się proces. Kluczem do efektywnego wdroŜenia zmiany jest odpowiednio wypełniony formularz w wersji papierowej bądź elektronicznej. Wersja papierowa bardzo szybko okazała się niepraktyczna, zwłaszcza w firmach, gdzie liczba zgłaszanych zmian jest bardzo duŜa. Poszczególne etapy procesu odbywają się drogą elektroniczną za pomocą odpowiedniej aplikacji. Przystępując do procesu zarządzania zmianą warto tuŜ przed wdroŜeniem zaplanowanego modelu postępowania zabezpieczyć aktualny stan aplikacji, sprzętu czy konfiguracji. Z kaŜdej nieudanej zmiany moŜna się wycofać, ale tylko wtedy, gdy dysponujemy szczegółami sprzed wprowadzenia zmian. Są jednak zmiany, które aby mogły być zaimplementowane, wymagają jednak wielu konsultacji. NaleŜy wówczas skorzystać z pomocy organu Rada Doradcza (CAB), w skład której wchodzą nie tylko menedŜer zmiany i przedstawiciele zarządu, ale równieŜ osoby odpowiedzialne za pozostałe procesy metodyki ITIL. Rozpoczynając wdroŜenie procesu w firmie warto rozpocząć od niewielkich, lekko skomplikowanych zmian i dopiero wraz z upływem czasu sięgać po zmiany bardziej złoŜone. Strona 4 z 63
  • 5. TESTER.PL Dzięki takiej postawie, menedŜerowie opanują zasady, jakimi kieruje się proces zarządzania zmianą na tyle dobrze, by móc go przeprowadzać w sposób mechaniczny. Proces zarządzania zmianą został przedstawiony na rysunku poniŜej. Rysunek 1. Proces zarządzania zmianą. Proces zarządzania zmianą funkcjonuje dzięki osobie odpowiedzialnej za jego poprawne zaplanowanie oraz wdroŜenie. Tą osobą jest menedŜer zmiany a zarazem właściciel procesu. MenedŜer zmiany odpowiada za realizację juŜ zaplanowanych zmian, planowanie następnych, nadzór nad zmianami przeprowadzonymi, a więc generowanie raportów dla strony biznesowej oraz przedstawienie argumentów potwierdzających potrzebę planowania zmian. Autorzy metodyki ITIL warunkują sukces we wdroŜeniu metodyki od indywidualnych działań właściciela procesu. Z reguły ilość pracy przekracza jednak moŜliwości jednej osoby. Dlatego teŜ waŜne jest aby menedŜer dowolnego procesu zaangaŜował do pracy tyle osób, ile jest w danej chwili niezbędnych (uwzględniając moŜliwości finansowe przedsiębiorstwa). Z reguły na etapie planowania menedŜer korzysta z pomocy analityków a potrzebne informacje czerpie z Bazy Konfiguracji. Natomiast na etapie wdroŜenia zmiany korzysta z pomocy programistów, administratorów, testerów, a więc osób dostępnych w ramach codziennej działalności organizacji. Jednak nie wszystkie zmiany są wynikiem efektywnej pracy menedŜera zmiany, wiele próśb o zmianę pochodzi bezpośrednio od uŜytkowników. Zaleca się by co pewien czas zapytać uŜytkowników jakich zmian oczekują. Nawet jeŜeli w danej chwili brakuje moŜliwości ich Strona 5 z 63
  • 6. TESTER.PL realizacji, mogą stać się pomocne na etapie planowania przyszłych zmian. Bardzo wiele zgłoszeń nie moŜe zostać zrealizowanych ze względu na ograniczenia ustalone w umowie SLA pomiędzy klientem a dostawcą usług informatycznych. Warto jednak wszystkie zgłoszenia zachowywać poniewaŜ mogą one stać się źródłem bardzo cennej wiedzy w chwili wykonywania kolejnego przeglądu umowy. Równie istotne dla procesu zmian są wyniki badań opinii klientów gdyŜ to ich zdanie w decydującym stopniu powinno wyznaczać kierunek wewnętrznych zmian. Powinny to być badania systematyczne, a ich rezultaty muszą być moŜliwie szybko uwzględniane w projektach zmian, tak aby w efekcie przyniosły klientowi satysfakcję wcześniej, niŜ działania rynkowe konkurencji Komunikacja oraz informacja W procesie zarządzania zmianą kluczową rolę odgrywa informacja oraz komunikacja. W aspekcie związanym z informacją waŜne jest, by osoba odpowiedzialna za realizację procesu dysponowała gruntownym przygotowaniem merytorycznym. MenedŜer odpowiedzialny za proces zarządzania zmianą podejmując decyzję musi kierować się regułą: im więcej informacji tym lepiej. Podobnie jest z drugą istotną kwestią w procesie zarządzania zmianą - komunikacją. W przypadku niewielkiej zmiany, której wpływu uŜytkownik nie odczuje w swojej codziennej pracy, aspekt ten nie odgrywa tak waŜnej roli. Nie chodzi bynajmniej o uświadomienie uŜytkownika, Ŝe zmiana jest planowana, ale równieŜ o czynny jego udział w pracach nad procesem. Tak naprawdę ludzie znacznie częściej przeciwstawiają się zmianom, niŜ je akceptują. Dlatego by wywołać reakcje pozytywne, naleŜy zaangaŜować większą grupę pracowników w proces zarządzania zmianą. Dzięki takiej postawie uzyskamy zrozumienie konieczności wprowadzania zmian, a co za tym idzie, wiarę w powodzenie całej misji. By uniknąć negatywnych reakcji uŜytkowników na wprowadzenie zmian bądź ich brak naleŜy poprawić komunikację na linii uŜytkownik - zarząd. Tylko dzięki pełnej jasności i klarowności działań uzyskamy lepsze zrozumienie sytuacji. Czasami jednak radykalna zmiana jest łatwiejsza do zaakceptowania dlatego, Ŝe niewiele elementów nowej strategii przypomina stare procedury. Istotnym czynnikiem wpływającym na reakcję na zmianę jest czas. Im wolniej przebiegają zmiany, tym dotkliwiej są one odczuwane przez pracowników. Ryzyko wprowadzanych zmian Wielu dyrektorów słysząc słowo „zmiana” reaguje bardzo nieufnie. Zmiana kojarzy im się ze stresem oraz chaosem w działalności przedsiębiorstwa. Tak być nie musi, wystarczy, Ŝe więcej uwagi skierują na etap planowania, a nie jak to było zazwyczaj dopiero na etap realizacji. Strona 6 z 63
  • 7. TESTER.PL W praktyce jednak rzadko zdarza się by wprowadzenie zmian było poprzedzone formalnym procesem. Być moŜe jest to jedną z głównych przyczyn późniejszych negatywnych skutków innowacji. Aby uniknąć niespodzianek warto przed wdroŜeniem zaplanować krok po kroku sposób wprowadzania zmiany oraz opracować wszystkie warianty postępowania na wypadek awarii. Realizując proces zarządzania zmianą nie sposób nie otrzeć się o ryzyko poraŜki, kaŜda zmiana moŜe bowiem zakończyć się niepowodzeniem i kaŜdy menedŜer zmiany musi mieć tego świadomość. Co zrobić by tego uniknąć? Przede wszystkim wyeliminować sytuacje, w których działamy w pośpiechu i w warunkach improwizacji. Zmiany powinny być planowane z wyprzedzeniem. MenedŜer zmiany powinien opracować Plan Zmian (FSC) na okres np. jednego miesiąca, w którym wskaŜe zarówno elementy konfiguracji i infrastruktury, jak i osoby, których wprowadzane zmiany będą bezpośrednio dotyczyły. Tylko takie postępowanie daje szansę uniknięcia ewentualnej poraŜki. Celem menedŜera zmian jest zablokowanie zmian krótkowzrocznych, a więc takich, które w pierwszej chwili wydają się być dobrym rozwiązaniem, jednak w szerszej perspektywie nie są nim. Oczywiście takie postępowanie moŜe być skuteczne w przypadku zmian standardowych czyli takich, które nie są wymuszone krótkim terminem wykonania. Co zrobić w przypadku zmian pilnych? Jak wówczas uchronić organizację przed ryzykiem poraŜki? Przede wszystkim naleŜy zachować zimną krew; nawet w krótkim okresie realizacji procesu zarządzania zmianą nie powinno dojść do pominięcia Ŝadnego z etapów procesu. Pominięcie któregokolwiek z kroków powoduje automatycznie wzrost ryzyka. Słownik CAB - Change Advisory Board FSC - Forward Schedule of Changes ITIL - Information Technology Infrastructure Library SLA - Service Level Agreement RfC - Request for Change Strona 7 z 63
  • 8. TESTER.PL Jak skutecznie usprawnić testowanie oparte na wymaganiach (Requirements Based Testing - RBT) Moty Aharonovitz – Borland Software Corporation Moty Aharonovitz pracuje jako senior director of Product Strategy w firmie Borland. Ma ponad 15 lat doświadczenia w pracy nad rozwojem oprogramowania, dzięki czemu aktywnie wspiera i rozwija wizję Optymalizacji Dostarczania Oprogramowania (SDO) w przedsiębiorstwach i firmach informatycznych oraz czynnie wspiera rozwiązania z zakresu Zarządzania Jakością w Cyklu śycia aplikacji. Kontakt: moty.aharonovitz@borland.com. Strona 8 z 63
  • 9. TESTER.PL Jak skutecznie usprawnić testowanie oparte na wymaganiach (Requirements Based Testing - RBT) Moty Aharonovitz – Borland Software Corporation Naszym głównym zadaniem jako menedŜerów ds. jakości oraz testerów jest znajdowanie błędów w tworzonym oprogramowaniu. Na szczęście obecnie praca wielu z nas wykracza juŜ poza ujawnianie oraz śledzenie „pluskw” i obejmuje bardziej krytyczne zagadnienia związane ze sprawdzaniem, czy oprogramowanie które będzie wytwarzać nasza firma (jeszcze przez jego finalnym wyprodukowaniem) spełni oczekiwania uŜytkowników. Aby tego dokonać wiele organizacji zaczęło wykorzystywać Testowanie Oparte Na Wymaganiach (RBT). Dzięki zastosowaniu RBT praca zespołów testowych staje się efektywniejsza - testy bezpośrednio nawiązujące do konkretnych wymagań funkcjonalnych umoŜliwiają skuteczne dotarcie do źródła problemu poprzez bezpośrednie powiązanie z wymaganiami na dowolnym etapie cyklu Ŝycia aplikacji. W rezultacie otrzymujemy systematyczne i efektywne pokrycie obszaru testów, co sprawia, Ŝe w zasadzie testujemy to, co ma największe znaczenie dla naszego klienta. Wyzwanie jakie napotykamy, wdraŜając RBT w naszej organizacji, to dopasowanie go do istniejących procesów Działu Jakości i dotychczasowych praktyk testowania. Z własnego doświadczenia mogę zaproponować trzy praktyczne sugestie, jak ulepszyć podejście RBT: 1. Testujmy wcześnie i często, tak aby testowanie stało się czynnością równoległą do procesu tworzenia, rozciągało na wszystkie role, uświadamiając wszystkim uczestnikom i sponsorom projektu znaczenie jakości. 2. Testujmy z głową, nie instynktownie - zapewniając metodyczność i powtarzalność w planie testów zwiększamy przewidywalność i mierzalność procesu testowania. 3. Testujmy z wykorzystaniem metryk - pozwoli to określić status produkcji i działalności IT, umoŜliwiając zarządowi wgląd w stan wszystkich projektów IT w firmie oraz właściwie ocenić nasz wkład i zaangaŜowanie. Strona 9 z 63
  • 10. TESTER.PL Kryzys jakości Wiele analiz, w tym te opracowane przez Standish Group Chaos Report, opisuje dotychczasowe standardy w przemyśle IT: większość projektów IT wykracza poza załoŜony czas oraz budŜet. Niewystarczająca jakość wytwarzanego oprogramowania jest najwaŜniejszym czynnikiem odpowiedzialnym za niepowodzenia i często prowadzi do przebudowania kodu, wpływając na zakres i jakość produktu finalnego. Ponowne praca nad tymi samymi fragmentami kodu wydłuŜa czas powstawania aplikacji i znacząco pochłania zasoby, takŜe finansowe. Aby zmniejszyć zagroŜenia wynikające z błędów, koniecznie musimy zdać sobie sprawę z coraz silniejszych tendencji dbania o jakość powstających aplikacji juŜ od samego początku ich Ŝycia. Doświadczenia firm z branŜy IT oraz badania trendów rynkowych jasno wskazują na dwa podstawowe powody niskiej jakości tworzonych aplikacji: 1.) źle sformułowane wymagania oraz 2.) niewłaściwe ich pokrycie przez testy. Wady w specyfikacji wymagań Wielu z nas często spotyka się ze skargami uŜytkowników stwierdzających ewidentne braki w oprogramowaniu, które przeszło przez rygorystyczną kontrolę działu jakości i szereg testów. Najczęstszy powód? Wymagania były niewłaściwie sformułowane juŜ od samego początku projektu. Wymagania do rozbudowanych systemów często są ustalane w dwóch równoległych procesach, które następnie ewoluują równolegle w całym cyklu Ŝycia aplikacji. Wspomniane dialogi powstają w wyniku postawienia sobie dwóch pytań: "co potrzebujemy zbudować?” oraz "co moŜemy zbudować?” Jakość tych dialogów często określa ostateczną jakość budowanej aplikacji. Badania wykonane przez Jamesa Martina1 pokazują, Ŝe 56 procent wszystkich zidentyfikowanych błędów w projektach informatycznych ma swoje korzenie w fazie tworzenia wymagań. To samo badanie wykazuje, Ŝe około połowę błędów powstaje w wyniku niewłaściwie napisanych, dwuznacznych, niejasnych i niepoprawnych wymagań. Druga połowa wad oprogramowania moŜe zostać przypisana niewystarczającej specyfikacji (np. braku wymagań, 1 James Martin, “An Information Systems Manifesto” Strona 10 z 63
  • 11. TESTER.PL które po prostu zostały pominięte w fazie analizy). Wykres 1: Dystrybucja Wad w Projektach Oprogramowania Inne studia ujawniają podobne odkrycia:  82 procent przypadków wielokrotnie tworzonego kodu jest związanych się z błędami w wymaganiach2  Problemy w obszarze wymagań w 44 procentach przypadków są powodem odwołania projektu3  Tylko 54 procent początkowych wymagań projektowych jest właściwie zrozumiane4  Tylko 45 procent zebranych wymagań zostaje właściwie uŜyte5 Podsumowując moŜemy stwierdzić, Ŝe dwa najwaŜniejsze problemy związane z jakością wymagań to: • wymagania i specyfikacje są niekompletne (z powodu braku informacji od uŜytkowników i sponsorów) • zebrane wymagania są nienajlepszej jakości (głównie w wyniku braku zrozumienia potrzeb obu stron, znalezienia wspólnego języka oraz metod skutecznej weryfikacji zebranych załoŜeń). Problemy z pokryciem wymagań testami 2 Martin & Leffinwell Standish Group: Chaos Report 4 Standish Group: Chaos Report 5 Jacobs 3 Strona 11 z 63
  • 12. TESTER.PL Kiedy temat jakości pojawia się na końcowym etapie cyklu wytwarzania aplikacji, testowanie wymaga uprzedniego zakończenia fazy kodowania. W tym momencie zespół testerów znajduje się pod presją czasu, a ich zadaniem staje się jak najszybsze zweryfikowanie poprawności funkcjonalnej aplikacji. Ten etap często postrzegany jest jako wąskie gardło, które opóźnia wdroŜenie aplikacji. W takich warunkach nie tylko trudnym jest upewnienie się, co do poprawności wymagań, ale przede wszystkim ułoŜenie takiego test planu, który zapewni poprawność i pełne pokrycie wymagań, jak równieŜ widoczność róŜnych aspektów jakościowych testowanej aplikacji. Oczywiste jest, Ŝe praca testera przy powyŜszych załoŜeniach staje się nieefektywna i, co tu ukrywać, frustrująca. Osobnym wyzwaniem jest osiągnięcie zadowalającego poziomu pokrycia testami. ZłoŜoność dzisiejszych aplikacji stanowczo nam w tym nie pomaga. Coraz trudniejsze staje się wykonanie wszystkich moŜliwych scenariuszy, głównie ze względu na liczbę alternatywnych ścieŜek przechodzenia przez aplikację. Jakakolwiek próba usystematyzowania (lub zautomatyzowania) procesu połączenia wszystkich moŜliwych przypadków doprowadza nas po prostu to tak duŜej liczby kombinacji scenariuszy testowych, Ŝe testowanie staje się bardzo trudnym i długotrwałym, a przez to i nieopłacalnym z punktu widzenia organizacji zadaniem. W dodatku, w wielu przedsiębiorstwach zarządzanie zmianą, głównie ze względu na częstotliwość i skalę zmian w wymaganiach, staje się coraz trudniejsze. Stąd bez odpowiedniego wsparcia w zakresie zarządzania zmianą, często gubimy się jeśli chodzi o śledzenie powiązań pomiędzy zmieniającymi się wymaganiami oraz odzwierciedlenia tych zmian w przypadkach testowych. Sprawdzone praktyki RBT Podejście RBT, jeśli zostało właściwie zdefiniowane, adresuje bezpośrednio zaleŜność pomiędzy testami funkcjonalnymi a źródłem znalezionych błędów. Nawet, jeśli nasza organizacja nie uŜywa podobnych praktyk dzisiaj, większości z nas zapewne nie są obce procesy uwzględnione na poniŜszym wykresie. Strona 12 z 63
  • 13. TESTER.PL Wykres 2: Przebieg procesu RBT Równolegle z aktualnymi procesami testowymi, organizacje mogą spróbować zaadoptować następujące praktyki RBT: 1. Testujmy wcześnie i często W momencie, gdy mamy przygotowane wymagania oraz gotowy projekt i fragmenty kodu, przejrzyjmy je pod kątem celów biznesowych, przypadków uŜycia (use cases) i przypadków testowych (test cases). Starajmy się testować nasz projekt na jak najwcześniejszym etapie, poniewaŜ usterki zlokalizowane we wczesnych fazach rozwoju projektu są tańsze i łatwiejsze do usunięcia, w wyniku czego moŜemy się spodziewać znacząco mniejszej ilości „niespodzianek” na dalszych jego etapach. Testowanie powinno stać się czynnością równoległą do procesu tworzenia aplikacji. W ten sposób sponsorzy projektu w większym stopniu uświadomią sobie rolę jakości w całym procesie. Dzięki takiemu podejściu, testowanie będzie postrzegane juŜ nie jako tzw. „wąskie gardło” w produkcji oprogramowania, a bardziej jako kluczowy czynnik w ogólnym procesie zapewnienia jakości powstających systemów. PoniewaŜ sukces projektu informatycznego moŜe być bezpośrednio powiązany z solidnym zrozumieniem i zdefiniowaniem wymagań, promowanie RBT staje się doskonałą wizytówką firm dbających o jakość swoich produktów. Najlepsze praktyki w procesie Jakości Wymagań RBT zawierają: Strona 13 z 63
  • 14. TESTER.PL • Walidacja wymagań w odniesieniu do celów biznesowych - optymalizacja zakresu projektu przez zapewnianie, Ŝe kaŜde wymaganie zaspokaja przynajmniej jeden biznesowy cel. JeŜeli brakuje powiązań pomiędzy wymaganiami a celami biznesowymi, konieczna jest weryfikacja tych pierwszych. • Przegląd wymagań przez uŜytkowników - zmiany dokonywane w wymaganiach powinny być przeglądane i akceptowane przez uŜytkowników i odbiorców końcowych. W ten sposób upewnimy się, Ŝe dodatkowa praca wynikająca z modyfikacji wymagań nie będzie zmarnowana. • Tworzenie przypadków uŜycia - kaŜdy przypadek uŜycia powinien być powiązany z odpowiednim wymaganiem. Jeśli któryś z przypadków uŜycia nie ma takiego przyporządkowania oznacza, Ŝe wymagania są niekompletne. • Wykorzystanie technik analizy języka - wyszukiwanie i poprawianie problematycznych wyraŜeń / sformułowań w opisie wymagań, pozwala na uniknięcie ich wieloznaczności i niejasności oraz wyeliminowanie pomyłek. Pozostawienie takich fraz bez poprawy powoduje iŜ mogą być one w późniejszych krokach interpretowane w róŜny sposób przez róŜne osoby, co z kolei moŜe prowadzić do zakłopotania i błędów w kolejnych etapach. Dwuznaczne określenia produkują równieŜ „nietestowalne” przypadki uŜycia. 2. Testujmy z głową, nie instynktownie Większość przedsiębiorstw ceni doświadczonych testerów, licząc, Ŝe „wyłapią” błędy które inni, mniej doświadczeni testerzy mogliby przeoczyć. O ile jednak pojedynczy „ekspert ds. jakości” moŜe być kuszącym ograniczeniem kosztów firmy, o tyle sytuacja taka zwiększa ryzyko polegania na instynkcie jednej osoby zamiast na grupowym racjonalnym podejściu. Obecnie systematyczne i rygorystyczne planowanie przypadków testowych nie jest powszechnie stosowaną praktyką. Częściej spotykamy się raczej z podejściem bardziej intuicyjnym, polegającym na wyczuciu, co jednak w efekcie moŜe prowadzić do nieprzewidywalnej jakości produktów końcowych. Doświadczenia firm stosujących podejście bazujące na RBT wskazują na fakt, Ŝe stosowanie metodycznego i systematycznego projektowania przypadków testowych jest najefektywniejszą polityką w zakresie jakości, zwłaszcza w perspektywie rozwoju firmy. Rygorystyczne i systematyczne zasady projektowania przypadków testowych uniezaleŜniają Strona 14 z 63
  • 15. TESTER.PL proces kontroli jakości od konkretnych testerów. W zamian wnoszą metodyczną powtarzalność do procesu planowania testów, zapewniając tym samym przewidywalną powtarzalność pokrycia testami. Organizacje bazujące na tej metodzie duŜo łatwiej mogą wprowadzać techniki optymalizacji procesu testowania, redukując przypadki testowe do liczby efektywnie pokrywającej wymagania, dzięki czemu uzyskują przyspieszenie cyklu testowego oraz utrzymują ten proces na poziomie ułatwiającym zarządzanie nim. W większości organizacji niemal wszyscy analitycy biznesowi uŜywają „naturalnego” języka do opisu wymagań. Wiemy, iŜ uŜywanie takiego języka moŜe utrudnić osiągnięcie pełnego pokrycia aplikacji testami, poniewaŜ testerzy bazując na potocznie sformułowanych wymaganiach muszą odwoływać się do własnej intuicji, aby określić stopień pokrycia testami obszaru tak zdefiniowanych wymagań. Innymi słowy, pracując z potocznie sformułowanymi wymaganiami organizacje nie mają moŜliwości dokładnej kontroli pokrycia ich przypadkami testowymi, co skutkuje w późniejszych błędach w wypuszczonej wersji aplikacji. W celu usystematyzowania pokrycia wymagań odpowiadającymi im testami firmy i działy produkujące oprogramowanie powinny skupić się na sformalizowaniu reprezentacji wymagań. Kiedy uda się to osiągnąć, będzie moŜliwe stworzenie szkieletu przypadków testowych zapewniających optymalne pokrycie wymagać, docelowo zaś powstaną z nich juŜ właściwe testy, które będziemy mogli uruchamiać i przeprowadzać. Aby wprowadzić usystematyzowanie i odpowiednią strukturę do wymagań sformułowanych przy pomocy naturalnego języka opisu moŜna skorzystać z wielu dostępnych technik. Ich celem jest odkrycie związków przyczynowo-skutkowych zawartych w wymaganiach, a przez to przedstawienie ich w postaci zestawu warunków (przyczyn) oraz wynikających z nich akcji (skutków). Tabele przyczynowo skutkowe są jedną z takich technik. Innym sposobem na osiągnięcie podobnego rezultatu jest przedstawienie wymagań w postaci wykresów przepływu (flowchart), które w naturalny sposób pokazują związki przyczynowo-skutkowe, jak równieŜ gałęzie warunkowe odpowiednich akcji. To swoiste „tłumaczenie” wymagań pozwala na duŜo łatwiejsze zbudowanie w oparciu o nie konkretnych przypadków testowych. Logiczny zestaw tych ostatnich moŜe być stworzony automatycznie bądź „ręcznie” tak, by stanowił odbicie zdefiniowanych i przetłumaczonych w powyŜszy sposób wymagań. NaleŜy pamiętać, Ŝe tak stworzony zestaw przypadków testowych moŜe zawierać nachodzące na siebie testy. W celu zoptymalizowania ich liczby przy zachowaniu Strona 15 z 63
  • 16. TESTER.PL pełnego pokrycia wymagań moŜemy skorzystać z tablic decyzyjnych (w przypadku zastosowania tabel przyczynowo-skutkowych na etapie strukturyzowania wymagań). W przypadku, jeśli w tym celu opracowane zostały uprzednio wykresy zamiast tabel, optymalizacja sprowadza się do znalezienia unikatowych ścieŜek przepływu na wykresach. Do tego celu moŜna wykorzystać wiele sprawdzonych algorytmów. Nawet po zaprojektowaniu przypadków testowych, wciąŜ będziemy mieli do czynienia z prawdopodobieństwem występowania w nich błędów. Aby wykryć potencjalne błędy, organizacje stosujące podejście RBT angaŜują zarówno analityków jak i uŜytkowników końcowych do weryfikacji przypadków testowych zanim powstaną na ich podstawie właściwe testy. To podejście pozwala wszystkim zainteresowanym ponownie zapoznać się z wymaganiami i przypadkami testowymi, co daje moŜliwość ich dokładniejszej weryfikacji, jak równieŜ wyłapania błędów powstałych w procesie przenoszenia wymagań z języka opisowego na systematyczny. Dodatkowo, firmy charakteryzujące się podejściem do cyklu tworzenia aplikacji jako całości, starają się interaktywnie włączyć zadania Działu Jakości w prace analityków i programistów, co staje się moŜliwe właśnie dzięki przejściu na usystematyzowane i zoptymalizowane przypadki testowe wypracowane w poprzednich fazach. W opisanym przedsiębiorstwie projekt aplikacji bywa przeglądany pod kątem przypadków testowych, poniewaŜ stanowią one niejako inną formę zdefiniowanych wymagań. W ten sposób zespoły projektowe nabierają pewności, Ŝe projekt spełnia załoŜenia i oczekiwania w postaci wymagań. JeŜeli model ich nie spełnia moŜe to być oznaką, Ŝe albo nie odpowiada tymŜe, i wymaga dalszej pracy, albo Ŝe jest problem w samych wymaganiach, co pociąga za sobą dodatkową pracę analityków. Aby uniknąć kosztownych przeróbek, przypadki testowe powinny być przeglądane równieŜ przez programistów tworzących kod. To zapewni im dobre zrozumienie, które elementy oraz w jakim zakresie będą testowane, a jednocześnie da strukturalny zestaw wymagań. Ostatecznie pojedyncze moduły kodu powinny zostać zweryfikowane pod kątem uporządkowanych wymagań tak, aby upewnić się, Ŝe powstały kod w pełni odpowiada załoŜeniom. Praktyka pokazuje, Ŝe duŜo łatwiej jest nam dopasować postać algorytmiczną kodu do uporządkowanych wymagań niŜ do ich niestrukturalnej postaci. Strona 16 z 63
  • 17. TESTER.PL 3. Nie zapominajmy o miarach i usprawnieniach Istnieje opinia, Ŝe czego nie da się zmierzyć, nie istnieje. UŜywając metody RBT firmy mogą nie tylko zarządzać procesem zarządzania jakością, ale i go usprawniać. W procesie RBT moŜe być stosowanych wiele róŜnych miar kwantyfikujących status projektu oraz aktywność jego członków. Stanowi to duŜą pomoc dla menedŜerów działu oraz menedŜerów projektu, pozwalając na dokładny wgląd w zakres całego portfolio IT. Przykłady informacji, które powinny być mierzone:  procent wymagań przejrzanych przez projektantów i programistów  procent wymagań, które zawierają dwuznaczności  procent wymagań o strukturalnej formie  procent formalnych wymagań pokrytych przypadkami testowymi  logiczne i faktyczne pokrycie kodu Rola śledzenia zmian w RBT Śledzenie zmian odgrywa takŜe istotną rolę w organizacjach wykorzystujących RBT - od podtrzymywania stałego przepływu informacji o zmianach w stosunku do wymagań, przypadków testowych i testów jest krytyczne. Te informacje są niezbędne dla prawidłowego monitorowania postępu i stanu projektu, jak równieŜ do właściwego zarządzania zmianami wymagań. Bez tej wiedzy trudne jest dokładne określenie, które przypadki testowe i testy powinny zostać zmodyfikowane w przypadku zmiany w wymaganiach. Nawet jeśli rozumiemy znaczenie śledzenia zmian, w wielu firmach tworzących oprogramowanie ta wiedza wciąŜ pozostaje bardzo trudna do uchwycenia we właściwy sposób. Podstawowym powodem tego jest to, Ŝe większość narzędzi dostępnych obecnie na rynku wymaga od niemal wszystkich członków zespołów projektowych ręcznego wprowadzania i zarządzania śledzeniem zmian. Z oczywistych powodów takie podejście jest raczej niemoŜliwe do zaakceptowania. Aby podołać temu wyzwaniu, naleŜy powaŜnie zastanowić się nad rozwiązaniami umoŜliwiającymi połączenia pomiędzy produktami poszczególnych faz wytwarzania oprogramowania. Pierwszy krok do Zarządzania Jakością Cyklu śycia Aplikacji Stosowanie solidnych praktyk RBT przez organizacje tworzące oprogramowanie bardzo szybko dostarcza im odpowiednie narzędzia i procesy umoŜliwiające maksymalizację wartości biznesowej działalności. Strona 17 z 63
  • 18. TESTER.PL Zespoły wdraŜające podejście RBT poprzez fakt, Ŝe robią ten pierwszy krok w celu zwiększenia jakości tworzonych produktów, stają się tym samym inicjatorami szeregu zmian prowadzących do implementacji Zarządzania Jakością w Cyklu śycia Aplikacji (Lifecycle Quality Management – LQM). Poprzez połoŜenie nacisku na zapewnienie jakości na wszystkich etapach wytwarzania oprogramowania, a nie koncentrowanie się na jakości samego kodu, działania z obszaru LQM zapewniają firmom podniesienie norm jakości i usług oraz systematyczną redukcję kosztów związanych z wielokrotnym powtarzaniem tej samej pracy lub prób opanowania złoŜonych projektów. Ponadto, działania LQM znacząco przyspieszą ścieŜkę do Optymalizacji Procesu Dostarczania Oprogramowania (Software Delivery Optimization - SDO), która z kolei pomaga przekształcić tworzenie i rozwój oprogramowania w zarządzalny proces biznesowy, zapewniając tym samym zwiększoną kontrolę, przewidywalność oraz wydajność w całym procesie dostarczania oprogramowania. Strona 18 z 63
  • 19. TESTER.PL Obiekt pozorny. A moŜe coś innego? Kierunek rozwoju testów jednostkowych Mateusz Bukowski i Paweł Paterek Mateusz Bukowski jest absolwentem Akademii Górniczo Hutniczej w Krakowie, kierunku Informatyka na Wydziale Elektrotechniki, Automatyki, Informatyki i Elektroniki. Obecnie jest pracownikiem Motorola Polska Electronics Sp. z o.o. gdzie zajmuje się testowaniem systemu bezpieczeństwa publicznego TETRA. Jego zainteresowania to Ŝeglarstwo, cross country i rozwiązywanie łamigłówek logicznych. Paweł Paterek jest absolwentem Wydziału Elektrotechniki, Automatyki, Informatyki i Elektroniki Akademii Górniczo-Hutniczej w Krakowie, kierunek Elektronika i Telekomunikacja. Obecnie jest pracownikiem firmy Motorola Polska Electronics Sp. z o.o., w której zajmuje się testowaniem oprogramowania systemów czasu rzeczywistego oraz systemów wbudowanych dla stacji bazowych w standardzie TETRA. Jego dziedziną zainteresowań jest modelowanie i ocena efektywności pracy sieci komputerowych. Strona 19 z 63
  • 20. TESTER.PL Obiekt pozorny. A moŜe coś innego? Kierunek rozwoju testów jednostkowych Mateusz Bukowski i Paweł Paterek W dzisiejszym świecie, dostarczenie produktu na czas, nie jest juŜ najwaŜniejszym celem. DuŜo większe znaczenie, zaczyna odgrywać jakość dostarczanego oprogramowania. PoniewaŜ testowanie zabiera coraz więcej czasu i wysiłku, niezbędne jest uproszczenie technik testerskich przy jednoczesnym zachowaniu poziomu znajdowanych defektów. W tym artykule zapoznamy się z obiektami pozornymi i zaślepkami, które wnoszą nową jakość do testów oraz pokaŜemy, dlaczego warto korzystać z tych pierwszych. Zanim przedstawimy nowe sposoby testowania, przypomnijmy, czego dotyczą testy jednostkowe oraz czym jest programowanie sterowane testami. Test jednostkowy (Unit test) to procedura mająca na celu walidację poprawności działania danej jednostki kodu źródłowego. Przez jednostkę kodu źródłowego rozumiemy najmniejszą i moŜliwą do przetestowania część oprogramowania. W programowaniu obiektowym (ang. Object Oriented Programming) taką jednostkę kodu stanowi klasa [8]. Testy jednostkowe odgrywają znaczącą rolę w procesie wytwarzania oprogramowania. UmoŜliwiają one wykrycie wielu błędów w kodzie juŜ na etapie implementacji lub we wstępnej fazie testów. Pisane są najczęściej przez te same osoby, które zajmują się tworzeniem danego fragmentu oprogramowania. Testy jednostkowe z załoŜenia są proste i szybkie w uruchomieniu. Testują wyodrębnioną część funkcjonalności w całkowitej izolacji od reszty systemu. Pozwala to na zautomatyzowanie znaczącej części procesu testowania. Tym samym pozwala to na częste sprawdzanie czy wprowadzane zmiany w istniejącym kodzie nie powodują błędów. Testy jednostkowe nie weryfikują wszystkich wymagań funkcjonalnych danego systemu. Jest to zadanie testów akceptacyjnych wykonywanych przez odbiorcę oprogramowania [4],[10]. Ten rodzaj testów nie ma za zadania sprawdzać interakcji między róŜnymi obiektami, co wynika wprost z ich definicji. Nie mogą wobec tego zastąpić testów integracyjnych i systemowych w procesie testowania. Są jednak ich bardzo dobrym uzupełnieniem. Strona 20 z 63
  • 21. TESTER.PL Bardzo często testowane klasy posiadają powiązania do innych klas. Utworzenie referencji do takich obiektów w środowisku testowym niejednokrotnie jest niemoŜliwe. Jednym z rozwiązań tego problemu są właśnie obiekty zastępcze powszechnie zwane obiektami pozornymi (mock). Są to specjalnie przygotowane przez nas implementacje interfejsów mogące zastąpić problematyczne części kodu oraz ułatwić wykonanie testów. Programowanie sterowane testami (Test Driven Develomnent TDD) zakłada, Ŝe Ŝaden fragment oprogramowania nie powstanie zanim nie napiszemy do niego odpowiedniego testu. Pisany kod musi spełnić wymagania testu, a następnie moŜna przeprowadzić na nim odpowiedni refactoring1, który pozwoli na ustalenie ostatecznego nazewnictwa metod czy obiektów. Stosowanie tej metodologii ma róŜne zalety, m.in. pisząc pełny zbiór testów przed napisaniem właściwego kodu piszemy zarazem specyfikację jego działania, po czym sama walidacja jest juŜ automatyczna. PoniewaŜ przed napisaniem kodu wszystkie testy kończą się błędem, a w miarę jak nasz kod spełnia coraz więcej wymagań tych błędów jest mniej, to otrzymujemy tym samym miarę, w jakim stopniu nasz kod realizuje załoŜenia projektowe [6],[1]. TDD pozwala małymi krokami (poprzez napisanie najmniejszej części kodu, która powoduje pomyślne przejście testu) na sukcesywne i skuteczne realizowanie danego projektu. Jednocześnie tak napisany kod jest mniej skomplikowany, dostajemy wysokie pokrycie testami, a odnajdywanie błędów staje się znacznie prostsze. Jednak, kiedy test dotyczy kodu odwołującego się do baz danych, zdalnych obiektów lub połączeń sieciowych napisanie go moŜe być bardzo problematyczne. Chcąc jednak być w zgodzie z koncepcją metodologii TDD i testować równieŜ takie obiekty, musimy zastąpić obiekty, do których odwołuje się testowany kod i zaimitować ich obecność w testowanym kodzie. Jednym z moŜliwych sposobów testowania takiego kodu jest wykorzystanie wspomnianych wyŜej lekkich obiektów pozornych lub duŜo cięŜszych zaślepek [11],[14]. W naszych rozwaŜaniach będziemy posługiwać się następującym przykładem. Mamy bazę danych ksiąŜek. KaŜdy element opisany jest następującymi parametrami: autor, tytuł oraz identyfikator. Ponadto w bazie przechowywana jest równieŜ liczba dostępnych egzemplarzy kaŜdej ksiąŜki. Z bazą danych moŜemy połączyć się poprzez BookManager’a, z którego korzysta BookClient. Strona 21 z 63
  • 22. TESTER.PL Rys. 1 Schemat systemu zamawiania ksiąŜek public interface BookManager { public boolean connect(); public boolean disconnect(); public TreeMap<Book, Integer> bookList(); public boolean order(BookOrder bookOrder) throws BookNotFoundException, NotEnoughBooksException; } public class BookClient { public static final String CONNECT_ERROR = "Database connect error"; public static final String DISCONNECT_ERROR = "Database disconnect error"; public static final String BOOK_NOT_FOUND_ERROR = "Book not found error"; public static final String NOT_ENOUGH_BOOKS_ERROR = "Not enough books error"; public static final String AVAILABLE_BOOKS = "Available books:"; public static final String ORDERED_BOOKS = "Ordered books:"; public static final String BOOK_ORDER_FAIL = "Book order failed error"; private BookManager bookManager; private String message; public BookClient() { bookManager = new DefaultBookManager(); message = ""; } public BookClient(BookManager bookManager) { this.bookManager = bookManager; message = ""; } public String getMessage() { Strona 22 z 63
  • 23. TESTER.PL return message; } public void getBookList() { if (!bookManager.connect()) { message = CONNECT_ERROR; return; } message = AVAILABLE_BOOKS + bookListToString(bookManager.bookList()); if (!bookManager.disconnect()) { message = DISCONNECT_ERROR; return; } } public void orderBooks(BookOrder bookOrder) { if (!bookManager.connect()) { message = CONNECT_ERROR; return; } try { if (bookManager.order(bookOrder)) { message = ORDERED_BOOKS + bookListToString(bookOrder.getBookList()); } else { message = BOOK_ORDER_FAIL; } } catch (BookNotFoundException e) { message = BOOK_NOT_FOUND_ERROR; } catch (NotEnoughBooksException e) { message = NOT_ENOUGH_BOOKS_ERROR; } if (!bookManager.disconnect()) { message = DISCONNECT_ERROR; return; } } public static String bookListToString(TreeMap<Book, Integer> bookList) { StringBuilder result; result = new StringBuilder(); for (Book book: bookList.keySet()) { result.append("n"); result.append(book.getId()); result.append(" "); result.append(book.getAuthor()); result.append(" "); result.append(book.getTitle()); result.append(" "); result.append(bookList.get(book)); } return result.toString(); } Strona 23 z 63
  • 24. TESTER.PL } Implementacja pozostałych NotEnoughBooksException oraz klas: Book, BookOrder, BookNotFoundException nie jest DefaultBookManager, istotna w naszych rozwaŜaniach. Niezbędna jest implementacja funkcji equals, hashCode oraz compareTo w klasie Book, poniewaŜ będziemy uŜywać tych obiektów jako kluczy w TreeMap’ie. Testowaną klasą jest BookClient. BookManager jest interfejsem, który będą implementować konkretne obiekty. Domyślnie jest to klasa DefaultBookManager. Na potrzeby testów dodaliśmy drugi konstruktor, w którym przekazujemy specjalnie spreparowany BookManager. Inną stosowaną praktyką jest równieŜ korzystanie z metod ‘set’. Wspomniane wcześniej obiekty pozorne nie są jedyną moŜliwością na zastąpienie prawdziwych obiektów w obiekcie testowanym. Często teŜ bywają mylone z podobnymi do nich w zastosowaniu, lecz róŜniącymi się funkcjonalnie obiektami atrapami (ang. dummy objects), obiektami falsyfikatami (ang. fake objects) oraz zaślepkami (ang. stubs). Obiekty atrapy przekazywane są do obiektu testowanego, ale nigdzie nie są uŜywane i zazwyczaj słuŜą jedynie do wypełnienia listy parametrów testowanej metody. Falsyfikaty (w Ŝargonie informatycznym często nazywane fake’ami) posiadają namiastkę działającej implementacji, najczęściej stworzoną ręcznie w ramach kodu testów (na przykład w postaci klasy anonimowej). Implementacja ta jest zazwyczaj skrócona do minimum, co sprawia, Ŝe nie nadają się one do uŜycia w prawdziwym kodzie. Wadami falsyfikatów są: czas poświęcony na ich stworzenie, zwiększenie zawartości całego projektu, a takŜe konieczność ich utrzymywania, w trakcie zmiany interfejsów kodu, z których one korzystają. Zaślepki mogą być napisane ręcznie lub generowane automatycznie i są najczęściej stosowane wtedy, gdy kod nie jest znany, nie jest gotowy na etapie testowania danego obiektu lub, gdy nie ma moŜliwości dostępu do danej części kodu, co czasami znacznie ułatwia i przyspiesza proces tworzenia oprogramowania [2], [5]. Zaślepki symulują zachowanie prawdziwego kodu. W naszym przykładzie będzie łączył się z prawdziwą bazą danych. Innym razem w celu dogłębnego przetestowania z uŜyciem tej techniki musielibyśmy uruchomić serwer http. Zaślepki są bardzo kosztowne w utrzymaniu. Jeśli są stosowane we wczesnych fazach testowania mogą później słuŜyć jako zaląŜki dla implementacji prawdziwych klas. Obiektami pozornymi zajmiemy się dokładniej w dalszej części. Implementacja BookManager’a moŜe być jednym z powyŜszych typów: falsyfikat, zaślepka, obiekt pozorny. Przyjrzyjmy się teraz konkretnym falsyfikatom i zaślepkom oraz odpowiadającym im testom jednostkowym. Strona 24 z 63
  • 25. TESTER.PL public class FakeBookManager implements BookManager { private TreeMap<Book, Integer> bookList; private int call; public FakeBookManager() { bookList = new TreeMap<Book, Integer>(); bookList.put(new Book(101, "Ernest Hemingway", "The Old Man and the Sea"), 3); bookList.put(new Book(102, "Gabriel Garcia Marquez", "One Hundred Years of Solitude"), 5); bookList.put(new Book(103, "Joanne Kathleen Rowling", "Harry Potter and the Deathly Hallows"), 27); call = 0; } public boolean connect() { return true; } public boolean disconnect() { return true; } public TreeMap<Book, Integer> bookList() { return bookList; } public boolean order(BookOrder bookOrder) throws BookNotFoundException, NotEnoughBooksException { call++; switch (call % 8) { case 0: case 2: case 4: return true; case 1: case 3: case 5: return false; case 6: throw new BookNotFoundException(); case 7: throw new NotEnoughBooksException(); default: return true; } } } Strona 25 z 63
  • 26. TESTER.PL Jak widzimy nasz falsyfikat ma zaszytą namiastkę logiki. Wiedząc, który raz wywołujemy metodę order(), moŜemy sprawdzić czy BookClient zachowuje się w poprawny sposób. public class BookClientTestFakeBookManager extends TestCase { private TreeMap<Book, Integer> bookList; private static final Book QUO_VADIS = new Book(1001, "Henryk Sienkiewicz", "Quo Vadis"); private static final Book PAN_TADEUSZ = new Book(2001, "Adam Mickiewicz", "Pan Tadeusz"); private static final Book CHLOPI = new Book(3001, "Wladyslaw Reymont", "Chlopi"); @Override protected void setUp() throws Exception { super.setUp(); bookList = new TreeMap<Book, Integer>(); bookList.put(QUO_VADIS, 2); bookList.put(PAN_TADEUSZ, 3); bookList.put(CHLOPI, 5); } public void testOrderGlobal() { BookClient client; FakeBookManager fake; BookOrder bookOrder; String message; fake = new FakeBookManager(); client = new BookClient(fake); bookOrder = new BookOrder(); bookOrder.addBook(CHLOPI); client.orderBooks(bookOrder); message = BookClient.BOOK_ORDER_FAIL; assertEquals(message, client.getMessage()); client.orderBooks(bookOrder); message = BookClient.ORDERED_BOOKS + BookClient.bookListToString(bookOrder.getBookList()); assertEquals(message, client.getMessage()); client.orderBooks(bookOrder); client.orderBooks(bookOrder); client.orderBooks(bookOrder); client.orderBooks(bookOrder); message = BookClient.BOOK_NOT_FOUND_ERROR; assertEquals(message, client.getMessage()); client.orderBooks(bookOrder); message = BookClient.NOT_ENOUGH_BOOKS_ERROR; assertEquals(message, client.getMessage()); Strona 26 z 63
  • 27. TESTER.PL } } W przykładzie z zaślepką uŜyliśmy najprostszego rozwiązania. Jako bazy danych uŜyliśmy Accessa. UŜywanie tej aplikacji nie wymaga obszernej wiedzy z zakresu zarządzania bazami danych. Access jest łatwy do konfiguracji, prosty w obsłudze i świetnie nadaje się do celów testowych. Gdybyśmy chcieli symulować serwer http nie musimy mieć postawionego Apache’a. MoŜemy uŜyć prostego symulatora Jetty, który jest prostą aplikacją javową. public class StubBookManager implements BookManager { private Connection conn; private String url; private String username; private String password; public StubBookManager(String location) { url = "jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=" + location; username = ""; password = ""; } public boolean connect() { try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conn = DriverManager.getConnection(url, username, password); } catch (ClassNotFoundException e) { return false; } catch (SQLException e) { return false; } return true; } public boolean disconnect() { try { conn.close(); } catch (SQLException e) { return false; } return true; } public TreeMap<Book, Integer> bookList() { TreeMap<Book, Integer> result; Statement st; ResultSet rs; Book book; int quantity; Strona 27 z 63
  • 28. TESTER.PL result = new TreeMap<Book, Integer>(); try { st = conn.createStatement(); rs = st.executeQuery("SELECT * FROM books"); while (rs.next()) { book = new Book(rs.getInt("id"), rs.getString("author"), rs.getString("title")); quantity = rs.getInt("quantity"); result.put(book, quantity); } } catch (SQLException e) { return new TreeMap<Book, Integer>(); } return result; } public synchronized boolean order(BookOrder bookOrder) throws BookNotFoundException, NotEnoughBooksException { TreeMap<Book, Integer> bookList; Statement st; int quantity; bookList = bookList(); for (Book book: bookOrder.getBookList().keySet()) { if (!bookList.containsKey(book)) { throw new BookNotFoundException(); } quantity = bookList.get(book) - bookOrder.getBookList().get(book); if (quantity < 0) { throw new NotEnoughBooksException(); } try { st = conn.createStatement(); st.executeUpdate( "UPDATE books" + " SET quantity = " + quantity + " WHERE id = " + book.getId() + " AND author = '" + book.getAuthor() + "'" + " AND title = '" + book.getTitle() + "'"); } catch (SQLException e) { return false; } } return true; } } Do poprawnego uruchomienia poniŜszego testu konieczne jest dodanie katalogu ‘data’ z odpowiednią plikiem bazy danych ‘book.mdb’. Strona 28 z 63
  • 29. TESTER.PL public class BookClientTestStubBookManager extends TestCase { public void testConnectOk() { BookClient client; StubBookManager stub; String message; stub = new StubBookManager("./data/book.mdb"); client = new BookClient(stub); client.getBookList(); message = BookClient.AVAILABLE_BOOKS; assertEquals(message, client.getMessage()); } public void testConnectFail() { BookClient client; StubBookManager stub; String message; stub = new StubBookManager("dummy.mdb"); client = new BookClient(stub); client.getBookList(); message = BookClient.CONNECT_ERROR; assertEquals(message, client.getMessage()); } } Obiekt pozorny (mock) jest imitacją stworzoną na potrzeby testu jednostkowego w celu sprawdzenia poprawności współpracy testowanego obiektu z jego najbliŜszym otoczeniem [9]. Bardziej formalne interpretacje tego pojęcia to: a) obiekt pozorny jest pewną atrapą zastępującą prawdziwy obiekt, który jest niedostępny lub trudny do uŜycia w scenariuszu testowym b) obiekt pozorny musi posiadać mechanizm do automatycznej walidacji ustawionych dla niego wcześniej oczekiwanych zachowań [4],[10]. Obiekty te powinny zawsze zwracać z góry określone dane, umoŜliwiając testowanie logiki aplikacji w jednoznaczny sposób, bez konieczności odwoływania się do prawdziwych obiektów, bądź baz danych. Ich logika oraz implementacja powinna być najprostsza jak to tylko moŜliwe oraz niezaleŜna od innych obiektów pozornych, bądź fragmentów testowanej aplikacji. Mechanizm automatycznej walidacji powinien zadziałać w momencie wystąpienia błędu, umoŜliwiając szybkie przerwanie testu i łatwą lokalizację przyczyny problemu [4],[9]. Strona 29 z 63
  • 30. TESTER.PL Program zorientowany wzajemnie przetestować ze sobą działanie obiektowo jest siecią współpracujących. obiektu, naleŜy obiektów Aby dobrze przetestować poprawność jego współpracy z obiektami, z którymi wymienia informacje w prawdziwej aplikacji. PoniewaŜ unit test polega na testowaniu obiektu w izolacji od innych obiektów istniejących w aplikacji, obiekty wymieniające informacje z obiektem testowanym muszą zostać podmienione na obiekty Rys. 2 Wymiana informacji Test – Obiekt – Obiekt Pozorny pozorne, patrz rys. 2 [6]. W chwili, kiedy na obiekcie zastępczym wystąpi inne wywołanie metody niŜ oczekiwane, lub wywołanie nastąpi z niewłaściwymi parametrami lub teŜ, gdy wywołanie wystąpi, a nie było ono jawnie oczekiwane test jest automatycznie przerywany i jego wynik ustawiany jest na negatywny [7]. Powody, dla których uŜycie obiektów pozornych moŜe być nieocenione, są przeróŜne. Prawdziwy obiekt wykazuje zachowanie niedeterministyczne. Jest trudno konfigurowalny. Jego działanie jest znacznie wolniejsze (niepotrzebnie wydłuŜając test). Część jego zdarzeń jest trudna do wywołania (szczególnie sytuacje dotyczące występowania błędów). Prawdziwy obiekt moŜe mieć lub stanowić interfejs uŜytkownika. NajwaŜniejsze jest to, Ŝe prawdziwy obiekt moŜe jeszcze nie być w ogóle stworzony lub test moŜe chcieć wywołać na nim specyficzne zapytanie, które nie jest dostępne w prawdziwym obiekcie w danej chwili [13],[10]. Korzystanie z obiektów pozornych posiada charakterystyczny wzorzec, który moŜna stosować podczas tworzenia testów. Najpierw tworzymy instancję obiektu pozornego, następnie ustawiamy ich stan (parametry i atrybuty uŜywane przez testowany obiekt), a na koniec ustawiamy ich oczekiwane zachowania (oraz ewentualne wartości zwracane), które powinny być wywołane przez testowany obiekt. Testowany kod wywołujemy podając jako parametry wcześniej przygotowane obiekty zastępcze i sprawdzamy czy wszystkie wywołania, które się do nich odnoszą są zgodne z naszymi oczekiwaniami [3]. Praktycznym ułatwieniem stosowania tej techniki moŜe być stworzenie interfejsu opisującego dany obiekt, a następnie implementacja tego interfejsu zarówno dla kodu, jak i w celu stworzenia obiektu zastępczego uŜywanego podczas testu. Jednym z typowych przykładów zastosowania techniki obiektów pozornych, moŜe być testowanie logiki biznesowej, odwołującej się do zdalnej bazy danych (rys. 3). Sterownik bazy danych implementuje najczęściej standardowy interfejs dostępu do bazy danych. Ten sam interfejs moŜna uŜyć do stworzenia obiektu pozornego, dzięki czemu nie musimy mieć dostępu do prawdziwej bazy danych. Strona 30 z 63
  • 31. TESTER.PL Ze względu na sposób tworzenia (uŜytą technikę programowania) obiektów pozornych moŜna podzielić na dwa rodzaje: obiektów pozornych statycznych oraz obiektów pozornych dynamicznych. Obiekty pozorne statyczne moŜemy tworzyć na dwa róŜne sposoby, poprzez zwykłą ręczną implementację dodatkowej klasy lub moŜna je wygenerować automatycznie (na etapie kompilacji bądź uruchomienia kodu Rys. 3 Przykład zastosowania obiektu pozornego testu). Obiekty pozorne dynamiczne równieŜ moŜna tworzyć na dwa sposoby: za pomocą interfejsu zwanego proxy lub za pomocą bardziej zaawansowanych technik programistycznych. Obiekty pozorne statyczne moŜemy uŜyć w przypadku pojedynczych testów, bądź niewielkich i niezbyt skomplikowanych projektów, natomiast w przypadku duŜych projektów (duŜa liczba klas, które musielibyśmy napisać w przypadku pozorantów statycznych), gdzie najczęściej podmieniane obiekty mają rozbudowane funkcjonalności bardziej odpowiednie staje się uŜycie obiektów pozornych dynamicznych [12]. W odróŜnieniu od technik opisanych powyŜej obiekty pozorne są najczęściej automatycznie generowanymi obiektami zastępczymi z zaprogramowanymi wcześniej oczekiwaniami wywołań z obiektu testowanego, które są jednocześnie specyfikacją zachowań tego obiektu. Obiekty te mogą zapisywać wykonane wywołania z obiektu testowanego w celu porównania tej informacji z oczekiwaniami wynikającymi ze scenariusza testu. Z wszystkich wymienionych technik tylko obiekty zastępcze weryfikują zachowanie testowanego obiektu, a pozostałe weryfikują jedynie stany, w których znajduje się testowany obiekt [5]. Obiekty pozorne są najczęściej generowani dynamicznie w trakcie uruchamiania testów, a więc nie ma potrzeby tworzenia dodatkowych klas z kodem, a zarazem utrzymywania i aktualizacji ich kodu, co znacznie odróŜnia je od pozostałych technik. Całość logiki obiektów zastępczych, a więc zbiór oczekiwanych zachowań jest trzymany razem z kodem testów [2]. Wszystkie przedstawione powyŜej techniki mają teŜ wspólne cechy, takie jak: moŜliwość eliminacji wielu zaleŜności w kodzie, inaczej mówiąc moŜliwość testowania wybranych fragmentów kodu w izolacji, czyli bez środowiska, w którym ten kod pracuje, moŜliwość testowania scenariuszy hipotetycznych lub rzadko występujących w prawdziwym działaniu kodu, moŜliwość znacznego przyspieszenia samego procesu testowania, pozwalając tym samym na częste uruchamianie testów jednostkowych, a jednocześnie na realizację idei testów jednostkowych. PoniŜej przedstawiamy róŜne podejścia do obiektów pozornych. Na początek obiekt pozorny napisany od początku do końca przez nas. Strona 31 z 63
  • 32. TESTER.PL public class MyMockBookManager implements BookManager { private TreeMap<Book, Integer> bookList; private boolean connect; private boolean disconnect; private int expectedOrderCount; private int orderCount; private int expectedConnectCount; private int connectCount; private int expectedDisconnectCount; private int disconnectCount; public MyMockBookManager() { bookList = new TreeMap<Book, Integer>(); connect = false; disconnect = false; expectedOrderCount = 0; expectedConnectCount = 0; expectedDisconnectCount = 0; orderCount = 0; connectCount = 0; disconnectCount = 0; } public boolean connect() { connectCount++; return connect; } public boolean disconnect() { disconnectCount++; return disconnect; } public TreeMap<Book, Integer> bookList() { return bookList; } public boolean order(BookOrder bookOrder) throws BookNotFoundException, NotEnoughBooksException { orderCount++; for (Book book: bookOrder.getBookList().keySet()) { if (!bookList.containsKey(book)) { throw new BookNotFoundException(); } if (bookList.get(book) < bookOrder.getBookList().get(book)) { throw new NotEnoughBooksException(); Strona 32 z 63
  • 33. TESTER.PL } } return true; } public void setConnect(boolean connect) { this.connect = connect; } public void setDisconnect(boolean disconnect) { this.disconnect = disconnect; } public void setBookList(TreeMap<Book, Integer> bookList) { this.bookList = bookList; } public void setExpectedConnectCount(int expectedConnectCount) { this.expectedConnectCount = expectedConnectCount; } public void setExpectedDisconnectCount(int expectedDisconnectCount) { this.expectedDisconnectCount = expectedDisconnectCount; } public void setExpectedOrderCount(int expectedOrderCount) { this.expectedOrderCount = expectedOrderCount; } public void verify() { if (expectedOrderCount != orderCount) { Assert.fail("Wrong number of performed orders"); } if (expectedConnectCount != connectCount) { Assert.fail("Wrong number of performed connects"); } if (expectedDisconnectCount != disconnectCount) { Assert.fail("Wrong number of performed disconnects"); } } } public class BookClientTestMyMockBookManager extends TestCase { private TreeMap<Book, Integer> bookList; private static final Book QUO_VADIS = new Book(1001, "Henryk Sienkiewicz", "Quo Vadis"); private static final Book PAN_TADEUSZ = new Book(2001, "Adam Mickiewicz", "Pan Tadeusz"); private static final Book CHLOPI = new Book(3001, "Wladyslaw Reymont", "Chlopi"); @Override Strona 33 z 63
  • 34. TESTER.PL protected void setUp() throws Exception { super.setUp(); bookList = new TreeMap<Book, Integer>(); bookList.put(QUO_VADIS, 2); bookList.put(PAN_TADEUSZ, 3); bookList.put(CHLOPI, 5); } public void testOrderOk() { BookClient client; MyMockBookManager mock; BookOrder bookOrder; String message; mock = new MyMockBookManager(); mock.setBookList(bookList); mock.setConnect(true); mock.setDisconnect(true); mock.setExpectedConnectCount(1); mock.setExpectedDisconnectCount(1); mock.setExpectedOrderCount(1); client = new BookClient(mock); bookOrder = new BookOrder(); bookOrder.addBook(QUO_VADIS); bookOrder.addBook(PAN_TADEUSZ); client.orderBooks(bookOrder); message = BookClient.ORDERED_BOOKS + BookClient.bookListToString(bookOrder.getBookList()); assertEquals(message, client.getMessage()); mock.verify(); } public void testOrderFail() { BookClient client; MyMockBookManager mock; BookOrder bookOrder; String message; mock = new MyMockBookManager(); mock.setBookList(bookList); mock.setConnect(true); mock.setDisconnect(true); mock.setExpectedConnectCount(1); mock.setExpectedDisconnectCount(1); mock.setExpectedOrderCount(1); client = new BookClient(mock); bookOrder = new BookOrder(); bookOrder.addBook(QUO_VADIS); Strona 34 z 63
  • 35. TESTER.PL bookOrder.addBook(PAN_TADEUSZ); bookOrder.addBook(PAN_TADEUSZ); bookOrder.addBook(PAN_TADEUSZ); bookOrder.addBook(PAN_TADEUSZ); client.orderBooks(bookOrder); message = BookClient.NOT_ENOUGH_BOOKS_ERROR; assertEquals(message, client.getMessage()); mock.verify(); } } Jak łatwo zauwaŜyć klasa MyMockBookManager jest dość obszerna. Wyobraźmy sobie ile musielibyśmy napisać takich klas w przypadku duŜego systemu. Na szczęście z pomocą przychodzi nam MockMaker. Jest to biblioteka słuŜąca do generacji obiektów pozornych z podanego interfejsu. Wygenerowany obiekt pozorny jest bardzo przyjazny dla uŜytkownika, a testy z jego uŜyciem pisze się bardzo sprawnie. Co więcej moŜe on być włączony jako wtyczka do eclipse’a. Więcej szczegółów moŜemy znaleźć na stronie domowej projektu http://mockmaker.sourceforge.net/ oraz w [15]. public class MockBookManager implements BookManager{ private ExpectationCounter myConnectCalls = new ExpectationCounter("example.book.BookManager ConnectCalls"); private ReturnValues myActualConnectReturnValues = new ReturnValues(false); private ExpectationCounter myDisconnectCalls = new ExpectationCounter("example.book.BookManager DisconnectCalls"); private ReturnValues myActualDisconnectReturnValues = new ReturnValues(false); private ExpectationCounter myBookListCalls = new ExpectationCounter("example.book.BookManager BookListCalls"); private ReturnValues myActualBookListReturnValues = new ReturnValues(false); private ExpectationCounter myOrderCalls = new ExpectationCounter("example.book.BookManager OrderCalls"); private ReturnValues myActualOrderReturnValues = new ReturnValues(false); private ExpectationList myOrderParameter0Values = new ExpectationList("example.book.BookManager example.book.BookOrder"); public void setExpectedConnectCalls(int calls){ myConnectCalls.setExpected(calls); } public boolean connect(){ myConnectCalls.inc(); Object nextReturnValue = myActualConnectReturnValues.getNext(); if (nextReturnValue instanceof ExceptionalReturnValue && ((ExceptionalReturnValue)nextReturnValue).getException() instanceof RuntimeException) throw (RuntimeException)((ExceptionalReturnValue)nextReturnValue).getException(); return ((Boolean) nextReturnValue).booleanValue(); Strona 35 z 63
  • 36. TESTER.PL } public void setupExceptionConnect(Throwable arg){ myActualConnectReturnValues.add(new ExceptionalReturnValue(arg)); } public void setupConnect(boolean arg){ myActualConnectReturnValues.add(new Boolean(arg)); } public void setExpectedDisconnectCalls(int calls){ myDisconnectCalls.setExpected(calls); } public boolean disconnect(){ myDisconnectCalls.inc(); Object nextReturnValue = myActualDisconnectReturnValues.getNext(); if (nextReturnValue instanceof ExceptionalReturnValue && ((ExceptionalReturnValue)nextReturnValue).getException() instanceof RuntimeException) throw (RuntimeException)((ExceptionalReturnValue)nextReturnValue).getException(); return ((Boolean) nextReturnValue).booleanValue(); } public void setupExceptionDisconnect(Throwable arg){ myActualDisconnectReturnValues.add(new ExceptionalReturnValue(arg)); } public void setupDisconnect(boolean arg){ myActualDisconnectReturnValues.add(new Boolean(arg)); } public void setExpectedBookListCalls(int calls){ myBookListCalls.setExpected(calls); } public TreeMap<Book,Integer> bookList(){ myBookListCalls.inc(); Object nextReturnValue = myActualBookListReturnValues.getNext(); if (nextReturnValue instanceof ExceptionalReturnValue && ((ExceptionalReturnValue)nextReturnValue).getException() instanceof RuntimeException) throw (RuntimeException)((ExceptionalReturnValue)nextReturnValue).getException(); return (TreeMap<Book,Integer>) nextReturnValue; } public void setupExceptionBookList(Throwable arg){ myActualBookListReturnValues.add(new ExceptionalReturnValue(arg)); } public void setupBookList(TreeMap<Book,Integer> arg){ myActualBookListReturnValues.add(arg); } public void setExpectedOrderCalls(int calls){ myOrderCalls.setExpected(calls); } public void addExpectedOrderValues(BookOrder arg0){ Strona 36 z 63
  • 37. TESTER.PL myOrderParameter0Values.addExpected(arg0); } public boolean order(BookOrder arg0) throws BookNotFoundException, NotEnoughBooksException{ myOrderCalls.inc(); myOrderParameter0Values.addActual(arg0); Object nextReturnValue = myActualOrderReturnValues.getNext(); if (nextReturnValue instanceof ExceptionalReturnValue && ((ExceptionalReturnValue)nextReturnValue).getException() instanceof BookNotFoundException) throw (BookNotFoundException)((ExceptionalReturnValue)nextReturnValue).getException( ); if (nextReturnValue instanceof ExceptionalReturnValue && ((ExceptionalReturnValue)nextReturnValue).getException() instanceof NotEnoughBooksException) throw (NotEnoughBooksException)((ExceptionalReturnValue)nextReturnValue).getExceptio n(); if (nextReturnValue instanceof ExceptionalReturnValue && ((ExceptionalReturnValue)nextReturnValue).getException() instanceof RuntimeException) throw (RuntimeException)((ExceptionalReturnValue)nextReturnValue).getException(); return ((Boolean) nextReturnValue).booleanValue(); } public void setupExceptionOrder(Throwable arg){ myActualOrderReturnValues.add(new ExceptionalReturnValue(arg)); } public void setupOrder(boolean arg){ myActualOrderReturnValues.add(new Boolean(arg)); } public void verify(){ myConnectCalls.verify(); myDisconnectCalls.verify(); myBookListCalls.verify(); myOrderCalls.verify(); myOrderParameter0Values.verify(); } } public class BookClientTestMockBookManager extends TestCase { private TreeMap<Book, Integer> bookList; private static final Book QUO_VADIS = new Book(1001, "Henryk Sienkiewicz", "Quo Vadis"); private static final Book PAN_TADEUSZ = new Book(2001, "Adam Mickiewicz", "Pan Tadeusz"); private static final Book CHLOPI = new Book(3001, "Wladyslaw Reymont", "Chlopi"); private BookClient client; Strona 37 z 63
  • 38. TESTER.PL private MockBookManager mock; @Override protected void setUp() throws Exception { super.setUp(); bookList = new TreeMap<Book, Integer>(); bookList.put(QUO_VADIS, 2); bookList.put(PAN_TADEUSZ, 3); mock = new MockBookManager(); client = new BookClient(mock); } public void testSecondConnectFail() { String message; BookOrder bookOrder; mock.setExpectedConnectCalls(2); mock.setExpectedDisconnectCalls(1); mock.setExpectedBookListCalls(1); mock.setExpectedOrderCalls(0); mock.setupConnect(true); mock.setupConnect(false); mock.setupDisconnect(true); mock.setupBookList(bookList); client.getBookList(); message = BookClient.AVAILABLE_BOOKS + BookClient.bookListToString(bookList); assertEquals(message, client.getMessage()); bookOrder = new BookOrder(); client.orderBooks(bookOrder); message = BookClient.CONNECT_ERROR; assertEquals(message, client.getMessage()); mock.verify(); } public void testSecondOrderFailWithBookNotFoundException() { String message; BookOrder first; BookOrder second; first = new BookOrder(); first.addBook(QUO_VADIS); first.addBook(QUO_VADIS); second = new BookOrder(); second.addBook(CHLOPI); mock.setExpectedConnectCalls(2); mock.setExpectedDisconnectCalls(2); mock.setExpectedOrderCalls(2); Strona 38 z 63
  • 39. TESTER.PL mock.setupConnect(true); mock.setupConnect(true); mock.setupDisconnect(true); mock.setupDisconnect(true); mock.setupBookList(bookList); mock.addExpectedOrderValues(first); mock.addExpectedOrderValues(second); mock.setupOrder(true); mock.setupExceptionOrder(new BookNotFoundException()); client.orderBooks(first); message = BookClient.ORDERED_BOOKS + BookClient.bookListToString(first.getBookList()); assertEquals(message, client.getMessage()); client.orderBooks(second); message = BookClient.BOOK_NOT_FOUND_ERROR; assertEquals(message, client.getMessage()); mock.verify(); } } Inne podejście do obiektów pozornych prezentują biblioteki takie jak EasyMock i jMock. Tworzą one pewnego rodzaju proxy na podstawie przedstawionego interfejsu. Mechanizm działania takiego testu wygląda w następujący sposób. Najpierw nagrywamy zachowanie obiektu pozornego: • jakie metody powinny być uruchomione i ile razy • jakie wartości powinny być zwrócone lub rzucone wyjątki Następnie uruchamiamy test i weryfikujemy zachowanie obiektu testowanego. Na poniŜszych wydrukach kodu, te przedstawione metody są najbardziej ekonomiczne i przyjazne w uŜyciu przez uŜytkownika. public class BookClientTestEasyMock extends TestCase { private TreeMap<Book, Integer> bookList; private static final Book QUO_VADIS = new Book(1001, "Henryk Sienkiewicz", "Quo Vadis"); private static final Book PAN_TADEUSZ = new Book(2001, "Adam Mickiewicz", "Pan Tadeusz"); private static final Book CHLOPI = new Book(3001, "Wladyslaw Reymont", "Chlopi"); Strona 39 z 63
  • 40. TESTER.PL private BookClient client; private BookManager mock; @Override protected void setUp() throws Exception { super.setUp(); bookList = new TreeMap<Book, Integer>(); bookList.put(QUO_VADIS, 2); bookList.put(PAN_TADEUSZ, 3); bookList.put(CHLOPI, 5); mock = EasyMock.createMock(BookManager.class); client = new BookClient(mock); } public void testBookList() { String message; EasyMock.expect(mock.connect()).andReturn(true); EasyMock.expect(mock.bookList()).andReturn(bookList); EasyMock.expect(mock.disconnect()).andReturn(true); EasyMock.replay(mock); client.getBookList(); message = BookClient.AVAILABLE_BOOKS + BookClient.bookListToString(bookList); assertEquals(message, client.getMessage()); EasyMock.verify(mock); } public void testNotEnoughBooks() { String message; BookOrder bookOrder; bookOrder = new BookOrder(); bookOrder.addBook(QUO_VADIS); EasyMock.expect(mock.connect()).andReturn(true); EasyMock.expectLastCall().times(3); try { EasyMock.expect(mock.order(EasyMock.eq(bookOrder))). andReturn(true). andReturn(true). andThrow(new NotEnoughBooksException()); } catch (BookNotFoundException e) { } catch (NotEnoughBooksException e) { } EasyMock.expect(mock.disconnect()).andReturn(true).times(3); EasyMock.replay(mock); client.orderBooks(bookOrder); Strona 40 z 63
  • 41. TESTER.PL message = BookClient.ORDERED_BOOKS + BookClient.bookListToString(bookOrder.getBookList()); assertEquals(message, client.getMessage()); client.orderBooks(bookOrder); message = BookClient.ORDERED_BOOKS + BookClient.bookListToString(bookOrder.getBookList()); assertEquals(message, client.getMessage()); client.orderBooks(bookOrder); message = BookClient.NOT_ENOUGH_BOOKS_ERROR; assertEquals(message, client.getMessage()); EasyMock.verify(mock); } } public class BookClientTestJMock extends TestCase { private TreeMap<Book, Integer> bookList; private static final Book QUO_VADIS = new Book(1001, "Henryk Sienkiewicz", "Quo Vadis"); private static final Book PAN_TADEUSZ = new Book(2001, "Adam Mickiewicz", "Pan Tadeusz"); private static final Book CHLOPI = new Book(3001, "Wladyslaw Reymont", "Chlopi"); private BookClient client; private BookManager mock; private Mockery context; @Override protected void setUp() throws Exception { super.setUp(); bookList = new TreeMap<Book, Integer>(); bookList.put(QUO_VADIS, 2); bookList.put(PAN_TADEUSZ, 3); context = new Mockery(); mock = context.mock(BookManager.class); client = new BookClient(mock); } public void testDisconnectFail() { String message; context.checking(new Expectations() {{ one (mock).connect(); will(returnValue(true)); one (mock).connect(); will(returnValue(true)); Strona 41 z 63
  • 42. TESTER.PL one (mock).bookList(); will(returnValue(bookList)); one (mock).bookList(); will(returnValue(bookList)); one (mock).disconnect(); will(returnValue(true)); one (mock).disconnect(); will(returnValue(false)); }}); client.getBookList(); message = BookClient.AVAILABLE_BOOKS + BookClient.bookListToString(bookList); assertEquals(message, client.getMessage()); client.getBookList(); message = BookClient.DISCONNECT_ERROR; assertEquals(message, client.getMessage()); context.assertIsSatisfied(); } public void testForException() { String message; final BookOrder bookOrder; bookOrder = new BookOrder(); bookOrder.addBook(CHLOPI); try { context.checking(new Expectations() {{ one (mock).connect(); will(returnValue(true)); one (mock).order(bookOrder); will(throwException(new BookNotFoundException())); one (mock).disconnect(); will(returnValue(true)); }}); } catch (BookNotFoundException e) { } catch (NotEnoughBooksException e) { } client.orderBooks(bookOrder); message = BookClient.BOOK_NOT_FOUND_ERROR; assertEquals(message, client.getMessage()); context.assertIsSatisfied(); } } Główną zaletą uŜycia techniki obiektów zastępczych w porównaniu z innymi technikami testowania jest moŜliwość weryfikacji zachowań (zarówno prawidłowości wywołań bądź ich braku, liczby wywołań jak i wartości zwracanych) obiektu testowanego względem środowiska, w którym pracuje, a nie jedynie weryfikacji zmiany samych stanów testowanego obiektu [4]. Dzięki jej zastosowaniu staramy się lepiej zrozumieć wymagania, które mają być realizowane przez nasz kod, a tym samym mamy większą wiedzę na temat tego, co powinniśmy zrobić w najbliŜszym etapie pracy. Strona 42 z 63
  • 43. TESTER.PL Inne drugoplanowe zalety obiektów pozornych, pozwalające lepiej realizować koncepcję testów jednostkowych to moŜliwość: • testowania jeszcze mniejszych części kodu niŜ w przypadku pozostałych technik, • tworzenia testów niezaleŜnych od siebie nawzajem, • późniejszego podjęcia decyzji o pewnych fragmentach infrastruktury oraz zmniejszenie ilości kodu testowego, kosztem nieco większej jego złoŜoności. Zbyt ścisłe zrównoleglenie z kodem testowanym i potrzeba synchronizacji pomiędzy nimi jest czasem uznawane za jedna z wad, choć w rzeczywistości zapewnia egzekwowanie przestrzegania ścisłej zgodności z procesem tworzenia testów jednostkowych. Wadą moŜe być za to bardzo wąski obszar danego scenariusza testowego z uŜyciem mocków, co w przypadku nawet drobnych zmian w testowanym kodzie moŜe prowadzić do tego, Ŝe test szybciej stanie się przestarzały i będzie wymagał od nas poprawek [3]. Obiekty zastępcze pozwalają w znacznym stopniu zautomatyzować tworzenie testów jednostkowych (zwłaszcza, Ŝe istnieje wiele róŜnych narzędzi do ich tworzenia w wielu językach programowania), a tym samym zwiększyć wydajność oraz przyspieszyć proces tworzenia testów, powodując, Ŝe powstające oprogramowanie będzie znacznie lepszej jakości. W szczególności technika ta pozwoli nam na większe zaufanie, co do jakości indywidualnych części naszego oprogramowania. Pewnymi niedogodnościami mogą być narzędzia do tworzenia obiektów pozornych (szczególnie dla rzadziej uŜywanych języków programowania), poniewaŜ są stale rozwijane ze względu to, Ŝe sama idea powstała w miarę niedawno [2]. Oczywiste jest, Ŝe nie ma jedynego słusznego sposobu na wykonanie testów, a tym samym nie zawsze istnieje moŜliwość skorzystania z tej wybranej techniki, ale tych wyjątków w przypadku obiektów zastępczych jest naprawdę niewiele. Podsumowując obiekty pozorne są i będą waŜną gałęzią testów. Powinny one w niedługim czasie znacznie się rozpowszechnić, co w połączeniu z technikami zwinnymi (agile’owymi) będzie potęŜnym narzędziem w tworzeniu oprogramowania. Strona 43 z 63
  • 44. TESTER.PL Bibliografia: [1] 2006. Techniques for Successful Evolutionary/Agile Database Development, artykuł internetowy, http://www.agiledata.org/essays/tdd.html [2] Bain S. 2006. Mocks, Fakes, and Stubs. Net Objectives, Vol. 3, No. 4, pp. 2-14. [3] Brown M., Tapolcsanyi E. 2003. Mock Object Patterns. Proceedings of The 10th Conference on Pattern Languages of Programs, Sept 8-12, USA. [4] Burke E. M., Coyner B. M. 2003. Java Extreme Programming Cookbook. O'Reilly and Associates. [5] Fowler M. 2007. Mocks Aren't Stubs. Artykuł ze strony internetowej autora, http://martinfowler.com/articles/mocksArentStubs.html. [6] Freeman S., Mackinnon T., Pryce N., Walnes J. 2004. Mock Roles Not Objects. Proceedings of 19th Ann. ACM SIGPLAN Conf. Object-Oriented Programming, Systems, Languages, and Applications (OOPSLA '04), ACM Press, pp. 236–246. [7] Freeman S., Pryce N. 2006. Evolving an embedded domain-specific language in Java. Proceedings of the 21th Annual ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications, OOPSLA 2006, October 22-26, 2006, Portland, Oregon, USA, pp. 855-865. [8] 1999. IEEE Standard for Software Unit Testing: An American National Standard, ANSI/IEEE Std 1008-1987 in IEEE Standards: Software Engineering, Volume Two: Process Standards; The Institute of Electrical and Electronics Engineers, Inc. Software Engineering Technical Committee of the IEEE Computer Society. [9] Mackinnon T., Freeman S., Craig P. 2001. Endo-testing: Unit testing with mock objects. Proceedings of XP2000. Extreme Programming Examined, Addison-Wesley, Boston, MA, pp. 287-301. [10] Massol V., Husted T. 2003. JUnit in Action. Manning Publications. [11] Ryu H.-Y., Sohn B.-K., Park J.-H. 2005. Mock objects framework for TDD in the network environment. Proceedings of the Fourth Annual ACIS International Conference on Computer and Information Science (ICIS’05), pp. 430- 434. [12] Stewart S. 2004. Approaches to Mocking. An article from O'Reilly Network Site, http://www.onjava.com/pub/a/onjava/2004/02/11/mocks.html. [13] Thomas D., Hunt A. 2002. Mock Objects. IEEE Software, Vol. 19, No. 3, pp. 22-24. [14] Wirfs-Brock R.J. 2007. Driven to … Discovering Your Design Values. IEEE Software, Vol. 24, No. 1, pp. 9-11. [15] Keld H. Hansen Using Mock Objects in Java. Artykuł ze strony internetowej, http://javaboutique.internet.com/tutorials/mock_objects/ [16] EasyMock 2.2 Readme, http://www.easymock.org/EasyMock2_2_Documentation.html [17] The jMock Cookbook, http://www.jmock.org/cookbook.html Strona 44 z 63
  • 45. TESTER.PL Dokumentacja – jak to się je? Maciej Dusza Maciej Dusza jest absolwentem informatyki na wydziale Matematyki, Informatyki i Mechaniki Uniwersytetu Warszawskiego. W 1993r. obronił pracę magisterską na temat "Tworzenie programów współbieŜnych o sterowaniu zadanym strukturami przyczynowoskutkowymi". Od tego czasu pracował w kilku firmach w Polsce i USA, pełniąc funkcje programisty, analityka, projektanta i testera, a przez ostatnie trzy lata pracował w IMPAQ Sp. z o.o., na stanowisku Starszego Analityka ds. Zapewnienia Jakości. Kawaler. Jego hobby to nurkowanie. Strona 45 z 63
  • 46. TESTER.PL Dokumentacja – jak to się je? Maciej Dusza Pisanie dokumentacji często traktowane jest przez programistów jak cięŜka kara i dopust boŜy. Z nieco większym zrozumieniem odnoszą się do tego kierownicy projektów, zwłaszcza ci, którzy kiedyś w połowie projektu stracili pracownika będącego „guru od systemu/programu/modułu”, i musieli w trybie nagłym wdraŜać do pracy nową osobę, przegryzając się przez tysiące linii nieudokumentowanego, i często pozbawionego komentarzy kodu. Ja osobiście lubię pisać dokumentację. Sprawia mi przyjemność opisywanie poszczególnych elementów systemu w sposób dokładny, precyzyjny, i zrozumiały dla odbiorcy. PoniŜej przedstawiam zbiór porad i uwag, które – mam nadzieję – przydadzą się zarówno osobom piszącym „z potrzeby serca”, jak i tym, dla których jest to przykrym obowiązkiem. Moje uwagi przedstawiam w postaci krótkich akapitów, odnoszących się do poszczególnych zagadnień związanych z pisaniem dokumentacji. Opracowałem je na podstawie własnych doświadczeń, oraz lektury kilku ksiąŜek, których tytuły wymieniam na końcu. Cechy dobrej dokumentacji: • jest dokładna i bezbłędna • jest kompletna • jest spójna • jest napisana w sposób klarowny i jednoznaczny • uŜytkownik moŜe łatwo znaleźć informację, której potrzebuje • ma porządny, ładny wygląd. Etapy tworzenia dokumentacji: • zbieranie informacji na temat opisywanego systemu • tworzenie projektu dokumentacji • pisanie dokumentacji • weryfikacja dokumentacji • dokonywanie poprawek i uzupełnianie braków. Dwa ostatnie etapy są zwykle powtarzane kilkakrotnie, aŜ do usunięcia wszystkich usterek. Informacje na temat opisywanego systemu, moŜna zbierać poprzez: • czytanie juŜ istniejących fragmentów dokumentacji • pracę z programem i analizę kodu źródłowego Strona 46 z 63
  • 47. TESTER.PL • konsultacje (zazwyczaj z developerami, którzy stworzyli system). Projekt dokumentacji powinien zawierać następujące elementy: • tytuł dokumentacji • rodzaj dokumentacji (np. podręcznik uŜytkownika) • przewidywana data zakończenia prac nad dokumentacją • imię i nazwisko dokumentalisty • krótki opis tego, co dokumentacja będzie zawierać • szacunkowa liczba stron (najlepiej określić to poprzez porównanie z podobnymi, istniejącymi juŜ dokumentami) • spis głównych rozdziałów • terminy kamieni milowych. Kamień milowy przy pisaniu dokumentacji, to moment kiedy dokumentacja idzie do weryfikacji. Dobrym miejscem na ustanowienie pierwszego kamienia milowego, jest moment gdy: • większość (dwie trzecie lub trzy czwarte) tekstu jest juŜ napisane • zaznaczone są miejsca, w których wstawione zostaną rysunki i zrzuty ekranów. Dobrym miejscem na ustanowienie drugiego kamienia milowego, jest moment gdy: • praktycznie cały tekst jest juŜ napisany • prawie wszystkie rysunki i zrzuty ekranów są juŜ w dokumentacji • poprawione zostały usterki, które wyszły na jaw w czasie weryfikacji po pierwszym kamieniu milowym. Przy tworzeniu dokumentacji, zwykle występują co najmniej dwa kamienie milowe, i często nie ma ich więcej. Dlatego w projekcie dokumentacji podaje się najczęściej terminy dwóch kamieni. Weryfikacja dokumentacji oznacza: • dokładne przeczytanie dokumentacji w poszukiwaniu błędów • sprawdzenie czy zrzuty ekranu odpowiadają temu, co rzeczywiście w opisywanym momencie widać na ekranie • wykonanie procedur (instrukcji) zawartych w dokumentacji, i sprawdzenie czy rzeczywiście generują Ŝądane rezultaty • przekazanie dokumentacji recenzentom i zebranie ich uwag. PoŜyteczną metodą weryfikacji, jest równieŜ poproszenie kogoś, kto nie posiada zbyt głębokiej wiedzy na temat opisywanego systemu, o przeczytanie dokumentacji, i upewnienie się, Ŝe wszystko zrozumiał. Powód: dokumentalista moŜe zapomnieć o napisaniu czegoś, co jest dla niego "oczywiste", a recenzent, zwykle dobrze znający system, moŜe nie zauwaŜyć takiego braku. Strona 47 z 63
  • 48. TESTER.PL Dwa waŜne pytania, jakie naleŜy sobie zadać przed rozpoczęciem pracy nad dokumentacją: • dla kogo ta dokumentacja jest przeznaczona? Inaczej pisze się np. dla sprzedawcy, który będzie jedynie korzystał z systemu, a inaczej dla programisty, który będzie ten system rozwijał. Nie chodzi tu tylko o treść, chodzi równieŜ o styl pisania (np. uŜywanie słownictwa technicznego). • jakie przyjąć podejście: opisowe czy zadaniowe? Opisowe podejście do pisania dokumentacji polega na tym, Ŝe po prostu opisujemy kolejne elementy systemu, troszcząc się jedynie o to, Ŝeby zrobić to dobrze. Podejście zadaniowe polega na tym, Ŝe przy pisaniu bierzemy pod uwagę zadania, które wykonuje odbiorca dokumentacji. Przykładowo: załóŜmy, Ŝe opisujemy interfejs uŜytkownika, a odbiorcą dokumentacji ma być osoba zajmująca się wystawianiem faktur. Jeśli przyjmiemy podejście zadaniowe, to opcje systemu opiszemy nie w takiej kolejności, w jakiej są ułoŜone w menu, tylko w takiej, w jakiej korzysta się z nich przy wystawianiu faktur. MoŜe się zdarzyć, Ŝe z dokumentacji mają korzystać dwie róŜne grupy uŜytkowników. Na przykład sprzedajemy system wspomagający sprzedaŜ firmie, która posiada dział sprzedaŜy i dział informatyki. Sprzedawcy będą uŜywać tego systemu korzystając z interfejsu uŜytkownika, a informatycy otrzymają kod źródłowy, i będą go na własną rękę rozwijać. W takiej sytuacji, najlepszym rozwiązaniem jest napisanie dwóch oddzielnych dokumentacji: jednej dla sprzedawców, i jednej dla informatyków. Jest to oczywiście bardziej praco- i czasochłonne, ale znacznie wygodniejsze z punktu widzenia odbiorców dokumentacji. Jeśli jednak nie chcemy pisać dwóch oddzielnych dokumentacji (bo np. nie ma na to czasu), i decydujemy się włoŜyć wszystkie informacje do jednej dokumentacji, wówczas naleŜy wyraźnie zaznaczyć, które sekcje dokumentacji są przeznaczone dla której grupy odbiorców. Pisanie dokumentacji jednym ciągiem, od początku do końca, nie jest dobrym pomysłem. Po pierwsze, nieraz będą się zdarzać sytuacje, kiedy napisanie kolejnego fragmentu nie jest moŜliwe (bo np. developer, z którym trzeba się skonsultować, nie jest chwilowo dostępny). Po drugie, po zakończeniu moŜe się okazać, Ŝe dokumentacja wprawdzie zawiera wszystko co trzeba, ale jej układ powinien być inny. Dlatego lepsze wyniki przynosi następujące podejście: • określamy jakie rozdziały będzie mieć dokumentacja • dla kaŜdego rozdziału określamy jakie będzie mieć podrozdziały • dla kaŜdego podrozdziału określamy jakie będzie mieć pod-podrozdziały • i tak dalej... • kiedy szczegółowa struktura rozdziałów jest juŜ gotowa, zaczynamy wypełniać treścią kolejne pod-...-rozdziały. Jeśli w jakimś momencie napotkamy problem, z którym nie Strona 48 z 63
  • 49. TESTER.PL moŜemy sobie od razu poradzić, wtedy zaznaczamy Ŝe to miejsce wymaga uzupełnienia, i po prostu zabieramy się za wypełnianie treścią kolejnego elementu struktury. Dodatkową zaletą takiego podejścia jest to, Ŝe na pierwszy rzut oka widoczny jest stan prac nad dokumentacją - co konkretnie jest juŜ gotowe, co wymaga uzupełnienia, a co nie jest jeszcze rozpoczęte. Na początku dokumentacji powinny się znaleźć: • copyright (najlepiej zwrócić się do działu prawnego firmy o opracowanie dokładnego sformułowania) • spis treści • opcjonalnie: spis ilustracji (dla uŜytkownika moŜe być wygodne, jeśli będzie mógł znaleźć konkretny diagram, bez zastanawiania się w jakim powinien być rozdziale) • wstęp. Wstęp powinien informować: • jaki produkt opisuje dokumentacja • dla kogo dokumentacja jest przeznaczona • co dokumentacja zawiera (bardzo ogólnie) • jakie konwencje są stosowane w dokumentacji (np. jaka czcionka jest stosowana dla zamieszczonych w dokumentacji fragmentów kodu źródłowego). Na końcu dokumentacji powinny się znaleźć: • załączniki • słowniczek terminów specyficznych dla opisywanego systemu, oraz terminów i skrótów które są uŜywane w dokumentacji, a których uŜytkownik moŜe nie znać • indeks. Załączniki powinny zawierać informacje, które uŜytkownik moŜe chcieć uzyskać, ale które nie są niezbędne do zrozumienia dokumentacji (np. definicje pól w bazie danych). Uwaga: nie naleŜy traktować załączników jako miejsce, do którego dokumentalista moŜe wrzucić wszystkie informacje, co do których nie ma pewności gdzie je umieścić. Indeks jest bardzo waŜnym elementem dokumentacji. To w duŜym stopniu od niego zaleŜy, na ile dokumentacja będzie uŜyteczna dla uŜytkownika. Kiedy prace nad dokumentacją dojdą do kamienia milowego, przy którym wskazane jest przeprowadzenie weryfikacji tego, co zostało dotychczas stworzone, dokumentalista powinien najpierw sam sprawdzić swoją pracę, a następnie poprosić kilka osób o recenzję. Naturalnymi kandydatami na recenzentów są developerzy pracujący nad opisywanym systemem, i szef Strona 49 z 63
  • 50. TESTER.PL dokumentalisty. Warto zwrócić się o to równieŜ do ludzi, którzy mają codzienny kontakt z klientem - odbiorcą dokumentacji (czyli do pracowników działu sprzedaŜy, działu marketingu, i działu pomocy technicznej). UwaŜne przeczytanie dokumentacji wymaga czasu. Dlatego recenzentów naleŜy z wyprzedzeniem zawiadomić o tym, Ŝe danego dnia przyśle im się dokumentację do zrecenzowania. Istnieją trzy metody zbierania opinii od recenzentów. Pierwsza, najbardziej efektywna, polega na tym, Ŝe dokumentalista i wszyscy recenzenci spotykają się razem, siadają przy stole, przechodzą po kolei przez wszystkie rozdziały i akapity dokumentacji, kaŜdy moŜe zgłaszać na głos swoje uwagi, a pozostali od razu ustosunkowują się do tych uwag. Dzięki temu, kaŜda sugestia, jaką ktoś moŜe mieć, jest rozpatrywana przez cały zespół recenzentów. Uwaga: na takim spotkaniu nieraz zdarza się, Ŝe rozmowa zaczyna odbiegać od tematu (np. developer zaczyna mówić o pracach nad modułem, który nie wchodzi w zakres systemu objęty dokumentacją). Im więcej ludzi bierze udział w spotkaniu, tym częściej zdarzają się takie mimowolne odejścia od tematu. Dlatego, jeśli recenzentów jest więcej niŜ kilku, warto poprosić kogoś, kto sam nie bierze udziału w recenzowaniu, o pełnienie funkcji moderatora. Moderator jest obecny na zebraniu, ale nie zabiera głosu w dyskusji. Jedyne, czym się zajmuje, to słuchanie tego, co mówią inni, i przerywanie ludziom, którzy zaczynają odchodzić od tematu. Druga metoda zbierania opinii, polega na rozesłaniu dokumentacji do wszystkich recenzentów, odebraniu opinii od kaŜdego oddzielnie, i skonsolidowaniu otrzymanych uwag. Szczególną uwagę dokumentalista musi tu zwrócić na sprzeczne sugestie, które mogą nadejść od róŜnych recenzentów. W takim przypadku naleŜy zawiadomić autorów tych sugestii o zaistniałej sytuacji, i poprosić o przemyślenie stanowiska drugiej osoby. Jeśli Ŝaden z recenzentów nie chce ustąpić, a dokumentalista nie ma własnego zdania w tej kwestii, właściwe wydaje się uwzględnienie propozycji recenzenta, zajmującego wyŜsze stanowisko w hierarchii firmy. Jeśli jednak dokumentalista skłania się ku rozwiązaniu proponowanemu przez niŜej stojącego recenzenta, powinien skonsultować się ze swoim szefem. Trzecia metoda zbierania opinii, polega na wysłaniu dokumentacji do jednego z recenzentów, następnie, razem z naniesionymi przez niego uwagami, do drugiego, potem z uwagami naniesionymi przez tych dwóch do trzeciego, i tak dalej. Dzięki temu kaŜdy (prócz pierwszego) recenzent, moŜe odnieść się nie tylko do samej dokumentacji, ale równieŜ do uwag poprzednich recenzentów. Metoda ta zabiera oczywiście więcej czasu, niŜ dwie pozostałe metody. Strona 50 z 63
  • 51. TESTER.PL Jeśli przekazuje się do recenzji dokument, który był juŜ kiedyś recenzowany, naleŜy zaznaczyć co zostało dodane lub zmienione od czasu poprzedniej recenzji, Ŝeby recenzenci nie musieli ponownie czytać wszystkiego od A do Z. Kiedy dokumentacja zostaje oddana do produkcji, następuje jej "zamroŜenie". Oznacza to, Ŝe Ŝadne zmiany nie będą juŜ wprowadzane. Jeśli przed przekazaniem dokumentacji odbiorcy, okaŜe się Ŝe coś wymaga zmiany, do dokumentacji zostanie dołączona errata (np. plik README na CD z dokumentacją), a potrzebne zmiany zostaną wprowadzone dopiero w następnej wersji dokumentacji. WaŜną rzeczą jest bardzo dokładne zebranie uwag od odbiorców dokumentacji, po dostarczeniu im pierwszej gotowej części. Na podstawie takich uwag moŜna nie tylko stworzyć wskazówki przydatne w dalszych pracach nad dokumentacją, ale moŜe to równieŜ prowadzić do zmiany przyjętych standardów (najprostszy przykład: odbiorca prosi o pisanie dokumentacji przy uŜyciu większej czcionki, bo ma słaby wzrok). Opinie odbiorców naleŜy zbierać po przekazaniu im kaŜdej części dokumentacji, jednak szczególnie waŜne jest to na początku, z powodów wymienionych wyŜej. Przed rozpoczęciem pisania dokumentacji, naleŜy ustalić symbol / ciąg znaków, który będzie stosowany w miejscach wymagających uzupełnienia. Takim symbolem moŜe być np. ciąg liter TBD (od ang. to be determined). Jeśli osoba pisząca dokumentację, nie będzie pewna co napisać w jakimś miejscu (bo np. wymaga to konsultacji z developerem), to moŜe kontynuować pisanie pod warunkiem wstawienia w to miejsce takiego symbolu. Obowiązuje zakaz oznaczania miejsc wymagających uzupełnienia w jakikolwiek inny sposób (np. pisząc: do uzupełnienia). Powód jest oczywisty: chodzi o to, Ŝeby w kaŜdej chwili moŜna było jednym find'em znaleźć wszystkie niedokończone miejsca. Jeśli wskazane jest umieszczenie w dokumentacji zrzutu jakiegoś ekranu, to ekran ten powinien zawierać konkretne dane, a nie tylko puste pola. Przykładowo, jeśli na ekranie jest okno słuŜące do wprowadzania informacji personalnych, to naleŜy do odpowiednich pól wprowadzić zmyślone imię, nazwisko, adres, telefon itd. Uwaga: niedopuszczalne jest wprowadzanie danych Ŝyjących osób (np. przepisywanie z ksiąŜki telefonicznej), moŜe to grozić procesem sądowym. Kiedy pisze się dokumentację, precyzja i klarowność sformułowań są waŜniejsze od elegancji. Jeśli np. w jednym zdaniu pojawi się dziesięć razy słowo system, to nie ma w tym nic niewłaściwego. Nie naleŜy pisać na zmianę system i np. program, bo moŜe to jedynie wprawić w zakłopotanie odbiorcę dokumentacji, który zacznie się zastanawiać czy na pewno chodzi o to samo. Na tej samej zasadzie, naleŜy trzymać się raz przyjętych terminów na określenie pewnych Strona 51 z 63
  • 52. TESTER.PL rzeczy czy czynności. Jeśli np. w pierwszym rozdziale piszemy o klikaniu myszą, to juŜ do końca powinniśmy konsekwentnie uŜywać tego określenia, a nie zastępować go w innym rozdziale słowem tupanie myszą. Czasem moŜe się zdarzyć, Ŝe trzeba wybierać między ścisłym trzymaniem się zasad pisowni, a klarownością przekazu. W takim przypadku lepiej złamać zasady, Ŝeby w zamian za to uzyskać jednoznaczny i zrozumiały przekaz. Przykładowo, weźmy następujące zdanie: „systemy: A, B, C i D”. Czytelnik moŜe mieć wątpliwości, czy chodzi tu o cztery systemy, czy teŜ o trzy, z których ostatni ma złoŜoną nazwę „C i D”. Jeśli dodamy przecinek po „C”, usuniemy niejednoznaczność. NaleŜy pamiętać, Ŝe dokumentacja jest przeznaczona dla człowieka, a nie dla komputera, i dlatego mogą się zdarzać sytuacje, w których uzasadnione jest odstąpienie od przyjętych standardów. Przykładowo, jeśli chcemy umieścić w dokumentacji diagram przejścia stanów, ale widzimy Ŝe taki diagram byłby bardzo skomplikowany, czytelnik niewiele by z niego rozumiał, i tylko co chwila by błądził nawigując po plątaninie strzałek, to lepiej w ogóle zrezygnować z diagramu, a poszczególne stany i warunki przejścia między nimi, opisać słownie. Jeśli dokumentalista przejmuje dokumentację napisaną przez kogoś innego, to zanim zacznie dopisywać nowe rozdziały, powinien: • przeczytać to, co juŜ jest napisane • jeśli jest to moŜliwe, porozmawiać z poprzednim autorem dokumentacji, i dowiedzieć się czego nie zdąŜył napisać, co zamierzał zmienić, itp. • usiąść nad dokumentacją razem z developerami, upewnić się Ŝe w dokumentacji nie ma błędów, oraz dowiedzieć się co nowego pojawiło się w systemie • samemu obejrzeć opisywany system, i stwierdzić na ile pasuje do dokumentacji • dowiedzieć się, czy odbiorcy dokumentacji zgłaszali jakieś uwagi lub prosili o zmiany w dokumentacji. Kiedy dokumentalista po raz pierwszy uruchamia system, który ma opisywać, i próbuje działać jako uŜytkownik, powinien zapisywać wszystkie rzeczy, które sprawiły mu trudność. Jest bardzo prawdopodobne, Ŝe rzeczywisty uŜytkownik natknie się na takie same trudności, a co za tym idzie jest to okazja do zlokalizowania miejsc, które warto opisać w dokumentacji szczególnie dokładnie. Kiedy dokumentalista rozpoczyna opisywanie systemu, i otrzymuje dostęp do kodu źródłowego, powinien upewnić się, Ŝe jest to najnowsza wersja kodu rozwojowego, nad którym prowadzone są prace. Nie moŜe to być stara wersja kodu, a w szczególności nie moŜe to być kod okrojonej wersji demonstracyjnej. Musi to być aktualny kod, nawet jeśli codziennie miałby się zmieniać. Strona 52 z 63