Sunteți pe pagina 1din 20

TESTAREA APLICAŢIILOR INFORMATICE

Testare empirică

Testarea empiricǎ este un proces ce corespunde stadiilor de început ale dezvoltǎrii


producţiei de software.
În timpul proiectării şi codificării se comit erori care sunt grupate în:
- erori legate de selectarea algoritmului: algoritm incorect, sau corect dar inadecvat
problemei; algoritm mai puţin performant ca precizie sau timp necesar rezolvării
problemei; omiterea, interpretarea greşită sau incompletă a părţilor algoritmului;
validarea incorectă şi/sau incompletă a datelor de intrare; inversarea răspunsurilor
la un bloc de decizie;
- erori în definirea şi utilizarea datelor ce provin din variabile neiniţializate, formate
improprii de citire, contoare de capacitate insuficientă, neverificarea datelor de
intrare, aliniere/redefinire incorectă a câmpurilor, utilizarea cuvintelor cheie ca
variabile, variabile ilegale - formate prin concatenare sau despărţite între două
linii de program;
- erori de calcule care au ca surse: expresii complicate cu posibilităţi necontrolate
de eroare; conversii implicite de tip - cu eroare de conversie, rotunjire, trunchiere;
neinterceptarea cazurilor de depăşire/subdepăşire a intervalului definit;
- erori produse în tehnica de programare cum sunt variabile şi structuri de date
globale, acces necontrolat la zone de memorie partajate, interfeţe program -
subprogram nerespectate, pasarea constantelor ca parametri transmişi prin adresă,
pasarea parametrilor de intrare/ieşire prin valoare, automodificarea programului în
timpul execuţiei, utilizarea necontrolată a mai multor limbaje cu convenţii de apel
diferite;
- erori produse din neatenţie caz în care logica de control e defectuoasă, salt în afara
limitelor programului, condiţii logice compuse sau incorect negate, neprelucrarea
primei sau ultimei înregistrări, neluarea în considerare a posibilităţii de existenţă a
fişierelor vide, neprelucrarea erorilor de intrare/ieşire, depăşirea capacităţii stivei,
adresare incorectă, necontrolarea indecşilor;
- erori în manipularea textului program care constă în erori de interpretare - I în loc
de 1, litera "O" în loc de cifra zero, de editare de legături incorectă - module lipsă,
versiuni diferite de module obiect, versiuni incompatibile, de depanare prin
modificare de valori ale constantelor sau adreselor;
- erori în contextul execuţiei datorate memoriei dinamice insuficiente sau nealocată,
periferice neoperaţionale, comunicare defectuoasă cu sistemul de operare.
În timpul execuţiei programelor apar erori de genul:
- erori hardware, care sunt legate de contextul în care se execută un program şi care
se împart în: erori în datele de intrare, erori ce decurg din neglijarea specificului
unui limbaj sau compilator - aritmetica numerelor în calculator, modul de
implementare a tipurilor şi structurilor de date pe un limbaj dat;
- erori de încărcare a programelor şi de apelare incorectă a diferitelor periferice.
Programatorul, dupǎ eliminarea erorilor de compilare şi a erorilor de editare de
legǎturi procedeazǎ la verificarea faptului cǎ programul executǎ prelucrǎrile pentru care a fost
realizat.
Şi testarea empiricǎ presupune existenţa seturi de date de test, seturi pe care le creazǎ
programatorul sau le primeşte de la beneficiar, fǎrǎ a avea un obiectiv precis legat de gradul
de acoperire a secvenţelor executabile în program.
Rezultatul testǎrii empirice evidenţiazǎ cǎ pentru seturile de date considerate
programul efectueazǎ prelucrǎrile în mod corespunzǎtor sau apar erori. În cazul apariţiilor
erorilor programatorul acţioneazǎ direct, depanând şi reluând procesul de testare.
Atunci când se delimiteazǎ precis testarea ca etapǎ în ciclul de elaborare software se
impune efectuarea de corecţii:
- de atitudine a programatorului faţǎ calitatea textului sursǎ elaborat;
- privind separarea activitǎţii de programare de activitatea de testare;
- cautarea ca seturile de date de test sǎ acopere cât mai mult ramurile asociate
structurii programului.
Testarea empiricǎ înseamnǎ:
- lansarea în execuţie a programului cu un set de date de test;
- în cazul încheierii execuţiei se analizeazǎ rezultatele afişate;
- dacǎ rezultatele sunt complete structural se trece la verificarea corectitudinii lor;
- dacǎ rezultatele sunt incomplete se analizeazǎ lipsurile, se stabilesc secvenţele
neactivate din program, se stabilesc cauzele care n-au activat secvenţele şi se fac
corecţiile;
- se reia lansarea în execuţie a programului cu acelaşi set de date de test;
- dacǎ rezultatele sunt complete şi corecte se trece la continuarea procesului de
testare cu un alt set de date de test;
- dacǎ au fost epuizate toatre seturile de date de test şi dacǎ rezultatele obţinute din
program sunt identice cu rezultatele din specificaţii procesul de testare se
considerǎ încheiat.
Se alege
cazul de test

Lansare
aplicatie

DA executie cu NU
Se alege alt succes
caz

Analiza
Rezultate NU lipsuri
complete

DA Stabilire secvente
DA neadecvate

Mai sunt DA Rezultate NU


cazuri corecte Stabilesc
- Rez. intermediare cauzele
- Urma

NU
Se fac
Incheiere corectiile
proces
Pentru a uşura detectarea cauzelor care genereazǎ diferenţe între rezulatele
programului şi rezultatele existente în specificaţii, programatorii dezvoltǎ secvenţe care oferǎ
informaţii suplimentare prin:
- afişarea de rezultate intermediare
- afişarea urmei programului prin specificarea cu mesaje a secvenţelor executate
(etichete ale instrucţiunilor)
În procedura urmǎtoare, se contorizeazǎ elementele pozitive, elementele negative şi
elementele nule dintr-o matrice, evidenţierea urmei programului presupune:
void contorizare(int a[][NCOL], int n, int m, int s[])
{
int i, j;
s[0] = s[1] = s[2] = 0;
printf(”s[0] = s[1] = s[2] = 0\n”);
//s[0] numerǎ elementele pozitive din matrice;
//s[1] numǎrǎ elementele negative şi
//s[2] numǎrǎ elementele nule
for(i = 0; i < n; i++){
printf(”for (i = %d; %d < %d”, i, i, n);
for( j = 0; j < m; j++){
printf(”for (j = %d; %d < %d)\n”, j, j, m);
if (a[i][j] > 0){
printf(”a[%d][%d]>0\n”, i, j);
s[0] ++;
}
if (a[i][j] < 0){
printf(”a[%d][%d]<0\n”, i, j);
s[1] ++;
}
if (a[i][j] == 0){
printf(”a[%d][%d]=0\n”, i, j);
s[2] ++;
}
}
}
}
Pentru matricea a cu 3 linii si 3 coloane data de:
a[0][0] = 1; a[0][1] = 2; a[0][2] = -3;
a[1][0] = -4; a[1][1] = 0; a[1][2] = 6;
a[2][0] = 0; a[2][1] = 8; a[2][2] = -9;
se afişeazǎ:
s[0] = s[1] = s[2] = 0
for (i = 0; 0 < 3)
for (j = 0; 0 < 3)
a[0][0]>0
for (j = 1; 1 < 3)
a[0][1]>0
for (j = 2; 2 < 3)
a[0][2]<0
for (i = 1; 1 < 3)
for (j = 0; 0 < 3)
a[1][0]<0
for (j = 1; 1 < 3)
a[1][1]=0
for (j = 2; 2 < 3)
a[1][2]>0
for (i = 2; 2 < 3)
for (j = 0; 0 < 3)
a[2][0]=0
for (j = 1; 1 < 3)
a[2][1]>0
for (j = 2; 2 < 3)
a[2][2]<0
Afişarea de rezultate intermediare pentru procedura de mai sus presupune:
void contorizare(int a[][NCOL], int n, int m, int s[])
{
int i, j;
s[0] = s[1] = s[2] = 0;
printf(”s[0] = s[1] = s[2] = 0\n”);
//s[0] numerǎ elementele pozitive din matrice;
//s[1] numǎrǎ elementele negative şi
//s[2] numǎrǎ elementele nule
for(i = 0; i < n; i++){
printf(”i = %d; n = %d\n”, i, n);
for( j = 0; j < m; j++){
printf(”j = %d; m = %d\n”, j, m);
if (a[i][j] > 0){
s[0] ++;
}
if (a[i][j] < 0){
s[1] ++;
}
if (a[i][j] == 0){
s[2] ++;
}
printf(”a[%d][%d]=%d \n”, i, j, a[i][j]);
printf(”s[0]=%d;s[1]=%d;s[2]=%d;\n”,
s[0], s[1], s[2]);
}
}
printf(”i=%d; j=%d; n=%d; m=%d\n”, i, j, n, m);
printf(”s[0]=%d;s[1]=%d;s[2]=%d;\n”, s[0], s[1], s[2]);
}
Pentru matricea a cu 3 linii si 3 coloane data de:
a[0][0] = 1; a[0][1] = 2; a[0][2] = -3;
a[1][0] = -4; a[1][1] = 0; a[1][2] = 6;
a[2][0] = 0; a[2][1] = 8; a[2][2] = -9;
se afişeazǎ:
s[0] = s[1] = s[2] = 0
i = 0; n = 3
j = 0; m = 3
a[0][0]=1
s[0]=1;s[1]=0;s[2]=0
j = 1; m = 3
a[0][1]=2
s[0]=2;s[1]=0;s[2]=0
j = 2; m = 3
a[0][2]=-3
s[0]=2;s[1]=1;s[2]=0
i = 1; n = 3
j = 0; m = 3
a[1][0]=-4
s[0]=2;s[1]=2;s[2]=0
j = 1; m = 3
a[1][1]=0
s[0]=2;s[1]=2;s[2]=1
j = 2; m = 3
a[1][2]=6
s[0]=3;s[1]=2;s[2]=1
i = 2; n = 3
j = 0; m = 3
a[2][0]=0
s[0]=3;s[1]=2;s[2]=2
j = 1; m = 3
a[2][1]=8
s[0]=4;s[1]=2;s[2]=2
j = 2; m = 3
a[2][2]=-9
s[0]=4;s[1]=3;s[2]=2
i = 3; j = 3; n = 3; m = 3
s[0]=4;s[1]=3;s[2]=2
Analizând aceste secvenţe rezultǎ cǎ execuţia este corectǎ şi completǎ.
Dacǎ şirul de valori ale indicelui de control i este întrerupt atunci se cautǎ cauzele ce
genereazǎ discontinuitatea.

Testare sistematică

Dezvoltarea tehnicilor de programare a atras dupǎ sine dezvoltarea de tehnici de


testare care:
- scurteazǎ durata procesului de testare
- creşte calitatea testǎrii în sensul identificǎrii unui volum cât mai mare de erori
folosind un volum cât mai redus de seturi de date de test
- reducând volumul de cheltuieli generat de testare şi de reluare a proceselor de
corectare a textelor sursǎ
Testarea sistematicǎ presupune:
- construirea grafului sau arborescenţei asociate programului
- elaborarea de seturi de date de test care activeazǎ modulele terminale ale
arborescenţei
- utilizarea de instrumente care asistǎ procesul de testare

Se folosesc generatoarele de seturi de date de test prin analiza:


- datei de intrare
- condiţii de selecţie module care dau arborescenţa
- module terminale
Pentru programul de calculare a maximului dintre patru elemente întregi existǎ:
- programul în C:
#include <stdio.h>
void main()
{
int x1, x2, x3, x4, max;
printf(“Introduceti elementele:”);
scanf(“%d %d %d %d”, &x1, &x2, &x3, &x4);
max = x1;
if(max < x2)
max = x2;
if(max < x3)
max = x3;
if(max < x4)
max = x4;
printf(“Maximul este %d”, max);
}
- structura de tip graf asociatǎ programului de aflare a maximului este datǎ de figura
1.
citeşte (x1,x2,x3,x4,x5)

max = x1

max < x2

max = x2

max < x3

max = x3

max < x4

max = x4

scrie (max)
Figura 1. Structura graf asociatǎ programului de aflare a maximului
- structura arborescentǎ asociatǎ programului de aflare a maximului este datǎ în
figura 2.
A. citeşte (x1,x2,x3,x4)

max = x1

max < x2

max = x2

max < x3 max < x3

max = x3 max = x3

max < x4 max < x4 max < x4 max < x4

max = x4 max = x4 max = x4 max = x4

B1. scrie B2.scrie B3.scrie B4 .scrie B5.scrie B6.scrie B7.scrie B8.scrie


(max) (max) (max) (max) (max) (max) (max) (max)

Figura 2. Structura arborescentǎ asociatǎ programului de aflare a maximului


Testarea programului este simplificatǎ întrucât se urmǎreşte comportamentul acestuia
pas cu pas numai pentru valori impuse.
În structura arborescentǎ asociatǎ programului se identificǎ urmǎtoarele tipuri de
noduri:
- un nod rǎdǎcinǎ, corespunzǎtor unei secvenţe din programul (modulul) apelator
- noduri intermediare, corespunzǎtoare instrucţiunilor (secvenţelor sau modulelor)
care sunt urmate şi de alte instrucţiuni (secvenţe sau module)
- noduri terminale (frunze), ce corespund instrucţiunilor (secvenţelor sau modulelor)
care încheie o prelucrare.
- seturile de date de test care activeazǎ nodurile terminale ale arborescenţei sunt
descrise în tabelul 1:

Tabelul 1. Seturile de date de test ale programului de aflare a maximului

Valori
Traseul Max
x1 x2 x3 x4
(A, B1) 9 8 7 0 9
(A, B2) 5 4 3 8 8
(A, B3) 9 1 10 -7 10
(A, B4) 7 6 9 12 12
(A, B5) 1 7 6 -3 7
(A, B6) 8 10 7 15 15
(A, B7) -1 0 6 4 6
(A, B8) 7 10 11 12 12

Seturile de date de test din tabelul 1 s-au ales astfel încât sǎ fie respectate situaţiile
urmǎtoare de stabilire a elementului maxim, tabelul 2:
Tabelul 2. Intrǎrile şi ieşirile programului de aflare a elemntului maxim

Traseul Intrǎri Ieşiri


(A, B1) x1≥x2, x1≥x3, x1≥x4 x1
(A, B2) x1≥x2, x1≥x3, x1<x4 x4
(A, B3) x1≥x2, x1<x3, x3≥x4 x3
(A, B4) x1≥x2, x1<x3, x3<x4 x4
(A, B5) x1<x2, x2≥x3, x2≥x4 x2
(A, B6) x1<x2, x2≥x3, x2<x4 x4
(A, B7) x1<x2, x2<x3, x3≥x4 x3
(A, B8) x1<x2 <x3<x4 x4

Cu cât programul este mai complex cu atât creşte numǎrul de variante în expresia
relaţionalǎ.
Existǎ programe care optimizeazǎ secvenţele relaţionale astfel încât tabelele de decizie
sǎ conducǎ la un numǎr minim de expresii ortogonale.
Testarea funcţionalǎ şi testarea structuralǎ

Testarea funcţională cunoscutǎ şi sub numele de testare black-box 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ări 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 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. Pentru funcţia care
returnează valoare absolută a unui număr întreg, un subdomeniu este intervalul numerelor
strict negative, iar un alt subdomeniu este cel al numerelor pozitive. Dintr-un număr ridicat de
cazuri de test posibile se ajunge la două cazuri de test. Două cazuri de test bazate pe membrii
aceleiaşi partiţii dezvăluie probabil aceleaşi buguri.
Prin identificarea şi testarea unui membru al fiecărei partiţii se obţine o acoperire
„bună” cu un număr „mic” de 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.
Valorile limită includ maximul, minimul, chiar în interiorul/exteriorul limitei, valori
caracteristice şi valori eronate. Dacă un sistem funcţionează corect pentru aceste valori
speciale atunci se sperǎ cǎ el va funcţiona corect pentru toate valorile din interval.
Graficul cauză – efect este o tehnică de testare care ajută în selectarea sistematică a
unui set de cazuri de test foarte performante care corelează logic cauzele cu efectele pentru a
produce cazuri de test. Are un efect secundar benefic prin relevarea ambiguităţii şi lacunelor
în specificaţii.
În [MYER79] este prezentată o metodă 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.
Dacă ne angajăm în utilizarea acestei metode trebuie să se conştientizeze că:
- trebuie să se ştie un model şi o strategie aplicabilă care nu se foloseşte în proiect
sau măcar să se ştie despre ea;
- dacă strategia noastră funcţionează atunci trebuie să se încerce să se cuantifice
astfel încât să se foloseascǎ în mod regulat;
- dacă nu funcţionează atunci cumva trebuie schimbat modelul şi strategia.
Testarea funcţionalǎ încearcă să găsească erorile din următoarele categorii:
- funcţii incorecte sau care lipsesc;
- erori de interfaţă;
- erori în structuri de date sau la accesul bazelor de date externe;
- erori de performanţă;
- erori de iniţializare şi terminare.
Testele black-box ajută la detectarea abaterilor obiectului de test de la specificaţiile
sale.
Testarea structuralǎ cunoscutǎ şi sub denumirea de testarea white-box este una dintre
cele mai importante metode de test. Pentru un număr limitat de căi de program, care de obicei
este suficient in practică, testul permite manipularea corectă a structurilor de date şi
examinarea comportamentului de intrare/ieşire a obiectelor de test.
În testarea white-box activităţile testului implică nu numai verificarea
comportamentului de intrare/ieşire ci şi examinarea structurii interne a obiectului de test.
Scopul este de a determina, pentru fiecare cale posibilă prin obiectul de test,
comportamentul obiectului de test faţă de data de intrare.
Cazurile de test sunt selectate pe baza informaţiilor despre structura fluxului de control
a obiectului de test.
Selecţia cazurilor de test trebuie sa aibă în vedere următoarele:
- fiecare modul şi funcţie a obiectului de test trebuie invocată cel puţin o dată
- fiecare ramificaţie trebuie să fie luată cel puţin o dată
- trebuie urmate cât mai multe căi posibile
Este important să se garanteze că fiecare ramificaţie a fost parcursǎ. Nu este suficient
numai sǎ asigurăm că fiecare declaraţie este executată, deoarece erorile în ramificaţiile binare
s-ar putea să nu fie găsite, pentru cǎ nu toate ramificaţiile au fost testate.
Este important să avem în vedere că, chiar si pentru programele foarte bine structurate,
în practică este imposibil sa testezi toate căile posibile; toate secvenţele de declaraţii posibile
ale unui program [PRES87], figura 3
Principalele măsuri white-box sunt: acoperirea declaraţiilor, acoperirea deciziilor şi
acoperirea condiţiilor.
Acoperirea declaraţiilor (Statement coverage) cunoscută şi sub denumirea de:
acoperirea liniei (line coverage), acoperirea segmentului (segment coverage) şi acoperirea
blocului de bază (basic block coverage). Acoperirea blocului de bază este asemănătoare
acoperirii declaraţiei exceptând unitatea de măsură a codului care este fiecare secvenţă de
declaraţii neramificate.
Avantajul principal al acestei măsuri este că se aplică direct pe codul obiectului şi nu
cere procesarea codului sursă.
Principalul dezavantaj al acoperirii declaraţiei este că ea este insensibilă la unele
structuri de control.
Acoperirea declaraţiei nu anunţă dacă buclele ating condiţia lor de terminare ci numai
dacă corpul buclei a fost executat.
Această măsură este afectată mai mult de declaraţiile implicate în calcule decât de
decizii.
Figura 3. Organigrama de control: pentru 10 iteraţii
sunt aproape un milion de căi [PRES87]

Acoperirea deciziilor (Decizion coverage) anunţă dacă expresiile booleane testate în


structurile de control (cum este declaraţia if sau while) evaluează atât adevărat cât şi fals.
Întreaga expresie boolean este considerată un predicat adevărat sau fals indiferent dacă
conţine operatorii “şi” logic sau “sau” logic.
Este cunoscută şi ca: acoperirea ramificaţiei (branch coverage) sau acoperirea căilor de
bază (basis path coverage).
Are avantajul simplicităţii fără problemele de la acoperirea declaraţiilor.
Un dezavantaj al acestei măsuri este că ignoră ramificaţiile din interiorul expresiilor
logice care apar datorită operatorilor de scurtcircuit. În următoarea secvenţă de cod:
if (conditie1 && (conditie2 || functie1())
declaratie1;
else
declaratie2;
Această măsură considerǎ completă structura de control fără a apela functie1. Expresia
este adevărată când conditie1 şi conditie2 sunt adevărate şi expresia este falsă când conditie1
este falsă. Deci operatorii de scurtcircuit exclud apelul lui functie1.
Acoperirea condiţiilor (Condition coverage) anunţă rezultatul adevărat sau fals pentru
fiecare sub-expresie logică (dacă apare), separată prin “şi” logic şi “sau” logic. Această
măsură este similară cu acoperirea deciziei cu deosebirea că are o mai bună senzitivitate
pentru controlul fluxului.
Acoperirea condiţiilor multiple (Multiple condition coverage) anunţă dacă apare
fiecare combinaţie posibilă de sub-expresii logice. La fel ca acoperirea condiţiilor, sub-
expresiile sunt separate prin “şi” logic şi “sau” logic. Un dezavantaj al acestei măsuri este că
devine plictisitor să determini setul minim de cazuri de test necesare, în special pentru
expresiile logice foarte complexe. Un alt dezavantaj este că numărul cazurilor de test necesare
variazǎ considerabil între condiţii cu aceeaşi complexitate.
Acoperirea condiţiei/deciziei (Condition/decision coverage) este o măsură hibrid
compusă prin unirea acoperirii condiţiei şi acoperirii deciziei. Are avantajul simplităţii dar
fără scurtcircuitarea componentelor sale.
Metodologiile de proiectare a cazurilor de test discutate se combinǎ într-o strategie
generală. O strategie este rezonabilă conform [MYER04] dacǎ:
• specificaţiile conţin combinaţii ale condiţiilor de intrare, se începe cu graficul
cauză-efect;
• în orice eveniment, se utilizează analiza valorilor limită; analiza valorilor limită
păstrează un set suplimentar de condiţii test, dar, multe sau toate acestea sunt
integrate în testele cauză-efect;
• se identifică clasele de echivalenţă valide şi invalide pentru intrări şi ieşiri, şi dacă
este necesar cazurile de test suplimentare identificate mai sus;
• se utilizează tehnica de presupunere a erorilor pentru a adăuga noi cazuri de test;
• examinarea logică a programului cu privire la setul cazurilor de test; se utilizează
criteriul de acoperire a deciziilor, acoperire a condiţiilor, acoperirea
decizie/condiţie sau acoperirea condiţiilor multiple; dacă criteriul de acoperire nu
a întâlnit cazurile de test identificate în primii 4 paşi şi dacă întâlnirea criteriului
nu este imposibilă, se adaugă suficiente cazuri de test pentru a satisface criteriu.
Utilizarea acestei strategii nu va garanta că toate erorile sunt găsite, însă reprezintă un
compromis rezonabil.
Standardizarea structurii documentaţiei obţinute în procesul de testare permite ca
fiecǎrei clase de erori depistate sa-i corespundǎ o procedurǎ bine definitǎ de depanare,
eliminânduse în acest fel iterativitatea pe o aceeaşi eroare.

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ă.
Numeroase aplicaţii complexe se prezintă sub formǎ de 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 şi anume:
- 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 sunt 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 de 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 îndeplinesc
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 de 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.
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; 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 tehnicilor
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ă.
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 sunt reutilizate ulterior şi pentru
testarea subclaselor. Fiecare subclasă este rezultatul combinării structurii şi comportamentului
claselor părinţi cu atribute şi metode noi
Testare pe niveluri

Aplicaţiile informatice distribuite, în marea lor majoritate au asociatǎ o structurǎ


arborescentǎ, iar evidenţierea corectitudinii prin testare se realizeazǎ printr-o metodǎ
combinatǎ mixtǎ de tip bottom-up / top - down.
Se identificǎ un nivel din structura arborescentǎ, numit nivel de bazǎ, care include
module de prelucrare.
Se testeazǎ mai întâi modulele aparţinând nivelului de bazǎ.
Se trece, conform metodei bottom-up la testarea din aproape în aproape a modulelor de
pe nivelurile superioare, dupǎ s-au produs agregǎri.
Procesul de testare bottom-up se întrerupe cu câteva niveluri înainte de a se ajunge la
modulul principal, nod rǎdǎcinǎ în structura arborescentǎ.
Procesul de testare se continuǎ cu modulele de pe nivelurile inferioare nivelului
considerat de bazǎ, dupǎ metoda top – down de testare.
În aceste situaţii se extrag din seturile de date de test acele elemente care activeazǎ
strict modulele implecate.
Încheierea procesului de testare presupune reluarea testǎrii bottom-up pentru toate
nivelurile.
Testarea finalǎ înainte de implementare presupune derularea unui proces top – down
cu toate seturile de date de test din specificaţii, într-un flux complet.
Strategia top-down porneşte cu modulul iniţial - modulul rǎdǎcinǎ al programului.
După acesta, nu există nici o procedură concretă pentru selectarea următorului modul ce va fi
testat incremental; singura regulă este că următorul modul pentru a fi eligibil trebuie să aibe
cel puţin unul dintre modulele subordonate lui, apelate de el, testate anterior, figura 4.
Testarea top-down presupune:
- modulul de control este implementat şi testat la început;
- modulele importate sunt reprezentate prin module surogate;
- surogatele au aceeaşi interfaţă ca modulele importate şi simulează comportamentul
intrării/ieşirii lor;
- după testul modulului de control, toate celelalte module ale sistemelor software
sunt testate în acelaşi mod; operaţiile lor sunt reprezentate prin proceduri surogate
până când dezvoltarea a progresat suficient pentru a permite implementarea şi
testarea operaţiilor;
- testul avansează treptat cu implementarea; implementarea şi scenele fuzionează, şi
testul de integrare a subsistemelor devine inutil.
Top

Down

Figura 4. Strategia de testare top-down


Avantaje metodei de testare top-down sunt:
- erorile de proiectare sunt detectate cât mai devreme posibil, economisind timpul de
dezvoltare şi costurile deoarece corecţiile în proiectarea modulului sunt făcute
inaintea implementării;
- caracteristicile sistemului software sunt evidente de la început, lucru ce permite un
test simplu al stării de dezvoltare şi aprobarea de către utilizator;
- sistemul software este testat complet de la început cu cazuri de test fără a fi nevoie
de medii de test costisitoare.
Dezavantaje metodei de testare top-down sunt:
- testarea top-down se dovedeşte a fi extrem de dificilă deoarece proiectarea de
obiecte-surogat utilizabile se dovedeşte foarte complicată, în special pentru
operaţii complexe;
- erorile în nivelul inferior al ierarhiei sunt foarte greu de localizat.
Acest tip de testare este în concordanţǎ cu proiectarea top-down şi presupune trecerea
de la general spre particular, bazatǎ pe existenţa unei experienţe foarte bogate a echipei de
testare care ştie cum se propagǎ erorile în cadrul unei structuri aborescente de la vârf cǎtre
bazǎ.
Testarea bottom-up este opusul testării top-down; avantajele testării top-down devin
dezavantajele testării bottom-up şi dezavantajele testării top-down devin avantajele testării
bottom-up.
Strategia bottom-up începe cu modulele terminale ale programului. După ce aceste
module au fost testate, nu există nici o procedură concretă pentru selectarea următorului
modul ce va fi testat incremental; singura regulă este că următorul modul, pentru a fi eligibil,
trebuie să aibe cel puţin unul dintre modulele subordonate lui, apelate de el, testate anterior,
figura 5.
Testarea bottom-up presupune:
- prima dată sunt testate acele operaţii care nu necesită alte componente ale
programului; apoi este testată integrarea lor intr-un modul;
- după testarea modulului este testată integrarea mai multor module testate într-un
subsistem şi tot aşa până când în final este testată integrarea finală a tuturor
subsistemelor în sistemul global.
Up

Bottom

Figura 5. Strategia de testare bottom-up


Avantajele metodei de testare bottom-up sunt:
- metoda de test bottom-up este solidă şi demonstrată; obiectele ce sunt testate sunt
cunoscute în detaliu; este simplu să se defineascǎ cazuri de test şi date de test
relevante;
- abordarea bottom-up este, din punct de vedere psihologic, mult mai satisfacătoare
deoarece testerul este sigur că baza pentru obiectele de test a fost testată în detaliu.
Dezavantajele metodei bottom-up sunt:
- caracteristicile produsului finit sunt cunoscute numai după completarea întregii
implementări şi testări, ceea ce înseamnă că erorile de proiectare din nivelele înalte
sunt detectate foarte târziu;
- testarea nivelurilor individuale impun de asemenea costuri mari pentru furnizarea
unui mediu de test potrivit.
Testare bottom-up este în concordanţǎ cu proiectarea bottom-up care presupune
trecerea de la modulele particulare spre componente derivate, bazatǎ pe existenţa unei foarte
bune cunoaşteri a secvenţelor din module şi pe rezultatelor proceselor de testare în detaliu,
trecerea spre nivelurile superioare efectuânduse din aproape în aproape, pe mǎsurǎ ce efectele
de pe nivelurile inferioare sunt gestionate astfel încât sǎ nu producǎ perturbaţii în procesul de
agregare.

Alte metode de testare

Nu toţi dintre testerii aplicaţiilor software citesc codul sursă, dar conceptul studierii
codului programului ca parte a acţiunii de testare este cu siguranţă acceptat în mare măsură.
Inspectarea codului şi examinarea superficială a programului (walkthrough) sunt două
metode principale ale testării umane şi fac parte din aşa numita testare staticǎ a aplicaţiilor.
Tehnicile testării umane sunt destul de eficace în găsirea erorilor – aşa că fiecare
proiect de programare trebuie să utilizeze una sau mai multe dintre aceste tehnici. Aceste
metode trebuie aplicate între timpul codării programului şi timpul începerii testelor bazate pe
computer. Ele contribuie substanţial la productivitatea şi fiabilitatea programului.
Inspecţie şi examinările superficiale sunt mult mai eficace, decât procesul citirii
programului înainte de testare de către programator, deoarece în aceste procese sunt implicate
şi alte persoane în afara autorului programului.
În general, aceste metode sunt eficiente în găsirea unui procent de 30% până la 70%
din erorile logice de proiectare şi de codificare din programe. Ele nu sunt eficiente în
detectarea erorilor de proiectare la nivel înalt cum sunt erorile descoperite în procesul de
analiză a cerinţelor.
Inspectarea codului sursă este o tehnică utilă pentru localizarea erorilor de proiectare
şi implementare. Multe erori sunt uşor descoperite dacă autorul va citi programul cu suficientă
atenţie.
Ideea din spatele inspecţiei codului este ca autorul sǎ discute pas cu pas programul cu
alţi ingineri software.
O echipă de inspectare a codului în mod normal este alcătuită din patru persoane
[FAGA76]:
P1. moderatorul - un programator competent care nu este implicat în proiect şi nu este
familiarizat cu detaliile programului; este asemănător unui inginer de control a
calităţii; responsabilităţile moderatorului includ:
- planificarea şi distribuirea materialelor pentru inspecţie;
- conducerea inspectării;
- înregistrarea tuturor erorilor găsite;
- să urmărească ca erorile să fie corectate ulterior;
P2. programatorul obiectului de test;
P3. programatorul responsabil cu proiectarea - dacă e diferit de programator;
P4. persoana responsabilă cu testarea.
Sarcina echipei de inspecţie este de a detecta şi nu de a corecta erorile. În timpul
sesiunii de inspecţie a codului, se petrec două activităţi:
A1. programatorul citeşte, declaraţie după declaraţie, logica programului; în timpul
expunerii, ceilalţi participanţi ridică întrebări şi urmăresc dacă există erori;
probabil că programatorul, mai mult decât ceilalţi membri ai echipei, găseşte cele
mai multe erori în timpul acestei relatări; cu alte cuvinte, simplul act de citire a
unui program în faţa unui public pare să fie o tehnică de detectare a erorilor
remarcabil de eficace;
A2. programul este analizat luând în considerare lista de erori comune în programare.
După inspectare, programatorul este deprins cu lista de erori găsite. Dacă au fost găsite
mai multe erori sau dacă oricare dintre erori cere o corecţie substanţială, moderatorul face
aranjamentele pentru o reinspectare a programului după ce erorile au fost corectate. Erorile
găsite sunt analizate, clasificate şi utilizate pentru perfecţionarea listei de erori ce va fi folosită
pentru a îmbunătăţii eficienţa inspecţiilor viitoare.
Acest proces de inspectare, de obicei, se concentrează pe descoperirea erorilor nu pe
corectarea lor.
Programatorul va începe munca de corectare a erorilor descoperite numai după
terminarea procesului de inspectare.
Marile programe trebuie examinate prin mai multe inspectări, fiecare inspecţie fiind
făcută pe una sau mai multe subrutine sau module.
Pentru ca procesul de inspectare să fie eficient, trebuie stabilită o atitudine
corespunzătoare. Dacă programatorul vede inspectarea ca pe un atac asupra sa şi adoptă o
atitudine defensivă, procesul va fi ineficient. Mai degrabă, programatorul trebuie să abordeze
procesul cu o atitudine pozitivă şi constructivă: obiectivul inspecţiei este de a găsi erorile în
program, îmbunătăţindu-se astfel calitatea muncii.
Procesul de inspectare a codului reprezintă o cale de a identifica mai devreme cât mai
multe dintre secţiunile predispuse la erori ale unui program, ajutând la concentrarea cât mai
mare a atenţiei pe aceste secţiuni în timpul procesului de testare bazat pe calculator.
Lista de erori urmărite în inspectare este în mare măsură independentă de limbaj. Se
completeazǎ această listă cu erori specifice limbajului şi cu erori detectate după procesul de
inspectare.
Erori de referire a datelor se referǎ dacǎ:
- o variabilă referinţă are o valoare care este nesetată sau neiniţializată; probabil
aceasta este cea mai frecventă eroare de programare; ea apare într-o mare varietate
de circumstanţe; pentru fiecare referire la un articol de date variabilă, element al
unui vector sau câmp într-o structură se încearcă să se "dovedeascǎ" neprotocolar
că articolul are o valoare la acel punct;
- pentru toate referiţele unui vector, este fiecare valoare a indicilor în interiorul
limitei definite;
- pentru toate referinţele unui vector, fiecare indice are o valoare întreagă; aceasta nu
este neapărat o eroare în toate limbajele, dar este o practică periculoasă;
- pentru toate referirile prin pointer sau variabile referinţă, este memoria referită
alocată; aceasta este cunoscută ca problema “referirii izolate”; apare în situaţiile în
care timpul de viaţă al pointerilor este mai mare decât timpul de viaţă a memoriei
referite;
- atunci când o suprafaţă de memorie are nume alias cu atribute diferite, valoarea
dată în această suprafaţă are atributele corecte când este referită prin unul din aceste
nume; un program FORTRAN conţine o variabilă reală A şi o variabilă întreagă B;
ambele variabile au construite aliasuri pentru aceeaşi zonă de memorie prin
utilizarea instrucţiunii EQUIVALENCE; dacă programul memorează o valoare în
A şi apoi referă variabila B, o eroare este probabil să apară deoarece calculatorul va
utiliza o reprezentare „floating - point” în zona de memorie ca un întreg;
- valoarea unei variabile are un tip sau atribut diferit de ceea ce aşteaptă
compilatorul; această situaţie apare când un program citeşte o înregistrare în
memorie şi o referă prin utilizarea unei structuri, dar reprezentarea fizică a
înregistrării este diferită de definiţia structurii;
- există probleme de adresare explicită sau implicită dacă, pe maşina utilizată,
unităţile de memorie alocate sunt mai mici decât unităţile de memorie adresate;
- sunt utilizate variabile pointer sau referinţă, locaţia de memorie referită are
atributele aşteptate de compilator; un pointer C++ stabilit pe o structură de date este
asignat cu adresa unei structuri de date diferite;
- o structură de date este referită în mai multe proceduri sau subrutine, este structura
definită identic în fiecare procedură;
- pentru limbajele orientate-obiect, sunt toate cerinţele de moştenire întâlnite în clasa
implementată.
Erori de declarare a datelor se referǎ dacǎ:
- au fost toate variabilele declarate explicit; o astfel de absenţă nu este neapărat o
eroare, dar este o sursă comună de necazur; dacă o subrutină de program primeşte
ca parametru un vector, şi nu reuşeşte să definească parametru ca un vector, o
referinţă către vector este interpretată ca un apel de funcţie, conducând la încercarea
maşinii să execute vectorul ca pe un program;
- toate atributele variabilelor nu sunt stabilite explicit în declaraţie, sunt valorile
default ale acestora bine înţelese;
- atunci când o variabilă este iniţializată într-o instrucţiune declarativă, este ea
iniţializată corect; în multe limbaje, iniţializarea vectorilor şi şirurilor este oarecum
complicată şi, deci, predispusă erorilor;
- are fiecare variabilă asignată lungimea şi tipul de dată corect;
- este iniţializarea unei variabile compatibilă cu tipul său de memorie; dacă o
variabilă într-o subrutină din FORTRAN trebuie să fie reiniţializată de fiecare dată
când subrutina este apelată, ea trebuie iniţializată cu o declaraţie de asignare mai
degrabă decât cu o declaraţie DATA;
- există variabile cu nume similare, volt sau volts; aceasta nu este neapărat o eroare,
dar trebuie văzut ca o avertizare pentru că numele sunt confundate undeva în
interiorul programului.
Erori de calcul se referǎ dacǎ:
- există calcule cu variabile ce au tipuri de date nepotrivite;
- există operaţii de calcul mixte; este adunată o variabilă reală cu o variabilă de tip
întreg; astfel de operaţii nu sunt neapărat erori dar ele trebuie cercetate cu atenţie
pentru a ne asigura că regulile de conversie ale limbajului sunt bine înţelese;
- există operaţii de calcul ce utilizează variabile cu acelaşi tip de date dar lungimi
diferite;
- este tipul de date a unei variabile într-o atribuire mai mic decât tipul de dată sau
rezultatul expresiei din partea dreaptă;
- este posibilă o depăşire de limită superioară sau inferioară în timpul calculării unei
expresii; aceasta înseamnă că rezultatul final pare a avea o valoare validă, dar un
rezultat intermediar este prea mare sau prea mic pentru tipurile de date ale
limbajului;
- este posibil ca divizorul unei operaţii de împărţire să fie zero;
- este posibil ca valoarea unei variabile să depăşească sensul domeniului; declaraţia
de atribuire a unei variabile numită “probabilitate” s-ar putea verifica pentru a se
asigura că valoarea asignată va fi întotdeauna pozitivă şi mai mare ca 1.0;
- pentru expresiile ce conţin mai mult de un operator, este corectă presupunerea
ordinii de evaluare şi precedenţă a operatorilor.
Erori de comparaţie se referǎ dacǎ:
- există comparaţii între variabile ce au tipuri de date diferite, cum sunt comparaţiile
dintre un caracter dintr-un şir cu o adresă, dată sau număr;
- există comparaţii între variabile de lungimi diferite; dacă există, trebuie să ne
asigurăm că regulile de conversie sunt foarte bine înţelese;
- sunt folosiţi corect operatorii de comparare; programatorii în mod frecvent
confundă astfel de relaţii ca: cel mai mare, cel mai mic, mai mare decât, nu este
mai mic decât, mai mic decât sau egal;
- starea fiecărei expresii logice este cea presupusă; programatorii fac adesea greşeli
când scriu expresii logice ce conţin: şi, sau sau not;
- pentru expresiile ce conţin mai mult de un operator boolean, este corectă
presupunerea despre ordinea de evaluare şi precedenţă a operatorilor;
- calea în care compilatorul evaluează expresiile logice afectează programul;
declaraţia if((x==0 && (x/y)>z) este acceptabilă pentru compilatoarele care
termină testul de îndată ce o parte a operatorului şi este falsă, dar cauzeazǎ o eroare
de împărţire la zero cu alte compilatoare.
Erori de control a fluxului se referǎ dacǎ:
- fiecare buclă se va termina în cele din urmă;
- programul, modulul sau subrutina se va termina;
- este posibil ca, datorită unei condiţii pe intrare, o buclă să nu se execute niciodată;
- pentru o buclă controlată prin iteraţie cât şi printr-o condiţie - o buclă de căutare,
care sunt consecinţele ieşirii din buclă prin epuizare (fall-through); adică ce se va
întâmpla în următorul pseudo-cod dacă NOTFOUND nu devine niciodată fals:
DO i = 1 to TABLESIZE WHILE (NOTFOUND)
- limbajul conţine un concept de declaraţii de grupuri sau blocuri de cod (do-while
sau {…}), există un while explicit pentru fiecare grup şi există un do
corespunzător; sau există acolada închisă pentru fiecare acoladă deschisă; marea
majoritate a compilatoarelor din ziua de azi depistează astfel de erori automat;
- există decizii incomplete; adică, dacă valorile unui parametru de intrare sunt 1, 2 şi
3, presupunerea logică că trebuie să fie 3 dacă nu e 1 sau 2 este validă.
Erori de interfaţă se referǎ dacǎ:
- numărul de parametrii recepţionaţi de un modul este egal cu numărul de argumente
trimise la apelarea modulului; parametrii sunt în ordinea corectă;
- atributele - tipul de dată sau dimensiunea fiecărui parametru corespund atributelor
argumentului corespunzător;
- unităţile de sistem pentru fiecare parametru corespund unităţilor de sistem ale
argumentului corespunzător; parametru este exprimat în grade dar argumentul este
exprimat în radiani;
- numărul de argumente transmise de un modul către un alt modul este egal cu
numărul de parametrii aşteptaţi;
- atributele fiecărui argument transmis către alt modul se potrivesc cu atributele
corespunzătoare parametrului din acel modul;
- unităţile de sistem ale fiecărui argument transmise unui alt modul se potrivesc cu
unităţile de sistem ale parametrului corespunzător din acel modul;
- există subrutine care modifică parametru de intrare deşi se doreşte a fi numai o
valoare de intrare;
- sunt prezente variabile globale, ele au aceeaşi definiţie şi atribute în toate modulele
care le referă;
- sunt constantele trecute ca argumente.
Erori de intrare/ieşire se referǎ dacǎ:
- fişierele sunt declarate explicit, sunt atributele lor corecte;
- atributele unei declaraţii OPEN a unui fişier sunt corecte;
- specificaţiile de format sunt în armonie cu informaţiile din declaraţiile de I/O;
- există suficientă memorie disponibilă pentru a păstra fişierul pe care programul îl
va citii;
- au fost toate fişierele deschise înainte de a fi utilizate;
- au fost toate fişierele închise după utilizare;
- sunt condiţiile de sfârşit de fişier detectate şi tratate corect;
- sunt erorile de I/O tratate corect.

Examinarea superficială (walkthrough), la fel ca inspectarea codului, constă dintr-un


set de proceduri şi tehnici de detecţie a erorilor pentru un grup ce citeşte codul sursă. Are
foarte multe în comun cu procesul de inspectare a codului, dar procedurile sunt oarecum
diferite şi se foloseşte o altă tehnică de detecţia a erorilor.
Echipa de examinare este formată din trei până la cinci persoane. Una dintre aceste
persoane joacă un rol similar cu cel al moderatorului din procesul de inspectare, o altă
persoană joacă rolul de secretar, persoana care citeşte toate erorile găsite şi a treia persoană
joacă rolul testerului.
Conform [MYER04] echipa de examinare este formată şi astfel:
P1. un programator foarte experimentat;
P2. un expert în limbajul de programare;
P3. un programator nou;
P4. persoana care în cele din urmă va menţine programul;
P5. o persoană de la un alt proiect;
P6. o persoană din aceeaşi echipă de programare ca programatorul.
Procedura iniţială este identică cu cea de la procesul de inspectare: participanţii
primesc materialele cu câteva zile înainte pentru a se familiariza cu programul. Persoana
desemnată ca tester vine la întâlnire cu un mic set de cazuri de test – seturi de intrări
reprezentative şi ieşirile aşteptate pentru program sau modul. În timpul întâlnirii, fiecare caz
de test este executat mental. Asta însemnând că datele de test sunt plimbate prin logica
programului. Starea programului, adică, valorile variabilelor, este monitorizată pe hârtie.
Desigur că, cazurile de test trebuie să fie simple şi puţine ca număr, deoarece
persoanele execută programele la o rată mult mai inceată decât calculatoarele. Deci, cazurile
de test nu joacă un rol critic; mai degrabă, ele servesc ca instrument pentru realizarea startului
şi pentru interogarea programatorului despre logica şi presupunerile făcute în program.
În marea majoritate a examinărilor, cele mai multe erori sunt găsite în timpul
procesului de chestionare a programatorului şi nu direct prin cazurile de test.
Ca şi în cazul inspecţiei, atitudinea participanţilor este critică. Comentariile trebuie
direcţionate mai degrabă către program decât spre programator. Cu alte cuvinte, erorile nu
sunt văzute ca slăbiciuni ale persoanei care le comite.
Un avantaj al examinării superficiale, constă în costuri mai mici la depanare - când o
eroare este găsită ea este de obicei localizată cu precizie în cod. Testele bazate pe calculator,
pe de altă parte, expun în mod normal numai un simptom al erorii (programul nu se termină
sau programul afişează un rezultat fără sens) şi erorile în mod normal sunt detectate şi
corectate una câte una.