V5 OOT Vezbe
V5 OOT Vezbe
Teorija
Finale State Machine (FSM) - Mašina sa konačnim brojem stanja ili Graf stanja i prelaza
U testiranju softvera:
FSM se prikazuje kao graf čiji čvorovi reprezentuju stanja koja predstavljaju stanja izvršavanja
softvera, a veze predstavljaju prelaze između stanja. Stanje predstavlja prepoznatljivu situaciju
koja ostaje u toj egzistenciji tokom nekog vremenskog perioda. Stanje se definiše posebnim
vrednostima za skup promenljivih, i dok promenljive imaju te vrednosti, smatra se da je softver u
tom stanju.
Sledeća slika ilustruje model sa jednostavnim prelazom kojim se otvaraju vrata lifta. Ako je
dugme u liftu pritisnuto (događaj koji se hvata), vrata će se otvoriti samo ako se lift ne pomera
(preduslov: elevSpeed=0).
Zadatak 1
/** *****************************************************
// Stutter checks for repeat words in a text file.
// It prints a list of repeat words, by line number.
// Stutter will accept standard input or a list
// of file names.
// Jeff Offutt, June 1989 (in C), Java version March 2003
//********************************************************* */
class Stutter
{
// Class variables used in multiple methods.
private static boolean lastdelimit = true;
private static String curWord = "", prevWord = "";
private static char delimits [] =
{’’, ’ ’, ’,’, ’.’, ’!’, ’-’, ’+’, ’=’, ’;’, ’:’, ’?’,
’&’, ’{’, ’}’, ’\\’}; // First char in list is a tab
//************************************************
// main parses the arguments, decides if stdin
// or a file name, and calls Stut().
//************************************************
public static void main (String[] args) throws IOException
{
String fileName;
FileReader myFile;
BufferedReader inFile = null;
if (args.length == 0)
{ // no file, use stdin
inFile = new BufferedReader (new InputStreamReader (System.in));
}
else
{
fileName = args [0];
if (fileName == null)
{ // no file name, use stdin
inFile = new BufferedReader (new InputStreamReader (System.in));
}
else
{ // file name, open the file.
myFile = new FileReader (fileName);
inFile = new BufferedReader (myFile);
}
}
stut (inFile);
}
//************************************************
// Stut() reads all lines in the input stream, and
// finds words. Words are defined as being surrounded
// by delimiters as defined in the delimits[] array.
// Every time an end of word is found, checkDupes()
// is called to see if it is the same as the
// previous word.
//************************************************
private static void stut (BufferedReader inFile) throws IOException
{
String inLine;
char c;
int linecnt = 1;
while ((inLine = inFile.readLine()) != null)
{ // For each line
for (int i=0; i<inLine.length(); i++)
{ // for each character
c = inLine.charAt(i);
if (isDelimit (c))
{ // Found an end of a word.
checkDupes (linecnt);
}
else
{
lastdelimit = false;
curWord = curWord + c;
}
}
linecnt++;
checkDupes (linecnt);
}
} // end Stut
//************************************************
// checkDupes() checks to see if the globally defined
// curWord is the same as prevWord and prints a message
// if they are the same.
//************************************************
private static void checkDupes (int line)
{
if (lastdelimit)
return; // already checked, keep skipping
lastdelimit = true;
if (curWord.equals(prevWord))
{
System.out.println ("Repeated word on line " + line + ": " +
prevWord+""+curWord);
}
else
{
prevWord = curWord;
}
curWord = "";
} // end checkDupes
//************************************************
// Checks to see if a character is a delimiter.
//************************************************
private static boolean isDelimit (char C)
{
for (int i = 0; i < delimits.length; i++)
if (C == delimits [i])
return (true);
return (false);
}
} // end class Stutter
1. FSM koji reprezentuje Stutter, baziran je na grafu kontrole toka za ove 3 metode (tzv. metod
kombinovanja grafova toka kontrole).
Graf prikazan na gornjoj slici nije u potpunosti FSM, i ovo nije način da se crtaju grafici iz
softvera. Ova metoda ima nekoliko problema, prvi je da čvorovi nisu stanja. Metode moraju da
vrate rezultat na odgovarajućim mestima poziva tih metoda, što znači da grafovi sadrže ugrađene
ne-determinizme. Na primer, postoje veze između čvora 2 u metodi checkDupes() ka čvoru 6 u
metodi shut() i takođe veza između čvora 2 u checkDupes() ka čvoru 8 u metodi shut(). Mesto
povratka zavisi odakle smo zvali metodu checkDupes(), iz čvora 6 ili čvora 8 u stut(). Drugi
problem može biti taj što implementacija samog softvera mora biti kompletno završena, pre nego
što počnemo da pravimo graf. Ipak, naš cilj je uvek da pripremimo testove što je ranije moguće.
Najvažniji problem je ne srazmernost grafa veličini softvera. Možemo primetiti da graf postaje
vro komplikovan sa samo ove tri metode, a sa velikim programima graf postaje mnogo gori.
2. FSM baziran na softverskoj strukturi
Prednosti:
- prikazuje opšti tok operacija u programu
Nedostaci:
- različiti testeri bi prikazivali različite grafikone, što uvodi nedoslednost u testiranju
- zahteva temeljno poznavanje softvera, ne možemo testirati dok ne znamo najmanje detalje
projekta, što je jako teško kod velikih programa
Teoretski svaka kombinacija ove 3 promenljive definiše različito stanje. U praksi međutim to
može dovesti do beskonačnog broja stanja. Na primer curWord i prevWord su stringovi i imaju
neograničen skup vrednosti. Dakle, uobičajno je da se identifikuju vrednosti ili opsezi vrednosti
koji će biti zastupljeni kao stanja. Za klasu Shutter, očigledno je da taj skup treba da bude
zasnovan na odnosu između te dve promenljive, stvarajući sledeće moguće vrednosti:
Stanje 7 je izostavljeno, jer je nemoguće doći do njega. Takođe, treba znati da se ovaj program
završava na kraju pročitanog fajla.
Mehanički proces u ovoj strategiji je privlačan zato što možemo očekivati da različiti testeri
izvedu isti ili sličan FSM. Taj proces nije uvek moguće da se potpuno automatizuje, zato što
postoji teškoća za utvrđivanje prelaza iz odredišta i zato što odluka o bilo kojoj varijabli zahteva
presudu.
FSM na osnovu koda su dosta laki za razumevanje. Ako je softver dobro dizajniran, ovaj tip
FSM treba da sadrži iste informacije koje činje UML dijagram stanja.
Teorija
Razmatramo sledeći UML klasni dijagram. V i X su izvedene klase iz klase W, klasa V preklapa
metodu m(), a klasa X prekla metode m() i n(). Minus (-) označava atribut sa privatnim pravom
pristupa, a plus (+) označava javno pravo pristupa. Deklarisani tip objekta o je W (linija koda 3),
ali u liniji 10 stvarni tip može biti V ili W.
Koja preklopljena metoda m() će se izvršiti zavisi od argumenta metode f - boolean promenljive
b. U sledećem zadatku biće ilustrovan problem preklapanja i polimorfizma.
Zadatak 2
U implementaciji klase A, pretpostavimo da metoda d() poziva metodu g(), g() poziva h(), h()
poziva i() i i() poziva j(). Dalje pretpostavimo da kod implementacije B, metoda h() poziva
metodu i(), i() poziva metodu i() iz svoje roditeljske klase (A), a k() poziva l(). Konačno, kod C
imamo metodu i() koja poziva roditeljsku metodu i() iz klase B, i metoda j() koja poziva k().
Rešenje:
Razumevanje koja metoda će se izvršiti je vrlo teško i za programere i testere, jer moraju pratiti
sve pozive i sve nivoe nasleđivanja. Izvršavanje metoda, kao što u ovom slučaju jedna poziva
drugu, druga treću,… može se stalno skakati između nivoa nasleđivanja i taj efekat se naziva yo-
yo efekat.
Yo-yo graf definiše hijerarhiju nasleđivanja, ima koren i potomke. Grafik prikazuje sve nove,
nasleđene i poništene metode za svakog od potomaka. Pozivanje metoda predstavlja se
strelicama. Strelica ide od metode iz koje se poziva ka metodi koja je pozvana. Svaka klasa ima
svoj nivou u yo-yo grafu, koji prikazuju stvarne pozive koji su napravljeni ukoliko je kreiran
objekat tipa tog nivoa. Podebljane strelice su stvarni pozivi, a svetle strelice su pozivi koji ne
mogu biti napravljeni zbog preklapanja.
Problem počinje sa pozivom A::d(). Na slici je prikazano šta se dešava kada je tip objekta A, B i
C, kada pozivamo tu metodu. U prvom slučaju, pretpostavljamo da je metoda pozvana nad
objektom tipa A i to je prikazano u prvom nivou grafikona. Ovaj redosled poziva je jednostavan i
jasan.
Drugi nivo pokazuje složeniju situaciju, kada je objekat tipa B. Metoda g() poziva metodu h(), ali
izvršava se verzija h() koja je definisana u B (svetla linija od A::g() ka A::h() pokazuje da se
A::h() NE IZVRŠAVA!). Zatim se kontrola nastavlja pozivanjem B::i(), A::i() i A::j().
Kada formiramo objekat tipa C, možemo videti odakle dolazi termin “yo-yo”. Kontrola nakon
pozivanja A::g() prelazi na B::h(), pa na C::i(), zatim se vraća preko B::i() i A::i() nazad
pozivanjem C::j(), ponovo se ide u B nivo i poziva B::k(), a na kraju C::l().
Metod može implementirati jednu ili više funkcionalnosti, tako da svaka prezentuje različite nivoe
kohezije. Čak kohezivne funkcije mogu imati složene ulazno/izlazne odnose. Potrebno je naći neku
sistemsku tehniku koja bi identifikovala funkcije i testirala svaki ulano/izlazni odnos.
Uzorak podela na kategorije (eng. category partition pattern) je pogodan za bilo koju metodu koja
sprovodi jednu ili više nezavisnih funkcija. Za metode i funkcije kojima nedostaje kohezija, sastavne
obaveze treba da budu identifikovanje i modelovanje kao zasebnih funkcija. Ovaj pristup je kvalitetan i
može se raditi ručno.
Ovaj uzorak pretpostavlja da se greške odnose na vrednosti kombinacija parametara poruke i instance
promenljive, a ove greške će dovesti do nedostatka ili netačne metode za izlaz. Istraživači Offutt i Irvine
su utvrdili da ovim pristupom mogu da se otkriju 23 uobičajne greške kodiranja u C++. Međutim, greške
koje se manifestuju samo pod određenim sekvencama, ili čije korumpirane instance promenljivih su
sakrivene u MUT (method under test) interfejsu, ne mogu biti otkrivene ovim metodom.
Ovaj primer ilustrovaćemo jednom C++ funkcijom List::getNextElement(). Ta funkcija vraća sledeći
element objekta List. Ako je tekuća pozicija u listi od m elemenata n-ti element, uzastopni pozivi
getNextElement() vratići elemente na pozicijama n+1, n+2, n+3, itd. Ako nema pozicije koja se dobija
pozivanjem prethodne funkcije ili prethodna pozicija više ne postoji zbog neke promene ili brisanja, baca
se izuzetak NoPosition. EmptyList izuzetak se baca ako je lista prazna.
Procedura testiranja:
1) Identifikujemo funkcije koje testiramo - Dobro dizajnirane metode implementiraju samo nekoliko
kohezivnih funkcija, koje se obično razlikuju u imenu i parametrima. Jedan metod može
implementirati više funkcija. Druge funkcije mogu biti bočni efekti primarne funkcije. Funkcija
može samostalno da se testira ako zadovoljava tri kriterijuma:
Ona može biti određena postavljanjem parametara na određene vrednosti
Izlaz se posmatra kao vrednost koju vraća metod
Izlaz može biti drugačiji od izlaza druge funkcije ili nekorektna funkcija
Parametar Kategorija
Pozicija poslednjeg referenciranog elementa n-ti element
specijalni slučaj
Stanje liste - element
specijalni slučaj
Teorijske osnove
Objektno orijentisano jedinično testiranje (eng. object-oriented unit test) sastoji se od sekvence
poziva metoda koje postavljaju stanje (kreiranje objekata i promene vrednosti) i provere rezultata
finalnog poziva.
Sekvenca metoda (engl. Method Sequence), ili sekvenca je niz poziva metoda. Svaki poziv sastoji
se od imena metode i niza argumenata, s.i označavaće vrednost vraćenu od i-tog metoda u
sekvenci s.
Produžavanje sekvenci (engl. Extending sequences), prihvata 0 ili više sekvenci i generiše novu
sekvencu. Ova operacija kreira novu sekvencu nadovezujući ulazne sekvence dodavanjem
jednog metoda na kraj. Možemo zapisati extend(m, seqs, vals):
Rezultat izvršavanja extend je nova sekvenca koja sadrži nadovezane sekvence u navedenom
redosledu u seqs praćene pozivom metoda m(v1,..., vk).
Primer:
Elementi sekvence:
s1.1 (b1)
s2.1 (b2)
s3.1 i s3.2 (a1 i b3)
Sekvence se grade postupno (eng. incrementally) pri čemu algoritam započinje sa praznim
skupovima. Nakon što je sekvenca zagrađena, ista se izvršava kako bi se izbeglo kreiranje
redudantnih sekvenci, što je sprecificirano filterima (eng. filters) i ugovorima (eng. contracts).
Četiri ulaza u algoritam: lista klasa za koje je potrebno kreirati sekvence, lista ugovora, lista
filtera i vreme na raspolaganju za računanje sekvenci.
randomPublicMethod()
randomSequences()
randomValues()
izbor vrednosti za argumente metode. Ukoliko je argument primitivnog tipa, vrši se izbor
vrednosti iz konstantnog niza vrednosti (na primer: -1, 0, 1, ’a’, true, itd.).
Ukoliko je argument složenog tipa, random se bira jedna od sledećih mogućnosti:
vrednost koja je izgenerisana nekim od prethodnih poziva u postojećoj sekvenci, izborom
sekvence koja se nalazi u skupu successSequnces njenim dodavanjem u skup sekvenci i
upotrebom vrednosti ili null.
Nakon što je kreirana jedinstvena sekvenca, poziva se execute metoda nad sekvencom koja izvršava
metode. Nakon svakog poziva metode vrši se provera ugovora. Ugovor je opis karakteristika koje treba da
važe na početku i na kraju svakog poziva (true/false).
Metode
Ugovor Opis
Objekti
Ugovor Opis
Null - ukoliko je rezultat izvršavanja null, njegova vrednost dalje neće biti korišćena.
Primer (nastavak):
B b3 = a1.m1(a1);
A a1 = new A();
B b3 = a1.m1(a1);
b1.m2(b1, a1);
B b3 = a1.m1(a1);
B b1 = new B(0);
b1.m2(b1, a1);
B b2 = new B(0);
b1.m2(b2, null);
Kod testiranja prvog Web sloja ispituje se kompatibilnost aplikacije u Web pregledaču (engl.
browser). Aplikacija treba da bude testirana u svim mogućim verzijama pregledača koje najčešće
koriste krajnji korisnici - Mozilla Firefox, Google Chrome, IE (više verzija, zbog različitog
ponašanja), Opera, Safari,... Kod testiranja srednjeg sloja ispituju se funkcionalnosti i sigurnosna
pitanja. Kod sloja baze podataka, testiraju se i verifikuju se integritet baze podataka i sadržaj
baze podataka.
Testiranje interfejsa
Testiranje interfejsa se radi da proveri pravilno funkcionisanje interfejsa aplikacije, prema datoj
specifikaciji. Testiranje interfejsa se uglavnom koristi u testiranju korisničkih GUI aplikacija.
Alfa, beta i gama testiranje
Alfa testiranje je uglavnom prvo testiranje, koje prikazuje upotrebljivosti, obavlja se najčešće
„in-house“, od strane programera koji su razvijali program ili od strane testera u toj kompaniji
koja je razvijala. Ponekad se ovo alfa testiranje vrši od strane klijenata ili osobe sa strane, ali uz
prisustvo programera koji je razvijao ili testera koji je već upoznat. Verzija nakon ovog
testiranja, zove se alfa verzija programa.
Beta testiranje vrši ograničen broj krajnjih korisnika pre isporuke, a zahtevana promena će biti
izmenjena od strane isporučilaca samo ukoliko korisnik daje povratne informacije o programu ili
pripremi izveštaj o defektima (bagovima). Verzija nakon beta testiranja naziva se beta izdanje
programa.