2 TEHNICI DE TESTARE SOFTWARE 2.1 Testarea – etapă a ciclului de dezvoltare software Proiectarea aplicaţiilor software de dimensiuni mari, dezvoltat ă în cadrul unei echipe, nu se face trecând direct la implementarea programului, ci urmează etape bine stabilite. Procesul de dezvoltare al aplica ţiilor software este alcătuit din următoarele etape: specificarea cerin ţelor, analiză, proiectare, implementare, testare şi întreţinere. În figura 2.1 este prezentat grafic modelul clasic de dezvoltare software [PETE00]. Specificarea cerinţelor Analiză Proiectare Implementare Testare Întreţinere
Figura 2.1 – Modelul clasic de
dezvoltare software
În etapa de specificare a cerin ţelor se determin ă necesităţile pentru toate elementele sistemului, atât hardware cât şi software. Cerinţele privind aplicaţia e-DSI au fost analizate împreun ă cu beneficiarul. Pornind de la cerinţa unei aplicaţii accesibile într-o manier ă simplă, s-au stabilit variantele posibile de realizare şi cerinţele globale. S-a convenit cu privire la detaliile validării sistemului de c ătre beneficiar. Testarea specificaţiilor se realizează prin metode de analiză statică: inspecţii, parcurgeri şi analize tehnice. Etapa de analiză continuă procesul de stabilire a cerin ţelor. Analistul trebuie să înţeleagă domeniul informaţiilor pentru software, func ţiile necesare, performanţele şi interfeţele. Se documentează cerinţele, iar acestea sunt revăzute împreună cu beneficiarul. În aceast ă etapă este descris ceea ce trebuie să facă aplicaţia informatică şi nu modul în care se realizează. Pentru aplicaţia e-DSI sunt definite principalele caracteristici ale aplicaţiilor componente: magazin electronic, gestiunea bibliotecii, bugetul personal, agenda telefonică, re ţete culinare şi jurnalul personal. Sunt descrise cazurile de test pentru fiecare component ă în parte. În etapa de proiectare accentul se pune pe structurile de date, arhitectura sistemului, detaliile procedurale şi caracterizarea interfeţelor. În această etapă sunt identificate structurile de date, interfe ţele, modulele şi sunt descrişi algoritmii. Pentru fiecare modul în parte sunt definite specificaţiile de realizare. Pentru aplica ţia e-DSI s-a stabilit structura fiecărei aplicaţii componente, arhitectura de baz ă, tabele din baza de date, modalităţile de regăsire şi actualizare a datelor. Cazurile de test sunt rafinate şi sunt adăugate noi cazuri de test corespunzătoare detaliilor introduse. În [PRES00] se arată că peste 35% din erorile software î şi au originea în fazele de analiză şi proiectare. Prin implementare se face trecerea de la proiect la o form ă specifică maşinii de calcul. Implementarea este cu atât mai u şor de realizat cu cât proiectarea se realizează mai în detaliu. Testarea în etapa de implementare are rolul de a eviden ţia diferenţele dintre comportamentul produsului din specificaţii şi cel dat la nivelul utilizatorilor. Aplica ţia e-DSI s-a implementat în limbajul de programare corespunz ător cerinţelor. Testarea se concentrează atât asupra logicii interne a programului, avându-se în vedere ca anumite elemente ale acestuia să fie parcurse, cât şi pe funcţionalitatea externă a sa, pe baza specificaţiilor. Se compar ă rezultatele efective obţinute după rularea programului cu seturi de date de test cu rezultatele scontate pe baza specificaţiilor. În timp, după livrarea la beneficiar , aplicaţiile software sufer ă schimbări care se datoreaz ă erorilor descoperite pe parcursul func ţionării aplicaţiei, modificărilor mediului în care ruleaz ă aplicaţia (software, hardware) precum şi noilor cerinţe de performanţă şi funcţionalitate dorite
În etapa de specificare a cerin ţelor se determin ă necesităţile pentru toate elementele sistemului, atât hardware cât şi software. Cerinţele privind aplicaţia e-DSI au fost analizate împreun ă cu beneficiarul. Pornind de la cerinţa unei aplicaţii accesibile într-o manier ă simplă, s-au stabilit variantele posibile de realizare şi cerinţele globale. S-a convenit cu privire la detaliile validării sistemului de c ătre beneficiar. Testarea specificaţiilor se realizează prin metode de analiză statică: inspecţii, parcurgeri şi analize tehnice. Etapa de analiză continuă procesul de stabilire a cerin ţelor. Analistul trebuie să înţeleagă domeniul informaţiilor pentru software, func ţiile necesare, performanţele şi interfeţele. Se documentează cerinţele, iar acestea sunt revăzute împreună cu beneficiarul. În aceast ă etapă este descris ceea ce trebuie să facă aplicaţia informatică şi nu modul în care se realizează. Pentru aplicaţia e-DSI sunt definite principalele caracteristici ale aplicaţiilor componente: magazin electronic, gestiunea bibliotecii, bugetul personal, agenda telefonică, re ţete culinare şi jurnalul personal. Sunt descrise cazurile de test pentru fiecare component ă în parte. În etapa de proiectare accentul se pune pe structurile de date, arhitectura sistemului, detaliile procedurale şi caracterizarea interfeţelor. În această etapă sunt identificate structurile de date, interfe ţele, modulele şi sunt descrişi algoritmii. Pentru fiecare modul în parte sunt definite specificaţiile de realizare. Pentru aplica ţia e-DSI s-a stabilit structura fiecărei aplicaţii componente, arhitectura de baz ă, tabele din baza de date, modalităţile de regăsire şi actualizare a datelor. Cazurile de test sunt rafinate şi sunt adăugate noi cazuri de test corespunzătoare detaliilor introduse. În [PRES00] se arată că peste 35% din erorile software î şi au originea în fazele de analiză şi proiectare. Prin implementare se face trecerea de la proiect la o form ă specifică maşinii de calcul. Implementarea este cu atât mai u şor de realizat cu cât proiectarea se realizează mai în detaliu. Testarea în etapa de implementare are rolul de a eviden ţia diferenţele dintre comportamentul produsului din specificaţii şi cel dat la nivelul utilizatorilor. Aplica ţia e-DSI s-a implementat în limbajul de programare corespunz ător cerinţelor. Testarea se concentrează atât asupra logicii interne a programului, avându-se în vedere ca anumite elemente ale acestuia să fie parcurse, cât şi pe funcţionalitatea externă a sa, pe baza specificaţiilor. Se compar ă rezultatele efective obţinute după rularea programului cu seturi de date de test cu rezultatele scontate pe baza specificaţiilor. În timp, după livrarea la beneficiar , aplicaţiile software sufer ă schimbări care se datoreaz ă erorilor descoperite pe parcursul func ţionării aplicaţiei, modificărilor mediului în care ruleaz ă aplicaţia (software, hardware) precum şi noilor cerinţe de performanţă şi funcţionalitate dorite
de beneficiar. Între ţinerea aplicaţiilor software se aplic ă tuturor fazelor ciclului de viaţă. În cadrul ciclului de dezvoltare software, un rol important îl au verificarea şi validarea (V & V). Verificarea şi validarea reprezint ă un proces prin care se determină dacă cerinţele unui sistem sau componentă sunt complete şi corecte, dacă rezultatul fiecărei faze de dezvoltare îndeplineşte cerinţele sau condiţiile impuse în faza anterioar ă şi dacă sistemul sau componenta finală este în concordanţă cu cerinţele specificate [PRES00]. Verificarea este mulţimea activităţilor care asigur ă că o aplicaţie softwar e implementează corect o anumită funcţie. Testarea software este o componentă a procesului de V & V. Validarea este mulţimea activităţilor care asigur ă că o aplicaţie software realizată este în concordanţă cu cerinţele beneficiarului. Pe măsura dezvoltării incrementale a aplica ţiei e-DSI rezultatele din fazele intermediare sunt validate de către beneficiar. Testarea sau analiza statică are ca scop examinarea aplicaţiilor software f ăr ă a fi executate şi cuprinde activităţi precum inspecţiile, execuţia simbolică şi verificarea. Aceste activit ăţi fac parte din categoria evalu ările tehnice [MYER79]. Inspec ţ iile iile codului presupun citirea sau inspecţia vizuală a codului sursă de către un grup de persoane. Având la dispozi ţie codul sursă şi specificaţiile de proiectare, grupul de persoane din echipa de inspec ţie urmăreşte explicaţiile programatorului legate de logica programului, instrucţiune cu instrucţiune. În acest mod se descoper ă o serie de erori specifice acestei metode cum ar fi: declararea datelor, referirea datelor, erorile de calcul, erorile de comparare, erorile de logic ă, erorile de intrare/ieşire, erorile de interfa ţă etc. Parcurgerile constau în citirea sau inspecţia vizuală a codului sursă de c ătre un grup de persoane, îns ă procedura de lucru este diferit ă având ca scop execuţia logică a fiecărei secvenţe de cod de testat pe baza unor seturi de test pregătite anterior. Prin urm ărirea programului pas cu pas sunt identificate erori care apar la acest nivel. Rezultatele acestei activit ăţi de verificare depind, ca şi în cazul inspecţiilor codului, de atitudinea echipei faţă de persoana care a scris programul, având în vedere faptul c ă atenţia trebuie acordată programului care se verific ă şi nu celui care l-a scris. Verificarea de birou este o tehnică de analiză statică în care listingurile codurilor sau rezultatele testelor sau alte documente sunt examinate vizual, de obicei de persoana care le-a realizat, pentru a identifica erorile sau abaterile de la standardele de dezvoltare sau alte probleme.
Testarea dinamică presupune examinarea aplicaţiilor software în scopul gener ării datelor de test cu care acestea vor fi executate şi rularea aplicaţiilor cu seturile de date de test ob ţinute. Se observă că spre deosebire de testarea statică, testarea dinamică presupune execuţia aplicaţiei care se testează. Există două strategii de dezvoltare a cazurilor de test: o strategie bazată pe structura programelor şi o alta, bazată pe funcţionalitatea acestora. În dezvoltarea unui produs software testarea se realizeaz ă pe mai multe niveluri: testarea de module, testarea de integrare, testarea de sistem şi testarea de acceptare (validare). În figura 2.2 este prezentat procesul de verificare şi validare în cadrul ciclului de dezvoltare a unui produs software [TEST90]. Se identifică etapele de realizare a aplica ţiei precum şi etapele şi nivelurile procesului de testare software.
Beneficiar Cerin ţe: aplicaţie de servicii casnice electronice
Dezvoltare Validare
Testare
Specificare cerinţe Analiz ă
Verificare
Proiectare Implementare
Recep ţie e-DSI
Testare de acceptare
Testare de module
Integrare
Testare de integrare
Sistem
Testare de sistem
Figura 2.2 – Testarea software în ciclul de
dezvoltare
P r o i e c t a r e t e s t e
E x e c u ţ i e t e s t e
În cadrul test ării de module se realizează verificarea celor mai mici unităţi software care pot fi compilate şi link-editate independent. În programarea clasică un modul este un subprogram (func ţie sau procedur ă). În cadrul programelor orientate obiect, cea mai mic ă unitate testabilă este clasa. Testarea de module se concentrează asupra verificării interfeţelor modulelor, structurilor de date locale, condi ţiilor de la limite, c ăilor independente şi căilor de tratare a erorilor. [PRES00] Deoarece modulele nu sunt programe de sine st ătătoare, este necesar să se construiască programe sau funcţii care să ajute la testarea de module. O primă categorie o reprezint ă programele conducătoare (drivers) care apelează modulele supuse testării cu datele de test construite şi preiau rezultatele execuţiei. O altă categorie este reprezentată de funcţiile sau procedurile apelate de către modulul de testat. Acestea sunt substituite cu subprograme care au acelaşi prototip, însă cu funcţionalitate redusă la minim, denumite module vide ( stubs). Modulele vide sunt funcţii sau proceduri simple care nu fac nimic în afara return ării controlului, sau sunt func ţii sau proceduri complexe, care simulează efectul apelării modulelor reale. În cele mai multe cazuri, modulele vide simple sunt nepotrivite. Un efort considerabil este necesar pentru implementarea de module vide complexe. Testarea de integrare este o tehnică sistematică de construire a structurii programului prin gruparea componentelor în paralel cu testarea aspectelor legate de interfa ţa dintre componente. Testarea de integrare se realizează într-o manier ă neincrementală sau incrementală. Testarea neincremental ă (big-bang testing ) constă în integrarea componentelor prin gruparea tuturor modulelor dintr-o dat ă, urmată de testarea întregului ansamblu. Acest tip de integrare nu este recomandat ă, deoarece corectarea erorilor va fi foarte greu de realizat. Testarea incremental ă presupune construirea structurii programului pas cu pas şi testarea ansamblului obţinut. Un element important în execu ţia testului este secvenţa în care modulele trebuie să fie testate. Astfel, testarea de integrare se realizează ascendent (bottom-up), descendent (top-down) sau mixt. Metoda de testare ascendentă (bottom-up testing ) presupune testarea mai întâi a modulelor de pe cel mai de jos nivel al ierarhiei programului, apoi se continuă în sus cu testarea celorlalte module. Metoda de testare ascendentă necesită construcţia de programe conducătoare pentru a ini ţializa mediul şi pentru a apela modulele. Programele de pe nivelul de sus, care de obicei sunt critice, sunt testate ultimele. În timp sunt descoperite erori care
pot influenţa multe module care au fost deja testate. După corecţia erorilor este necesar ca toate modulele de pe nivelurile de jos să fie testate regresiv. Agenda mea
Adaugă
Caută
Modifică
Edit Figura 2.3 – Testarea de integrare ascendentă
În figura 2.3 este prezentat un exemplu de testare de integrare ascendentă. În cadrul aplicaţiei e-DSI, modulele Adaug ă, Caută, Modifică şi Edit sunt integrate şi testate, modulul ”Agenda mea” fiind înlocuit cu un modul conducător, care le apelează. În metoda testării descendente (top-down testing ), modulul din vârful ierarhiei de programe este testat primul, procesul de testare continuând cu modulele de pe nivelurile inferioare. Metoda de testare descendentă necesită construcţia de module vide asociate subprogramelor care nu sunt disponibile. Avantajul acestei metode este c ă dacă este descoperită o eroare în modulele de pe nivelurile înalte, impactul va fi asupra modulelor de pe nivelurile de jos care nu au fost înc ă implementate, deci refacerea modulelor de pe nivelurile inferioare poate fi evitat ă. În figura 2.4 se prezintă un exemplu al testării de integrare descendentă. Modulele ”Agenda mea”, Adaugă şi Caută sunt deja integrate şi testate, modulele Modific ă şi Edit fiind înlocuite cu module vide.
Agenda mea
Adaugă
Caută
Modifică
Edit
Figura 2.4 – Testarea de integrare descendent ă
În cadrul metodei mixte, testarea urmează mai întâi metoda descendentă. Modulele selectate de pe nivelurile inferioare sunt testate utilizând metoda ascendentă. Această flexibilitate permite preluare a avantajelor metodelor de ascendentă şi descendentă. În cele mai multe aplicaţii, metoda mixtă este cea mai potrivită modalitate de abordare. Aplicaţia e-DSI a fost testată utilizând această metodă de integrare. Pe măsur ă ce sunt adăugate noi module în cadrul test ării de integrare, apar schimbări în software, care pot s ă modifice comportamentul structur ii obţinute anterior. În acest context este executat ă testarea de regresie, prin care se re-testeaz ă noua structur ă obţinută, utilizând o parte din testele utilizate în etapa precedentă de integrare. Testarea de validare are loc după ce toate erorile de i nterfaţă descoperite în cadrul testării de integrare au fost corectate. Testarea de validare se încheie cu succes atunci când func ţionalitatea aplicaţiei software este în conformitate cu cerin ţele beneficiarului. Pentru testarea de validare se utilizează o serie de teste func ţionale pentru a confirma faptul c ă aplicaţia software se comportă conform cerinţelor. În cadrul testării de validare se reg ăsesc testările alfa şi beta. Testarea alfa este realizată la firma care produce aplica ţia software, beneficiarul fiind acela care conduce testele. Testarea beta este realizată la beneficiari şi utilizatori finali. Ace ştia primesc o versiune a aplica ţiei software, versiune apropiată de cea finală şi o utilizează în scopul descoperirii erorilor şi a problemelor de performan ţă şi funcţionalitate. Problemele apărute în cadrul acestei testări sunt raportate firmei care a realizat aplicaţia. După ce perioada acordată pentru testarea beta s-a terminat, toate erorile apărute sunt corectate, urmând s ă se realizeze versiunea finală a aplicaţiei software.
Pentru testarea aplicaţiei e-DSI au fost înregistrate observa ţiile primite de la o serie de utilizatori care au folosit aplicaţia în diverse stadii ale acesteia în scopul identificării problemelor şi neajunsurilor din utilizare. Utilizatorii care au testat aplicaţia au niveluri de preg ătire şi experienţă în lucrul cu calculatorul diferite. După ce a avut loc testarea aplicaţiei software, intervine testarea de sistem, prin care se testează întregul sistem în care produsul software este o parte componentă a acestuia. Testarea de sistem presupune rularea unor teste diferite, astfel încât s ă fie examinate toate caracteristicile sistemului. În literatura de specialitate, [PRESS00], [PETE00], [MYER79] sunt prezentate câteva tehnici specifice pentru testarea de sistem. Astfel, se identifică testarea pentru determinarea capacităţii de recuperare ( recovery te sting ), testarea securităţii, testarea de solicitare ( stress testing ), testarea de încărcare (load testing ) şi testarea performanţelor ( performance te sting ). T estarea pentru determinarea capacit ăţ ii de recuperare este un test de sistem care, printr-o multitudine de c ăi, for ţează aplicaţia software să îşi încheie execuţia în mod necorespunzător. Prin acestea se testează capacitatea aplicaţiei de revenire dintr-o situa ţie necorespunzătoare. Testarea securit ăţ ii se realizează pentru a verifica eficien ţa mecanismelor de protecţie dintr-un sistem software şi dacă acesta se comportă corespunzător la atacurile externe. Testarea de solicitare se execută astfel încât sistemul să funcţioneze cu un volum mai mare de resurse decât cel normal, cu scopul de a determina fiabilitatea aplicaţiei şi momentul în care funcţionarea aplicaţiei devine critică şi pentru a lua măsurile corespunzătoare, astfel încât să nu se ajungă în exploatarea de zi cu zi a sistemului informatic la astfel de situa ţii. Utilizând instrumente care simuleaz ă existenţa mai multor utilizatori simultan, precum JMeter şi Rational SiteCheck, pentru aplicaţia e-DSI s-au descoperit cazuri în care serverul devine inaccesibil. Acest lucru impune verificarea configur ării serverului de Web sau a procesorului JSP, prin modificarea numărului de clienţi care accesează aplicaţia simultan. Testarea de încă rcare constă în rularea sistemului software în condiţiile în care resursele (memorie, microprocesor, reţea) sunt încărcate la maxim astfel încât se ajunge la situaţii critice, în care o parte din re surse nu mai sunt disponibile. Se urmăreşte ca pacitatea sistemului de a-şi întrerupe funcţionarea f ăr ă pierderea sau coruperea datelor. Testarea performan ţ elor se realizează pentru determinarea conformităţii sistemului cu cerinţele de performanţă impuse. De exemplu sistemul este încărcat într-un interval de timp pornind de la capacitatea minimă până la capacitatea maximă şi se verifică dacă resursele sistemului
se află în limitele corespunzătoare şi nu sunt întârzieri în executarea funcţiilor aplicaţiei. Prin folosirea de instrumente pentru m ăsurarea performanţelor aplicaţiei e-DSI (precum JMeter) s-a constat că aplicaţia r ăspunde în limite acceptabile, cu excepţia primei cereri efectuate, datorit ă particularităţilor serverului Web şi a motorului JSP.
2.2 Fazele procesului de testare software Fazele procesului de testare sunt: planificarea, analiza şi proiectarea, implementarea şi execuţia şi evaluarea testelor. Planificarea testelor se realizează în strânsă legătur ă cu planificarea derulării proiectului. În faza de planificare a proiectului pentru testare se alocă resurse, specificându-se bugetul şi perioada de timp în care se va derula testarea. Pe baza acestora se realizeaz ă planificarea detaliată a procesului de testare. Planificarea testării are scopul de a determina ce s ă testeze şi cât să testeze, astfel încât procesul de testare s ă se încadreze în limitele resurselor alocate. În urma planific ării testării rezultă planul de test, un document pe baza căruia se desf ăşoar ă celelalte faze ale testării. În această fază se identifică şi obiectivele testării. Planul de test este un document important, fiind utilizat ca baz ă pentru desf ăşurarea întregului proces de testare. În plus, trebuie identificate şi sursele de risc în testare. Planificarea test ării poate să înceapă din momentul în care au fost elaborate cerin ţele aplicaţiei software [PETE00]. În planul de test sunt descrise: • aria de cuprindere; • responsabilităţile fiecărui membru al echipei de testare; • resursele umane necesare; • desf ăşurarea în timp a testelor; • descrierea şi configurarea mediului specific aplica ţiei; • lista echipamentelor care trebuie achizi ţionate; • crearea şi managementul datelor de test; • criteriile de acceptare a testelor. Deoarece este foarte dificil s ă se stabilească momentul în care se va încheia testarea, în planul de test se specific ă o serie de criterii care constituie o bază pentru determinarea finalizării testării. Analiza testelor rafinează metodele prezentate în planul de test. În [DUST99] sunt prezentate etapele fazelor de analiz ă şi proiectare a testelor.
Astfel, în etapa de analiză se identifică următorii paşi: • identificarea scopurilor, obiectivelor şi a strategilor testării de către echipa de testare; • metodele de verificare sunt asociate cerin ţelor sistemului sau cazurilor de utilizare şi sunt documentate în cadrul unei matrice de urmărire a cerinţelor; • analiza cerinţelor testelor; • construirea matricei cerin ţelor testelor, în care declara ţiile cerinţelor testelor sunt asociate cerin ţelor sistemului sau a cazurilor de utilizare; • asocierea tehnicilor de testare cu declara ţiile cerinţelor testelor. În faza de analiză a procesului de testare, un aspect important îl ocupă analiza cerinţelor pentru testare. Cerinţele testării trebuie să fie identificate şi documentate astfel încât toate persoanele implicate în procesul de testare ăs fie conştiente de scopul acestuia. Analiza se desf ăşoar ă din mai multe puncte de vedere, depinzând de faza de testare. Astfel se identifică o abordare structural ă şi o abordare bazată pe comportament. Faza de proiectare a testelor urmează după încheierea analizei. În faza de proiectare, apar urm ătorii paşi: • definirea modelului programului de test astfel încât acesta s ă reflecte tehnicile de testare utilizate; • definirea arhitecturii de test; • definirea procedurilor de test; • luarea deciziei de automatizare a anumitor teste şi de testare manuală a altor componente; • asocierea datelor de test astfel încât fiecare cerin ţă pentru datele de test să se reflecte pentru fiecare procedur ă de test. Programul de test se elaborează fie la nivelul proiectării, fie la nivelul tehnicilor de testare. În primul caz, procedurile de test sunt asociate componentelor hardware şi software ale aplicaţiei, iar în al doilea caz procedurile de testare sunt v ăzute la nivelul tehnicilor de testare. Proiectarea procedurilor de test începe dup ă determinarea cerinţelor testării. Proiectarea procedurilor de test const ă în: • analiza arhitecturii de test pentru determinarea tehnicilor de testare relevante; • definirea procedurilor de test atât la nivelul sistemului cât şi la nivelul de implementare; • elaborarea sau preluarea de standarde de utilizare a procedurilor de test;
identificarea procedurilor de test care vor fi efectuate manual şi a celor care vor fi efectuate automat; • identificarea procedurilor de test complexe, pentru a fi proiectate în continuare în faza de proiectare detaliat ă; • proiectarea detaliat ă. În etapa de implementare a testelor sunt construite cazurile de test şi procedurile de test, pe baza rezultatelor fazei de proiectare. Cazurile de test descriu atât parametrii de intrare cât şi rezultatele aşteptate după execuţie utilizând acei parametri. Realizarea de cazuri de test cât mai complete duce la descoperirea unui număr mai mare de erori. Procedurile de test identific ă toţi paşii necesari pentru executarea cazurilor de test specifice. Primul pas în faza de implementare este iniţializarea mediului de implementare prin punerea la punct a arhitecturii dezvolt ării testelor. Un alt aspect important este identificarea standardelor pe care se bazează elaborarea secvenţelor de test. Dacă au fost definite astfel de standarde de implementare, atunci implementarea se realizeaz ă pe baza acestora. Dacă nu există standarde, în cadrul acestei faze, la început, se stabilesc convenţii de scriere a programelor de test (alinieri, comentarii, prefixe pentru date). Implementarea secvenţelor de test se realizeaz ă în limbaje specifice mediilor de testare (asemănătoare Visual Basic) sau se utilizează limbaje de programare evoluate (C++, Java). Prin reutilizare se folosesc proceduri de test din proiectele anterioare sau din cadrul aceluiaşi proiect pentru module care au p ăr ţi comune. În [McGR97c], [McGR97d] este prezentată o arhitectur ă paralelă de testare a claselor, în care clasele de test sunt derivate în paralel cu dezvoltarea ierarhiei claselor care se testează. Faza de rulare a testelor are ca intr ări planul de test şi orarul execuţiei procedurilor de test, mediul de test fiind preg ătit corespunzător. Ieşirile fazei de execuţie a testelor sunt rezultatele testelor, lecţiile învăţate şi orarul modificat al testelor. Execu ţia modulelor se realizează în conformitate cu planul de test. Procedurile de test descriu intr ările şi ieşirile aşteptate după execuţie. În această etapă din cadrul procesului de testare sunt rulate secvenţele de test. Execuţia secvenţelor de test se realizează pe cât posibil în mediul specific aplicaţiei iar dacă nu este posibil, acest mediu este simulat. Rezultatele execuţiei secvenţelor de test sunt evaluate pentru a determina dacă produsul a trecut testul cu succes. Evaluarea rezultatelor testelor se face de către un oracol . Oracolul este o persoană sau un •
instrument automat, care pe baza specificaţiilor determină dacă rezultatele execuţiei testelor sunt corecte sau nu. În figura 2.5 este prezentat fluxul procesului de execu ţie şi evaluare a testelor pentru testarea la nivel de modul, bazat ă pe specificaţii. Rezultatele testelor arată dacă programul a trecut sau nu testul. Program de testat Execuţie Date de intrare
Ieşiri
Evaluare
Rezultate teste
Ieşiri aşteptate
Figura 2.5 – Fazele de execuţie şi evaluare pentru testarea de module
Rezultatele execuţiei testelor se vor memora într-o baz ă de date care conţine şi alte informaţii referitoare la aplica ţia software realizată. Execuţia şi evaluarea testării de integrare necesită noi secvenţe de test pe măsur ă ce se adaugă module în cadrul structurii programului care se testează. Aprobarea de către beneficiar a rapoartelor test ării de modul şi ale testării de integrare constituie încheierea acestor faze. În execuţia şi evaluarea testării de sistem, beneficiarul aplica ţiei se implică mai mult decât în celelalte faze. În mod asemănător, acesta trebuie să semneze raportul de test pentru a considera încheiată această fază de testare. Procesul de testare pentru aplica ţia e-DSI a urmat aceleaşi etape, gradul de aprofundare fiind diferit.
2.3 Testarea structurală Testarea structurală (white box testing ) este o strategie de testare care necesită accesul la codul sursă şi la structura programului şi pune accentul pe acoperirea prin teste a căilor, ramificaţiilor şi fluxurilor programului. Principalele metode de testare structural ă au în vedere gradul în care cazurile de test acoper ă sau execută codul sursă al programului.
Strategiile de testare bazate pe c ă i utilizează fluxul de control al programului. Acestea reprezintă o familie de tehnici de testare bazate pe selectarea cu atenţie a unei mulţimi de căi din program. Dacă mulţimea căilor este aleasă corespunzător, atunci se va atinge o anumită măsur ă a profunzimii testului. Pentru utilizarea acestor tehnici este necesar ă cunoaşterea completă a structurii programului şi accesul la codul surs ă. Tehnicile sunt utilizate cel mai des de c ătre programatori pentru testarea propriului cod. Cu ajutorul acestei tehnici se detectează erorile care cauzează execuţia unei alte căi a programului decât aceea care trebuia s ă se
execute. Graful asociat programului este o reprezentare grafic ă a structurii de control al programului, care utilizeaz ă elemente ca proces, decizie şi joncţiune. Un bloc de baz ă este o secvenţă de instrucţiuni ale programului, neîntrerupte de decizii sau de joncţiuni. Un proces are o singur ă intrare şi o singur ă ieşire şi constă dintr-o singur ă declaraţie/instrucţiune, dintr-o secvenţă de declaraţii/instrucţiuni, un singur apel de subrutin ă sau o secvenţă din acestea. O decizie este un punct al programul ui în care fluxul de control continuă pe una din alternativele oferite. Construc ţiile if-then-else şi switchcase sunt exemple de decizii cu două ramuri, respectiv cu n ramuri, n ≥2. Jonc ţ iunile sunt punctele din program în care fluxul de control se uneşte, care pot fi etichetele ţintă ale unui salt, punctele în care se unesc ramurile unei instrucţiuni if-then-else etc. O cale într-un program este o secven ţă de instrucţiuni care începe cu o intrar e, joncţiune sau decizie şi se termină la altă (sau aceeaşi) joncţiune, decizie sau ieşire. O cale poate să treacă prin câteva joncţiuni, procese sau decizii o dată sau de mai multe ori. Un segment constă dintr-o succesiune de legături consecutive care apar ţin aceleiaşi căi. Lungimea unei căi este dată de numărul de legături şi nu de numărul de declaraţii sau instrucţiuni executate de-a lungul căii. O metodă alternativă de măsurare a lungimii unei c ăi este numărul de noduri traversate. Dac ă programul are un singur nod de intrare şi un singur nod de ie şire, numărul de legături traversate este mai mic cu unu decât numărul nodurilor traversate. Numele căii este dat de numele nodurilor de-a lungul c ăii. De exemplu în cadrul aplica ţiei e-DSI diverse secven ţe de program au graful asociat asemănător celui din figura 2.6. Nodurile sunt numerotate corespunzător fluxului secvenţei de program. O cale de la punctul de intrare până la ieşire se notează de exemplu cu 1-2-3-5-6-7-9-10. În mod asemănător, dacă se notează legăturile, numele căii este dat de succesiunea
numelor legăturilor de-a lungul c ăii. Pentru aceeaşi cale, numele ei este abcfghk . În practică, o cale de test este o cale care începe în punctul de intrare în program şi se termină la ieşirea din program.
1 a 2 b
d
4
e
c f
5 6
i
8
3
g
7
h
j k 9 10
Figura 2.6 – Nodurile şi legăturile grafului asociat unei secven ţe de
program Tehnicile de testare bazate pe căile programului au la baz ă o serie de criterii de acoperire a codului care se testeaz ă precum: acoperirea instrucţiunilor, acoperirea ramificaţiilor, acoperirea condi ţiilor, acoperirea ramificaţiilor şi a condiţiilor, acoperirea condiţiilor multiple şi acoperirea tuturor căilor programului. Pe baza acestor criterii de acoperire a codului se determină seturile de date de test care se utilizează în testările structurale corespunzătoare: testarea instrucţiunilor, testarea ramificaţiilor, testarea căilor etc. Criteriul pentru acoperirea instruc ţ iunilor este ca fiecare instrucţiune să fie executată cel puţin o dată în cadrul unor teste. Dac ă se realizează acest obiectiv, atunci declaraţiile sunt acoperite în propor ţie de 100%. Acest lucru este echivalent cu o acoperire a nodurilor fluxului de control asociat programului în propor ţie de 100%. Criteriul de acoperire a instrucţiunilor este criteriul cel mai slab, deoarece cazurile de test identificate astfel încât s ă fie acoperite toate instrucţiunile iau în calcul doar
acest criteriu, nefiind tratate toate situa ţiile posibile. Acest criteriu de acoperire se mai numeşte şi criteriul C1. Acoperirea ramifica ţ iilor are drept criteriu ca fiecare ramur ă a unei decizii să fie executată cel puţin odată. Se rulează un număr suficient de teste pentru a se executa toate ramifica ţiile din program cel pu ţin o dată. În cazul în care s-a realizat acest lucru, se atinge un procent de 100% a acoperirii ramificaţiilor sau echivalent de acoperire a leg ăturilor dintre nodurile grafului asociat programului. Pentru software-ul structurat testarea tuturor ramificaţiilor include şi acoperirea tuturor declara ţiilor. Criteriul de acoperire a ramificaţiilor se notează cu C2. Acoperirea condi ţ iilor are ca obiectiv identificarea de date de test astfel încât fiecare condiţie cu rezultat logic s ă ia valorile posibile (adevărat sau fals). În secvenţa: if(criteriu!=nul l &&
!criteriu.equals(""))
pentru acoperirea ramificaţiilor sunt necesare două cazuri de test corespunzătoare celor două ramuri, iar pentru acoperirea condi ţiilor sunt necesar e patru cazuri de test corespunz ătoare combinaţiilor adevărat sau fals pentru cele dou ă condiţii. Pentru acoperirea ramifica ţ iilor şi condi ţ iilor criteriul de parcurgere a codului sursă este o combinaţie a criteriilor de la acoperirea ramifica ţiilor şi acoperirea condiţiilor. Criteriul de acoperire a condi ţ iilor multiple are ca scop execuţia cel puţin o dată a fiecărei combinaţii de stări posibile ale condi ţiilor. Acoperirea tuturor că ilor programului are ca obiectiv execuţia tuturor căilor fluxului de control din program, care încep la intrarea în program şi se termină la ieşirea din program. Dac ă se reuşeşte să se îndeplinească acest criteriu, atunci se acoper ă căile în propor ţie de 100%. Acesta este cel mai puternic criteriu din familia de strategii de testare bazate pe căi şi este, în general, imposibil de atins deoarece numărul de căi poate ajunge foarte mare, mai ales prin existen ţa structurilor repetitive. Acoperirea declaraţiilor şi a ramificaţiilor sunt cerinţe minime obligatorii pentru testarea structural ă. Pentru programele care conţin bucle, acoperirea marginilor interioare (bound ary-interior cover ) este deseori utilizată pentru a executa bucla cel puţin de două ori. În primul caz se execut ă bucla f ăr ă iteraţii, iar în al doilea caz se execută bucla cu una sau mai multe iteraţii. Dacă testarea se realizează utilizând doar analiza căilor sunt detectate circa 25% din erori.[MYER79]
În capitolul 8 sunt tratate distinct problemele legate de testarea structurală şi sunt f ăcute aprecieri în legătur ă cu costul asociat diferitelor tipuri de teste la nivel structural. Testarea fluxului datelor utilizează graful fluxului de control pentru a studia anomaliile fluxului de date. Acest tip de testare formeaz ă o familie de strategii de test bazate pe selec ţia unei căi din fluxul de control al programului în scopul cercetării secvenţelor de evenimente referitoare la starea datelor. Testarea fluxului de date se bazează pe faptul că cel puţin jumătate din codul sursă constă în declaraţii de date, declaraţii car e definesc structuri de date, obiecte individuale, valori ini ţiale (sau implicite) şi atribuite [BEIZ90], [PETE00]. Graful fluxului datelor este alcătuit din noduri şi legături direcţionate. Obiectivul s ău este acela de a prezenta deviaţiile de la fluxul de date im plementat faţă de ceea ce s-a dorit la proiectare. Acţiunile posibil de efectuat asupra datelor sunt: • definire (d); este explicită, când variabila apare într-o declara ţie de date şi implicită, când variabila este într-o instruc ţiune de atribuire; • nedefinire (k ); atunci când data nu mai este disponibil ă sau când conţinutul ei nu este cu certitudine cunoscut; • utilizare (u): o în calcule (c); variabila apare în partea dreaptă a unei atribuiri, într-o expresie de calcul; o într-un predicat ( p); când apare direct, de exemplu if (a
O variabilă se află într-una din următoarele stări: K – nedefinit ă, inexistentă; D – definită, dar neutilizată; U – utilizată; A – anomalie. În figura 2.7 sunt prezentate secvenţele de acţiuni prin care o variabilă trece dintr-o stare în alta.
Figura 2.7 – Graful stărilor unei variabile
Strategiile de testare pe baza fluxurilor datelor sunt: • all-use: se consider ă o cale care începe cu definirea unei variabile şi se încheie la prima utilizare a acesteia; • c-use: căile încep cu definirea variabilelor şi se încheie cu instrucţiunea în care variabilele sunt folosite în calcule; • p-use: o cale începe cu definirea unei variabile şi se termină la instrucţiunea în care variabila este utilizat ă într-o condiţie logică; • du-use: constă în parcurgerea tuturor fluxurilor de forma definire-utilizare pentru fiecare dat ă care apare în program. Strategia de testare pe baza fluxurilor datelor utilizat ă cel mai des este strategia du-use.
1 int n=0; 2 Produs produse[]; … 3 public boolean AdaugaProdus(Produs p) 4 { 5 if(n
Figura 2.8 – Fluxurile de date pentru func ţia AdaugaProdus
În figura 2.8 sunt identificate c ăile du-use 1,7 pentru variabila n şi 3,7 pentru variabila p, precum şi căile c-use 1,8 pentru variabila n şi 2,7 pentru variabila produse. Căile all-use încep cu definirea unei date şi se încheie la prima utilizare. De exemplu pentru variabila n o cale este 1,5. Testarea bazată pe fluxul datelor nu este eficient ă în cazurile: • determinării stării unei variabile într-un punct al programului; • masivelor; • structurilor şi listelor; • numelor de funcţii care sunt şi ele variabile. În metoda de testare bazat ă pe muta ţ ii, selectarea datelor de test se face pornind de la două ipoteze: 1. programatorii realizeaz ă programe "aproape corecte"; 2. datele de test care detectează erori simple sunt de asemenea eficiente în detectarea erorilor complexe. [BEIZ90] Se realizează mutaţii ale programelor prin modificarea unei singure declaraţii în programul original. Apoi se aleg datele pentru a distinge toate mutaţiile faţă de programul original verificându-se astfel capacitatea cazurilor de test alese de a descoperi erorile introduse în program prin mutaţii.
2.4 Testarea funcţională Testarea funcţională (black-box testing ) este o strategie de testare care necesită cunoaşterea comportamentului extern al programului pe baza specificaţiilor. Testarea funcţională nu necesită cunoaşterea structurii interne a programului sau cunoştinţe despre modul în care este implementat programul sau modulul. Principalele tehnici de testare func ţională sunt testarea cu intr ări aleatoare, partiţionarea pe clase de echivalenţe, analiza valorilor limit ă, graful cauză-efect şi ghicirea erorilor. Testarea cu intr ăr i aleatoare porneşte de la domeniul datelor de intrare şi constă în generarea de date de test în mod aleator, în cadrul domeniului acestora. Este o tehnic ă slabă de testare, cu cel mai mic procent de descoperire a erorilor. Aceast ă tehnică de testare a fost dezvoltată, astfel încât datele de test sunt alese aleatoriu din domeniu şi sunt filtrate astfel încât să îndeplinească anumite condiţii, ca de exemplu producerea unui rezultat dintr-o clasă de ieşiri posibile. Parti ţ ionarea pe clase de echivalen ţ e constă în împăr ţirea domeniului datelor de intrare în subdomenii, astfel încât comportamentul programului să fie acelaşi, cel puţin teoretic, pentru orice dată de intrare din subdomeniul corespunzător. De exemplu, pentru func ţia care returnează valoare absolută a unui număr întreg, un subdomeniu ar fi intervalul numerelor strict negative, iar un alt subdomeniu ar fi cel al numerelor pozitive. Dintr-un număr ridicat de cazuri de test posibile se ajunge la dou ă cazuri de test. Caracteristicile partiţionării pe clase de echivalenţe sunt: • domeniul datelor de intrare este împ ăr ţit în clase de echivalenţe; • elementele din aceeaşi clasă sunt tratate în mod asemănător; • de asemenea se are în vedere parti ţionarea ieşirilor; • se consider ă atât clasele valide cât şi cele invalide; • pentru testare se alege cel pu ţin un element din fiecare clas ă de echivalenţă. Prin utilizarea analizei valorilor limit ă pentru realizarea cazurilor de test, pentru fiecare parametru al funcţiei se determină un set de valori aflate la limita domeniul de validitate. Analiza valorilor limit ă se concentrează asupra valorilor de la limita claselor de echivalen ţe şi sunt avute în vedere limitele atât în spaţiul intr ărilor cât şi în spaţiul ieşirilor. Dacă li este limita inferioar ă şi l s este limita superioar ă a domeniului unei date de intrare de tip numeric, pentru aceasta se vor construi următoarele cazuri de test: li-1, li, li+1, ls-1, ls şi ls+1 (figura 2.9).
li-1
li l i+1
ls-1
ls
ls+1
Figura 2.9 – Valorile limită pentru date numerice
În cazul aplicaţiei e-DSI, validarea sumelor introduse ca venit impune teste pentru valori mai mici decât zero, egale cu zero şi valori peste zero. Având în vedere faptul c ă în baza de date venitul se memoreaz ă ca long , valori posibile pentru teste sunt de exemplu -1, 0, 1, MAX_LONG-1, MAX_LONG şi MAX_LONG+1, unde MAX_LONG reprezint ă o constantă egală cu valoarea maximă care se poate reprezenta pe formatul long int. Pentru parametrii de tip şir de caractere de lungime maximă L se aleg ca exemple de test: • şirul vid,; • şirul având un caracter; • şirul de lungime L-1; • şirul de lungime L; • şirul de lungime L+1. În cadrul aplicaţiei e-DSI există o serie de date de intrare de tip şir de caractere. De exemplu, pentru numele unei persoane care se introduce în agendă, valorile posibile de test sunt şirul vid, un caracter, un nume format din patru caractere, un nume alcătuit din numărul maxim de caractere rezervate în baza de date (50) şi un şir de caractere care dep ăşeşte acest număr maxim. De asemenea, în cazul şirurilor de caractere, pentru teste se utilizează şi caractere speciale, având în vedere faptul c ă există prelucr ări care se efectuează asupra şirurilor de caractere în cadrul sistemului de gestiune a bazelor de date. La aceste toate aceste cazuri de test se adaug ă valori din interiorul domeniului variabilelor. Graful cauz ă- efect este o tehnică pentru alegerea cazurilor de test într-un mod sistematic şi ar e la bază un limbaj formal în care sunt translatate specificaţiile bazate pe limbajul natural. Cauza este o condi ţie distinctă de intrare sau o clasă de echivalenţă iar efectul este o condi ţie de ieşire sau o transformare a sistemului.
În [MYER79] este prezentată o metodă de testare de ghicire a erorilor , metodă care nu presupune utilizarea unei metodologii anume, ci se bazează pe intuiţia anumitor persoane şi capacitatea acestora de a descoperi erori.
2.5 Testarea software orientat obiect Programarea orientată obiect presupune definirea de clase şi obiecte, construirea de ierarhii, precum şi referirea obiectelor create. În cazul în care clasele sunt bine definite, reutilizarea generează efecte pozitive. Când clasele sunt subdefinite este necesar ă construirea de clase derivate de completare. Când clasele sunt supradefinite apar restric ţii de referire şi de reutilizare. Testarea completă a claselor este premisa reutiliz ării. Testarea claselor evidenţiază gradul de moştenire şi de acoperire probabilă a tipologiilor de clase de probleme prin constructori/destructori definiţi. Sistemul orientat obiect este caracterizat printr-un nivel foarte ridicat al reutilizării software. De aceea el a câ ştigat teren în faţa metodologiilor clasice, tocmai datorită acestui aspect al reutiliz ării. Dacă nu ar fi reutilizarea, tehnologia orientat ă obiect ar fi la fel ca celelalte, doar pu ţin mai rapidă. Construirea clasei CosCumparaturi în cadrul aplicaţiei e-DSI este echilibrată, nefiind necesar ă obţinerea de clase derivate. Mai mult, definirea de metode de tipul get, face ca referirea membrilor s ă se facă f ăr ă utilizarea de parametri: getNumarProduse() ; Numeroase aplicaţii complexe se prezint ă sub forma unor programe apelatoare, corespunzător limbajului C++ func ţia principală main(), care conţin secvenţe lungi de directive #include iar ca instrucţiuni executabile, în general structuri liniare de expresii de definire de obiecte şi referire a funcţiilor membre ale obiectelor definite [IVAN99]. Testarea software orientat obiect presupune dou ă planuri: • testarea construcţiilor proprii; • testarea construcţiilor incluse pentru reutilizare. Metodele orientate obiect utilizează strategii diferite de încapsulare faţă de metodele convenţionale, acest lucru având un dublu impact asupra testării software [BERA90]: • cea mai mică unitate testabilă nu mai este modulul; • strategia pentru testarea de integrare trebuie modificat ă. În mediile neorientate obiect, unitatea testabil ă are o interfaţă bine definită şi realizează o singur ă funcţie specifică. Într-un mediu orientat
obiect se utilizează clasele, care sunt unităţi de program mari datorită faptului că acestea includ metode şi date membre. Metodele unei clase nu mai pot fi testate izolat, ci în contextul clasei c ăreia apar ţin. Testarea software orientat obiect are pe lângă obiectivul general al stabilirii măsurii în care produsul software realizeaz ă sarcinile prezentate în specificaţii, obiective specifice legate de: • testarea funcţiilor membre ale fiec ărei clase; • testarea gradului de încapsulare şi a efectelor acestuia; • testarea efectelor induse de nivelurile de mo ştenire şi de derivare; • testarea efectelor induse de polimorfismul func ţiilor membre; • testarea interacţiunilor dintre clase. Spre deosebire de aplicaţiile software dezvoltate prin alte metode, în cazul programării orientate obiect testarea vizeaz ă şi măsura în care clasele sunt proiectate în vederea reutiliz ării. Astfel, se evidenţiază gradul de generalitate şi, mai ales, concordanţa între specificaţiile fiecărei funcţii şi ceea ce funcţia realizează efectiv. Rezultatul testării vizează două aspecte: • secvenţa referirilor determin ă pentru exemplele de test rezultate acceptabile sau nu, ceea ce se r ăsfrânge asupra produsului ca atare; • rezultate privind măsura în care clasele definite sau referite acoper ă cerinţele unei reutilizări confortabile, f ăr ă nevoia de a construi interfeţe sau de a realiza derivări în obţinerea de clase noi cu un nivel de saturare redus, create în mod special şi destinate unor utilizări cu totul particulare. Dacă produsul este acceptat pentru calit ăţile lui în raport cu problema de rezolvat, nu este obligatorie şi acceptarea claselor definite. În acelaşi fel, clasele definite pot îndeplini condi ţiile de reutilizare, f ăr ă ca programul s ă fie acceptat în urma test ării. În ciuda avantajelor pe care limbajele şi metodologiile orientate obiect le ofer ă, testarea r ămâne necesar ă. Destul de des sistemele complexe produc rezultate neanticipate. Rolul testării în domeniul orientat obiect derivă din mai multe lucruri, dar în principal din faptul c ă acele componente realizate pentru reutilizare trebuie s ă fie de înaltă fiabilitate. O testare extensivă trebuie asigurată atunci când este destinată reutilizării. Testarea este necesar ă pentru a obţine o înaltă fiabilitate în sistemele orientate obiect. Paradigmele ingineriei software orientate obiect sunt ascunderea informaţiei, moştenirea şi polimorfismul. Acestea influen ţează modul în care se testează aplicaţiile orientate obiect fa ţă de aplicaţiile clasice.
Ascunderea informa ţ iei presupune că sunt vizibile doar acele informaţii care sunt necesare la un moment dat în scopul atingerii unor obiective specifice. Dacă sunt accesibile mai multe informa ţii decât este necesar, creşte posibilitatea apariţiei erorilor. În m ăsura în care limitează domeniul de accesibilitate, încapsularea cu ascunderea informaţiei este un obstacol pentru testare, ţinând cont de faptul c ă stările implementate nu mai sunt controlabile şi observabile. Pentru clasa CosCumparaturi, accesul la data membr ă n ar fi imposibil dintr-o func ţie de test dacă în clasă nu ar exista metoda care returnează valoarea acestui membru. Mo ştenirea se defineşte ca fiind o rela ţie între clase în cadrul c ăreia o clasă partajează structura sau comportamentul definite într-o alt ă clasă (moştenire simplă) sau în mai multe clase (mo ştenire multiplă) [BOOC91]. Noi oportunităţi de eroare vin odat ă cu puterea crescută a limbajelor orientate obiect. Fiecare nivel nou în ierarhia de mo şteniri este un nou context pentru caracteristicile moştenite. Comportamentul corect la un nivel superior al ierarhiei nu garanteaz ă în nici un fel comportamentul corect la un
nivel inferior. Polimorfismul este
exemplul cel mai practic al reutilizării, fiind prin definiţie capacitatea unei entităţi de a lua mai multe forme. Legarea dinamică joacă un rol în determinarea cerin ţelor testării, dar numai în contextul testării de integrare. Legarea dinamic ă creează posibilitatea unui set de mesaje (combinaţii de obiecte emiţătoare şi receptoare de mesaje), ceea ce înseamnă că va fi nevoie de mai multe teste (în locul unuia singur) pentru a testa un mesaj specific. Polimorfismul şi legarea dinamică cresc foarte mult numărul căilor posibile de execuţie. Analiza statică a codului sursă pentru identificarea c ăilor nu mai este de mare ajutor în acest nou context. În [CHAN02] sunt prezentate nivelurile la care sunt testate programele orientate obiect: • nivel algoritmic; metodele claselor sunt tratate independent; aplicarea tehnicilor de testare clasice se realizează f ăr ă probleme; • nivelul clasei; clasa este testat ă ca o entitate de sine st ătătoare; sunt integrate metodele care au fost testate la nivelul anterior; • nivel de grup ( cluster ); testarea se concentrează asupra integr ării claselor; se au în vedere apelurile de metode efectuate între clase şi sincronizările dintre obiectele concurente; • nivel de sistem; sunt testate interac ţiunile dintre grupurile de clase. În [SIEG96] este prezentată o metodă de testare ierarhică a aplicaţiilor software orientate obiect. Aceast ă metodă de testare se utilizează
şi
se construieşte pe baza unor tehnici de testare cunoscute, legate împreun ă într-un sistem de testare corespunz ător. Metoda defineşte şi aplică standarde de testare pentru câteva niveluri ale componentelor software: obiecte, clase, componente de bază şi sisteme. Metoda de testare ierarhică constă în desemnarea ca fiind corespunzătoare acele componente care îndeplinesc standardele de testare pentru tipul respectiv de componentă. Odată ce o componentă a fost calificată drept corespunzătoare prin testare, aceasta va fi integrat ă cu alte componente care au fost desemnate ca fiind corespunzătoare, pentru a realiza împreună componentele de pe nivelul următor. Starea de a fi corespunz ător pentru o component ă este relativă şi depinde foarte mult de standardele utilizate pentru realizarea aplicaţiei, de atitudinea faţă de risc, de riscurile specifice şi de practicile de management al riscului adoptate în cadrul proiectului. Metoda ierarhică elimină obligativitatea testării tuturor combina ţiilor de stări în timpul test ării de integrare, ceea ce duce la cre şterea productivităţii prin accesarea doar a interconexiunilor dintre componentele de bază şi orice funcţionalitate complexă nouă. Metoda de testare ierarhic ă este reprezentată grafic în figura 2.10.
Figura 2.10 – Metoda de testare ierarhică [SIEG96]
Testarea ierarhică este utilizată pentru software cu grad de complexitate ridicat. Aplicaţiile distribuite dezvoltate folosind tehnologii orientate obiect intr ă în această categorie.
În cadrul testării ierarhice, sunt utilizate urm ătoarele suite de teste: • suita de teste condiţionale care constă din testarea claselor utilizând modelul de testare condi ţională şi aser ţiunile, excepţiile, operaţiile de testare concurente adi ţionale; • suita de teste ierarhice incrementale utilizat ă pentru testarea componentelor de bază prin diverse modele de testare şi scenarii; • suita de teste de integrare pentru testarea combina ţiilor de componente de bază utilizând modelul ierarhic incremental cu scenarii care utilizează toate componentele, nu doar module vide; • suita de teste de sistem care are ca scop testarea sistemelor componentelor de bază utilizând modele de testare de sisteme; • suita de teste de regresie pentru a testa componentele de baz ă şi sistemul. La baza metodei ierarhice şi incrementale de testare st ă ansamblul de relaţii dintre clase. În mediul orientat obiect, într-o ierarhie de clase, când se testează o clasă se testează în acelaşi timp şi clasele părinte şi, construind teste, acestea pot fi reutilizate ulterior şi pentru testarea subclaselor. Fiecare subclasă este rezultatul combinării structurii şi comportamentului claselor părinţi cu atribute şi metode noi. În figura 2.11 se observă cum, prin moştenire, clasa derivată CosCumparaturi se obţine prin combinarea clasei de baz ă Object cu un modificator, care cuprinde metodele şi proprietăţile specifice clasei derivate. Dacă se notează cu D clasa derivată, cu B clasa de bază şi cu M modificatorul, se poate scrie c ă D = B ⊕ M, conform [HARR92]. CosCumparat uri Object
Modificator
Figura 2.11 – Modificarea incrementală prin moştenire
De exemplu, se consider ă o metodă care apar ţine unei clase, aceasta fiind testată în contextul clasei sale. Se creeaz ă o nouă clasă din aceasta prin deriva re, clasă care va moşteni şi metodatestat ă anterior. Chiar dacă metoda a fost testată în contextul superclasei, nu se poate garanta corectitudinea metodei în contextul subclasei pân ă când aceasta nu se testează în noul context creat. Cazurile de test bazate pe func ţionalitate create pentru metodele din superclasă nu sunt în întregime adecvate acelor aşi metode din clasa derivată. De asemenea, vor trebui modificate şi cazurile de test bazate pe structur ă În cadrul unei clase derivate au fost distinse trei metode: • metode noi – metode definite în subclase, incluzându-le pe acelea cu acelaşi nume ca metoda din superclasă dar cu parametri diferiţi; necesită o testare completă; • metode recursive – metode definite într-o superclas ă şi care nu sunt supradefinite sau supraîncărcate în subclasă; necesită o retestare limitată dacă metoda interacţionează cu funcţii membre noi sau redefinite; este nevoie doar s ă fie retestat obiectul care interacţionează, nu toate obiectele; • metode redefinite – metode definite într-o superclas ă şi supradefinită sau supraîncărcată într-o subclasă; se retestează prin reutilizarea modelelor de teste şi obiecte dezvoltate din specificaţii şi mai puţin pe baza logicii interne. De aici rezultă că pentru un obiect v a fi nevoie să se testeze doar p ăr ţile care sunt noi. Acest lucru cre şte semnificativ productivitatea test ării, precum şi productivitatea programării şi proiectării. Moştenirea multiplă conduce la clasificări mai dificile, dar nu afectează categoriile în sine. Anumite modele de mo ştenire multiplă, din limbajul C++, determin ă existenţa aceleiaşi superclase în două sau mai multe instanţe, în obiect, creând astfel mai mult decât o metod ă recursivă.
2.6 Testarea sistemelor distribuite bazate pe tehnologia Internet Reţelele de calculatoare au o extindere rapidă într-o multitudine de domenii cum ar fi sistemul bancar, administra ţia publică, alocarea temporară de resurse în hoteluri, rezervar ea biletelor de avion, rezervarea biletelor de tren etc. Aplicaţiile moderne iau în considerare accesul unui număr cât mai mare de utilizatori, mai ales de când se prevede extinderea folosirii cardurilor şi creşte numărul acelora care utilizeaz ă Internetul.
Aplicaţiile distribuite constau în mai multe componente care ruleaz ă pe maşini diferite, acestea aplicaţii integrând acţiunile componentelor lor. Proiectarea aplicaţiilor distribuite se axează nu numai pe detaliile păr ţilor individuale, ci şi pe realizarea unei integr ări a componentelor distribuite, astfel încât acestea să coopereze foarte bine între ele. Principalele cerinţe pentru aplicaţiile distribuite sunt: • interfeţe puternice; • fiabilitate foarte mare; • securitate ridicată; • viteză ridicată de prelucrare şi transmitere a datelor. În mod tradiţional, aplicaţiile software distribuite se bazeaz ă pe arhitectura client/server sau pe arhitectura multistrat ( n-tier ). În contextul aplicaţiilor client/server care utilizeaz ă baze de date, arhitectura client/server presupune existen ţa unui server de baze de date (denumit server) şi a unui modul software specific aplicaţiei (denumit client) care prelucrează datele şi prezintă rezultatele, deci conţine logica aplicaţiei şi logica prezentării. În acest si stem nu există noţiunea de obiecte, partea client lucrează direct cu tabelele de date şi procedurile stocate din baza de date. În cadrul arhitecturii multistrat, un server de aplica ţii se interpune între aplicaţia client şi serverul de baze de date. Serverul de aplicaţii implementează logica aplicaţiei iar clientul implementează logica de prezentare a sistemului. Avantajul ma jor al arhitecturii multistrat faţă de arhitectura client/server îl reprezint ă creşterea flexibilităţii. Arhitectura Web confer ă acestora fiabilitate, scalabilitate şi flexibilitate ridicată. Arhitectura Web difer ă faţă de arhitectura multistrat prin două aspecte (figura 2.12): • aplicaţia client are o complexitate redus ă, fiind un navigator Web; • nivelul regulilor aplica ţiei este bazat pe componente şi nu este un singur sistem ce implementează întreaga construcţie.
Bază de date A Naviga tor
Server Web
Server de aplica ţii
Bază de date B
Figura 2.12 – Arhitectura sistemelor bazate pe Internet
Com ponentele client sunt interfeţele grafice utilizator şi rulează în navigatoar e Web precum Netscape Navigator sau Internet Explorer. Componentele server care rulează într-un server de aplicaţii, furnizează logica procesului de afaceri. Pentru testarea aplicaţiilor distribuite bazate pe In ternet, trebuie luate în considerare următoarele aspecte: • capacitatea de utilizare; uşurinţa în utilizare şi înţelegerea elementelor interfeţei constituie elemente importante în acceptarea aplicaţiei de către clienţi; • funcţionalitatea; faptul că aplicaţia realizează ceea ce îşi propune prin specificaţii conduce la cre şterea încrederii utilizatoril or în aceasta şi în firma produc ătoare; • compatibilitatea; având în vedere faptul c ă există o multitudine de combinaţii între sisteme de operare, navigatoare şi aplicaţiile care rulează în navigatoare, asigurarea compatibilit ăţii aplicaţiei este o problemă importantă, deoarece există riscul ca o parte importantă dintre potenţialii utilizatori să nu poată utiliza aplicaţia datorită incompatibilităţilor şi astfel sunt pierduţi o serie de clienţi. • performanţa; timpul de r ăspuns şi resursele ocupate reprezint ă un aspect important pentru utilizatorii aplica ţiei. În cazul aplicaţiilor Internet care accesează baze de date există o multitudine de cauze care duc la func ţionarea incorectă a acestora. De exemplu testul eşuat al unei tranzacţii într-o bază de date poate avea mai multe cauze: • logica bazei de date; procedurile stocate, interog ările sau comenzile de actualizare, inserare sau ştergere conţin erori;
logica serverului de aplica ţii; există erori de proiectare sau de programare în cadrul funcţiilor implementate în serverul de aplicaţii; • logica procesării tranzacţiilor; ordinea eronat ă a efectuării unor operaţii conduce la eşuarea întregii tranzacţii; • comunicaţia; în cazul unor probleme de comunica ţie la diverse niveluri, tranzacţiile fie nu au loc, fie se realizeaz ă incomplet sau incorect. Testarea aplicaţiilor bazate pe arhit ectura Web, în plus fa ţă de testarea aplicaţiilor clasice, necesită o serie de teste specifice cum ar fi: testarea funcţională, testarea de compatibilitate, testarea conţinutului, testarea performanţelor, testarea de încărcare, testarea securităţii, testarea serverului Web, testarea serverului de aplica ţii şi testarea bazelor de date. Testarea func ţ ional ă se realizează pentru a constata dacă site-ul se compor tă conform cu specificaţiile sale. Detaliile acestui tip de testare depind de natura site-ului Web şi, în general, constă în verificarea legăturile paginilor, testarea formularelor, verificarea tranzacţiilor pentru comer ţul electronic şi pentru baze de date, testarea applet-urilor Java. Prin testarea de compatibilitate se urmăreşte aspectul şi comportamentul site-ului Web în raport cu o varietate de sisteme de operare şi de navigatoare Internet. Aceast ă testare scoate în evidenţă problemele cu controalele ActiveX, applet-urile Java, func ţiile JavaScript sau VBScript şi formulare din pagini. La ora actuală există peste 100 de combinaţii posibile între diverse sisteme de operare Windows şi diverse versiuni ale navigatoarelor Netscape şi Internet Explorer. Pentru testarea con ţ inutului se urmăreşte corectitudinea şi aşezarea în pagină a textelor, imaginilor şi fişierelor de animaţie şi video din cadrul site-ului. Prin testarea performan ţ elor se măsoar ă comportamentul site-ului Web în diverse condiţii de trafic. Testarea de încă rcare se utilizează pentru a verifica dac ă site-ul Web poate gestiona un anumit număr de utilizatori care îl acceseaz ă concurent, în limite acceptabile ca timp de r ăspuns. Testarea securit ăţ ii tranzac ţ iilor efectuate este foarte important ă pentru aplicaţiile de comer ţ electronic având în vedere faptul c ă sunt vehiculate date confidenţiale, la care dacă au acces persoane neautorizate sau r ăuvoitoare se pot produce pierderi materiale importante. •
Testarea serverului Web are în vedere testarea interac ţiunilor dintre serverul Web şi serverul de aplica ţii, verificarea integrităţii bazei de date în cadrul serverului de baze de date, verificarea faptului c ă scripturile ASP, PHP sau JSP se execută corect pe server. Testarea serverului de aplica ţ ii se realizează ţinându-se seama de caracteristicile funcţionale şi structurale ale acestuia. Se testează componentele serverului, folosind metode clasice de testare, precum şi metode de testare care iau în considerare tranzac ţiile şi comunicaţiile
asincrone dintre aceste componente. Testarea bazelor de date presupune verificarea executării corecte a interogărilor şi operaţiilor de adăugare şi actualizare a datelor, precum şi verificarea conexiunilor dintre site-ul Web şi baza de date. Testarea aplicaţiilor distribuite bazate pe arhitectura Web este realizată fie de către echipe de specialitate din cadrul departamentului de asigurare a calităţii al firmei, fie de către o firmă specializată în testare (outsourcing ). În Capitolul 10 sunt prezentate aspectele legate de testarea aplica ţiei e-DSI bazată pe arhitectura Internet.
2.7 Testarea sistemelor distribuite bazate pe componente Cele mai cunoscute tehnologii pentru dezvoltarea sistemelor distribuite bazate pe componente sunt CORBA, standardizat ă de Object Management Group, DCOM(COM+) dezvoltată de Microsoft şi RMI dezvoltată de firma Sun. Aceste tehnologii presupun existen ţa unor componente (denumite server) care, prin intermediul unor interfe ţe, pun la dispoziţie funcţii, care sunt apelate de componente client aflate pe un alt sistem. Transferul în cadrul sistemului distribuit se realizeaz ă prin intermediul unui protocol bine definit.
Descriere IDL Compilator IDL
Stub
Proxy
Server/Client
Compilator
CLIENT/ SERVER
STUB
PROXY
Figura 2.13 – Generarea componentelor
În CORBA şi DCOM interfe ţ ele sunt specificate utilizând un limbaj de definire a interfeţelor (IDL). Interfeţele în CORBA şi DCOM sunt compilate de către compilatorul IDL, respectiv MIDL, acesta generând un proxy şi un stub. Proxy-ul şi stub-ul conţin codul pentru declararea claselor la nivelul modulelor, interfeţelor şi excepţiilor definite cu (M)IDL. De asemenea este generat cod pentru încapsularea parametrilo r metodelor (marshalling/unmarshalling ). Codul stub-ului şi al proxy-ului sunt compilate împreună cu implementările clientului şi ale serverului pentru a obţine codul executabil al aplica ţiei (figura 2.13).
Cererile clientului sunt transmise c ătre server prin intermediul stubului, ORB-ului şi al proxy-ului (figura 2.14).
CLIENT
SERVER
PROXY
STUB
ORB (COM) Figura 2.14 – Execuţia cererilor clienţilor
Componentele unui sistem distribuit opereaz ă deseori pe platforme şi arhitecturi eterogene şi, de aceea, este necesar ca instrumentele de testare să opereze şi ele pe platforme şi cu arhitecturi eterogene. Complexitatea aplicaţiilor distribuite cre şte pe măsur ă ce componentele din aplicaţia software devin complicate. Testarea aplica ţiilor distribuite bazate pe componente este văzută la nivel de: • micro-model – la acest nivel se descriu paşii fiecărui test, pentru fiecare componentă în parte. Fiecare test conţine cazuri de test, pentru fiecare caz de test în parte fiind definite driverul de test, datele de test, pre-condiţiile şi post-condiţiile specifice fiecărei componente testate. • macro-model – în acest nivel se defineşte contextul în care fiecare test va fi rulat. Se are în vedere locul fizic în care se afl ă componentele. Acest nivel este orientat pe proces şi are în vedere fazele de analiză, proiectare, implementare şi livrare. Toleranţa la erori este evaluat ă prin injectarea de erori în scopul determinării posibilităţii serverului de a trata aceste situa ţii excepţionale. Pentru aceasta, trebuie determinate posibilele surse şi tipuri de erori, utilizate pentru injectarea cu erori a interfe ţei serverului [GHOS00]. În cazul aplicaţiilor bazate pe componente distribuite, erorile din fazele de proiectare şi implementare apar la definirea interfe ţei, im plementarea serverului şi în codul client.
Cele mai frecvente erori apar în descrierea interfe ţei. O greşeală des întâlnită este specificarea incorectă a direcţiei parametrilor, astfel c ă un parametru de ieşire este definit ca parametru de intrare sau invers. De exemplu se consider ă o metodă într-o interfaţă care are ca scop adunarea a două numere. Parametrii metodei sunt a şi b de intrare şi r de ieşire, declaraţia interfeţei fiind: interface ICalcule : IUnknown { //… HRESULT Suma([in] double a, [in] double b, [out] double *r); //… };
Dacă pentru parametrul de ieşire r nu se specifică atributul out, atunci deşi la compilarea interfeţei cu compilatorul MIDL nu se va constata nici o eroare, la utilizarea metodei se va constata că r nu reprezintă suma lui a şi b. Majoritatea erorilor de programare apar în implementarea serverului. Codul client poate conţine de asemenea erori de programare. Deseori apar erori în apelurile metodelor serverului, fie prin specificarea ordinii incorecte a parametrilor, fie prin utilizarea incorect ă a parametrilor. De exemplu, corespunzător interfeţei de mai sus în codul client, în care se doreşte efectuarea calculului z=x+y prin apelul metodei Suma a interfeţei ICalcul, există secvenţa: double x=30.00993, y=88.49494, z=0.0; ICalcul *pIC; //…… pIC->Suma(x,z,&y); //……
execuţia metodei Suma va avea va efect y=x+z.
În aplicaţiile distribuite există diferite surse şi tipuri de încheieri nereuşite a execuţiei. Aceste nereuşite sunt datorate unor cauze diverse, cum ar fi: • probleme în cadrul componentelor (de exemplu blocarea serverului); • probleme de comunicare în reţea (întârzieri, congestionarea reţelei); • probleme la nivelului ORB (de exemplu zona tampon a acestuia este plină); • probleme legate de securitate şi autentificare; • probleme de sincronizare. În [McGR00c] este prezentat un model de testare distribuit ă (figura 2.15), precum şi un limbaj şablon pentru testarea sistemelor distribuite. Colaborator
Driver de test
Interfaţ a obiectului de testat
Metodă releu
Jurnal de urmărire
Interfaţa de test
Interfaţa obiectului de test Obiect de testat (OUT)
Figura 2.15 – Elementele modelului de testare a
distribuite
sistemelor
Limbajul şablon pentru testarea sistemelor distribuite prezint ă următoarele caracteristici: • izolarea obiectului de testat (OUT), care ajut ă la testarea implementărilor par ţiale şi este utilă la testele unde colaboratorii sunt par ţiali; • încapsularea obiectului de testat pentru ca încapsulatorul s ă aibă aceeaşi interfaţă ca şi clasa de testat (CUT) şi metodele încapsulatorului să poată modifica mesajele înainte de retransmitere; • utilizarea metodelor încapsulate pentru control; mesajele clienţilor sunt redirec ţionate către încapsulator; încapsulatorul poate întârzia şi chiar poate reordona mesajele înainte de retransmitere către obiectul de testat; • utilizarea unui jurnal de urm ărire sincronizat; prin utilizarea jurnalelor de urmărire fiecare mesaj va avea asociat un astfel de jurnal, iar la analiza ulterioar ă se pot identifica secvenţele testate; se vor testa toate mesajele posibile; • utilizarea unui client substituit pentru c ă aceştia pot încapsula cazuri de test ca metode şi, de asemenea, clientul poate fi un server; • testarea completă a protocolului este necesar ă deoarece execuţia corectă a unei singure metode nu este de ajuns şi, de aceea, se impune testarea secvenţelor de mesaje care însoţesc o operaţie; • verificarea locală a rezultatelor testelor; trimiterea fiec ărui rezultat înapoi către driverul de test este costisitoare iar distribuirea cunoştinţelor despre teste duce la o îmbun ătăţire a performanţelor; • testarea tuturor secvenţelor posibile; utilizarea de întârzieri între metode pentru a controla secven ţa execuţiilor şi acordarea unei atenţii speciale ordinii în care se execută testele; • utilizarea unui jurnal de urm ărire distribuit; îmbun ătăţirea performanţelor se poate face prin distribuirea jurnalului de urmărire; un dezavantaj îl prezint ă necesitatea sincronizării ceasurilor maşinilor pentru acurateţea jurnalelor;