În calitate de profesionişti în testarea produselor software avem, adesea, oportunitatea de a testa produse cu grade diferite de complexitate. Cu cât gradul de complexitate al aplicaţiei, care urmează a fi supusă testelor noastre, este mai ridicat, cu atât mai atent trebuie să planificăm activităţile de testare. Iar pentru o testare cât mai de calitate, este necesară o planificare cât mai riguroasă. În mod ideal, testerul este implicat în procesul de dezvoltare al produsului software încă din stadiile incipiente ale acestuia. Asftel că, pe măsură ce se conturează definiţiile modulelor, entităţilor de date, obiectelor, claselor, funcţiilor, etc. testerul are posibilitatea să înceapă definirea scenariilor de testare ale acestora.
În mod evident, un test case bun este acela care are o mare probabilitate de a dezvălui erori sau defecte în aplicaţia aflată în testare şi, la fel de evident este că, în calitate de testeri încercăm să scriem test case-uri bune, care să dezvăluie erori și defecte. Însă, o dată cu experienţa pe care o acumulăm devenim conştienţi de faptul că nu există proces de testare care să permită identificarea tuturor defectelor posibile ce le poate conţine un produs software. Din acest motiv, dar şi pentru că, adeseori, alocarea timpului pentru validare şi testare este limitat, activităţile de testare sunt planificate în aşa fel încât să acopere anumite zone de risc, asupra cărora convenim împreună cu clientul sau alţi stakeholderi. În acest proces de identificare al riscurilor se vor defini, de asemenea, şi flow-urile de business care sunt esențiale pentru client. Flow-rile de business astfel identificate vor sta la baza test case-urilor pe care le vom crea şi care, de asemenea, ne vor ajuta să evaluăm şi să determinăm gradul de acoperire al aplicaţiei de către testele executate sau ceea ce numim test coverage.
Când vine vorba de testarea produselor software, una din convingerile mele personale este aceea că în fiecare activitate de testare trebuie să avem în vedere business logicul sistemului care va fi supus testelor noastre. Nu putem face click pe fiecare link sau buton din aplicaţie şi să considerăm la finalul acestei activităţi că am acoperit 100% toată aplicaţia în testele noastre, că am validat toate flow-urile posibile în acest mod. Procesul de testare este mult mai complex decât a acţiona câteva linkuri sau butoane într-o aplicaţie. Testarea presupune printre altele identificarea tuturor condiţiilor care afectează sau influenţează starea produsului nostru. După identificarea acestor condiţii trebuie să planificăm cu atenţie fiecare activitate de testare a acestor condiţii. Trebuie să definim teste care să execute fiecare flow, fiecare tranziţie de la o stare la alta a aplicaţiei noastre, fiecare zonă de risc.
În următoarele rânduri aş dori să expun şi să exemplific câteva principii pe care le aplic în momentul în care trebuie să creez test case-uri pentru o aplicaţie cu un business logic complex. Deoarece mintea umană învaţă şi înţelege mai bine când operează cu analogii, aş dori să folosesc ca exemplu sau studiu de caz, dacă doriţi, un change request pe care echipa noastră l-a implementat în urma cu puţin timp pentru un client de-al nostru.
Acest client deţine una dintre cele mai mari companii de distribuţie de combustibil din America Centrală, cisternele lor distribuind toate tipurile de combustibili în majoritatea statelor Americii Centrale. De-a lungul anilor am dezvoltat pentru acesta software specializat în afacerea sa, care i-a permis să crească şi să îşi extindă businessul. Produsele dezvoltate de echipa noastră deservesc diferite scopuri: vorbim despre aplicaţii destinate echipei lor de operaţiuni şi logistică, echipei de contabilitate, software adresat şoferilor pentru alimentările clienţilor lor, aplicaţii pentru inspecţiile tehnice ale cisternelor şi maşinilor din dotare etc.
Într-o zi am primit o solicitare de la acest client pentru a optimiza unul dintre principalele procese de facturare care presupune emiterea facturilor către anumite grupuri de clienţi, la care clientul nostru livrează combustibil. Aceste grupuri de clienţi deţin mii de spații fizice răspândite pe întreg teritoriul Americii Centrale, unde şoferii clientului nostru livrează combustibilul. Procesul respectiv constituie o mare parte a activităţii contabile a clientului nostru. Acest proces a fost iniţial dezvoltat pentru a executa 7 procesoare de date, unul după altul şi colecta date din diferite tabele din baza de date. Fiecare dintre aceste procesoare avea propriile sale proceduri stocate şi tabele cu care opera şi actualiza tranzacţiile în funcţie de diferite condiţii şi configurări setate la nivelul entităţilor definite în baza de date. Probabil aţi intuit deja că cea mai mare parte a logicii de business a fost introdusă în aceste proceduri stocate, la nivelul bazei de date. Sarcina noastră a fost să centralizăm acest proces şi să dezvoltăm API-urile care ar fi oferit posibilitatea reutilizării lor în alte procesoare de date pe care le avem implementate în proiect. Prin urmare, această parte a aplicaţiei era cel puţin spus, sensibilă şi avea un impact mare asupra activităţii zilnice a clientului nostru.
Efortul depus de întreaga echipă a fost colosal, întrucât termenul de livrare era destul de scurt: în două sprinturi trebuia să rescriem şi să testăm întregul proces, iar resursele noastre erau oarecum limitate.
În calitate de tester, de fapt singurul tester din echipă, sarcina mea a fost, în primul rând, să creez test case-uri pentru acest change request, astfel încât toate flow-urile să fie acoperite. Aceasta a fost de fapt cea mai cronofagă activitate şi cea mai grea sarcină a mea pentru acest change request. Aşadar, am început să lucrez la crearea test case-urilor, dar destul de curând am înţeles că trebuie să sistematizez acest proces şi să îl plănuiesc riguros, astfel încât să nu pierd nimic din vedere. Rezultatul final al acestui efort a fost undeva la 300 de test case-uri şi, de asemenea, un proces care m-a ajutat ulterior să abordez strategic acest tip de solicitări.
Mai jos vreau să sumarizez rezultatul final al acestui efort şi să expun câţiva paşi pe care i-am identificat a fi de ajutor şi pe care îi urmez ori de câte ori trebuie să creez test case-uri pentru astfel de funcţionalităţi complexe.
De nenumărate ori am avut neşansa să lucrez pe task-uri care aveau în descrierea lor o singură frază ca cerinţă, alteori nici măcar atât, singura informaţie oferită era titlul task-ului, fără vreo altă descriere în bug tracking toolul folosit. Alteori, nu primisem niciun background al problemei raportate sau locul exact de reproducere al erorii, informaţii de care aveam imperios nevoie pentru a soluţiona problema şi, în mod inevitabil, de a valida fixul. Bănuiesc că nu sunt singurul tester nefericit din această lume care a avut neşansa să opereze cu această cantitate limitată de informaţii.
Poate însă vă regăsiţi în situaţia pe care am descris-o mai sus şi trebuie să lucraţi la optimizarea unui proces deja existent. Pentru a putea testa şi valida o astfel de solicitare, va trebui în primul rând să ştiţi cum funcţionează aplicaţia/ modulul/ componenta respectivă în momentul de faţă şi ce anume face. Trebuie să înţelegeţi fiecare flow normal şi alternativ. În cazul ideal, în care aveţi posibilitatea să obţineţi toate informaţiile necesare din descrierea taskurilor sau din documentele cu specificaţii tehnice oferite de client este perfect. Însă adeseori, informaţiile din taskuri nu sunt nici pe departe suficiente pentru a demara procesul de creare al test case-urilor. În astfel de situaţii va fi nevoie să obţineţi aceste detalii în moduri mai puţin convenţionale. Puteţi solicita informaţiile dorite unei persoane care cunoaşte aplicaţia/ modulul/ componenta în detaliu, PM-ul, un developer cu mai multă experienţă pe proiect, un coleg tester care a avut deja ocazia să lucreze pe această funcţionalitate etc. Dacă nu aveţi această posibilitate, puteţi apela mereu la skillurile de detectiv pe care mulţi dintre testeri le posedă în mod natural şi să căutaţi în bug tracking tool taskuri mai vechi care descriu funcţionalitatea pe care trebuie să lucraţi. În cazul fericit în care aveţi cunoştinţe de programare, puteţi cere acces la codul sursă pe care ulterior îl puteţi studia şi încerca să înţelegeţi funcţionalitatea actuală. Atenţie însă, dacă apelaţi la această ultimă soluţie, ţineţi minte: codul sursă nu trebuie sub nicio formă considerat test oracle. (Test oracle reprezintă mecanismul prin care determinăm dacă un test case a trecut sau nu).
În concluzie, apelaţi la oricine şi orice resursă care vă poate ajuta să colectaţi toate informaţiile necesare pentru a demara procesul de creare al test case-urilor.
Mare parte din munca de zi cu zi a unui tester se rezumă la eforturile constant depuse pentru a înţelege pe deplin logica de business a aplicaţiei sau sistemului pe care îl testează. Acest lucru este valabil, fără îndoială, pentru toate procesele pe care aplicaţia aflată în testare le încorporează. Lucraţi cu ea şi înţelegeţi-o. Executaţi procesele ori de câte ori este nevoie, până înţelegeţi tot ce trebuie să ştiţi despre ea. Dar, desigur, tineţi mereu cont de restricţiile de timp cu care vă confruntaţi pe proiectele dvs. într-un sprint sau iteraţie dată.
Procesul menţionat mai sus, pe care echipa noastră a trebuit să-l optimizeze fusese deja implementat, astfel încât treaba noastră a fost să îmbunătăţim parte din infrastructura sa. Aşadar, aveam deja la îndemână procesul iniţial, astfel că l-am putut studia și executa de câte ori am avut nevoie. Dacă nu aveţi această şansă, vă sugerez să folosiţi orice documentaţie disponibilă, studiaţi-o şi, de asemenea, studiaţi sisteme similare pe care le puteţi găsi online sau pe piaţă. Dacă este nevoie şi doar ca ultim resort, cereţi ajutor. Întrebaţi un tester senior sau un developer care este mai familiarizat cu logica de business, despre sistemul pe care trebuie să îl testaţi.
Trebuie să ştiţi ce ştiţi şi ce nu ştiţi, ce aţi înţeles din cerinţele clientului şi ce anume vă este încă neclar sau lipseşte din flow. În esenţă, în această etapă, încerc să înţeleg cum se va integra noua funcţionalitate în sistem şi cum va funcţiona în ansamblu. De asemenea, în această etapă vă poate ajuta extrem de mult înţelegerea nevoii de business din spatele funcţionalităţii pe care o implementaţi. Ce anume va câştiga clientul din implementarea acestei funcţionalităţi.
În această etapă este important să aşezaţi pe hârtie tot ceea ce înţelegeţi, luarea de notiţe este un instrument puternic în sine. Mai mult decât o înregistrare audio a unei şedinţe de clarificare a cerinţelor, prefer să iau notiţe pentru că pot reveni în orice moment asupra lor şi îmi amintesc cu uşurinţă la ce anume mă gândeam în acel moment, care a fost înţelegerea mea cu privire la cerinţele clientului la acel moment.
Alte instrumente pe care le folosesc ori de câte ori se iveşte ocazia sunt diagramele. Îmi place să realizez diagrame pentru a exercita ceea ce am înţeles din cerinţele şi specificaţiile tehnice oferite de client. Nimic prea fantezist, simple scheme logice care mă pot ajuta să identific toate condiţiile de test si flow-ul de date dintr-o anumită funcţionalitate sau componentă care urmează să fie testată. Acest lucru mă ajută, de asemenea, să-mi planific test case-urile în mod logic şi cronologic.
De obicei, tot în această etapă, întocmesc o listă cu toate obiectele din baza de date care vor fi create ca urmare a noii implementări sau vor fi afectate de noua implementare, şi de care va trebui să ţin cont în test case-urile pe care le voi crea.
Această etapă este extrem de importantă atunci când intenţionăm să creăm test case-uri pentru o anumită funcţionalitate şi cu atât mai mult când este vorba de procese complexe care vor fi declanşate în culise, la un simplu click de buton. Condiţiile de test sunt vitale pentru procesul de creare al test case-urilor şi din acest motiv trebuie să le identificaţi corect pe toate. Dacă esuaţi să identificaţi vreo astfel de condiţie de test, s-ar putea ca o dată cu codul, să livraţi în producţie şi o defecţiune majoră, şi acesta este coşmarul oricărui tester. Acestea fiind spuse, este bine cunoscut faptul că fiecare test case trebuie scris în aşa fel încât să execute o astfel de condiţie de test.
În cazul meu, a trebuit să mă deplasez (în mod virtual) acolo unde era păstrată logica de business şi anume în procedurile stocate din baza de date. Deja aveam lista obiectelor de DB (identificate în etapa anterioară) care erau folosite de procesul existent la acel moment, aşa că a fost nevoie doar să le parcurg pe fiecare în parte şi să identific toate condiţiile, flow-urile normale şi cele alternative. Deşi pare uşor de îndeplinit, această etapă a fost extrem de grea deoarece mi-a consumat grozav de mult timp şi din acest motiv a fost şi teribil de obositoare. Dar este absolut necesar să extrageţi şi să identificaţi corect condiţiile de test, fie din documentaţia tehnică aflată la dispoziţia dvs., fie din vechiul sistem (legacy system) sau chiar din funcţionalitatea deja existentă.
Există o mulţime de factori care ne ajută să identificăm ce tehnică este cea mai potrivită pentru crearea test case-urilor pentru o anumită funcţionalitate care urmează să o testăm. ISTQB ne oferă următoarea listă de factori:
tipul de componentă sau sistem;
complexitatea componentelor sau a sistemului;
standarde de reglementare;
cerinţele clientului sau cerinţe contractuale;
diferite nivele de risc;
documentaţia disponibilă;
obiectivele testării;
cunoştinţele şi abilităţile testerului;
instrumentele disponibile;
timp şi buget;
modelul ciclului de viaţă al dezvoltării produsului software;
utilizarea preconizată a software-ului;
Pe baza acestor factori sau a altora care nu sunt enumeraţi mai sus, puteţi alege una sau mai multe tehnici pentru crearea test case-urilor. Indiferent de decizia dvs. trebuie să ţineţi cont de restricţiile de timp şi, de asemenea, de gradul de acoperire al funcţionalităţii sau sistemului în testele dvs. .
Eu optez, de obicei, pentru decision tables sau decision trees, deoarece aceste tehnici mă ajută să urmăresc cât mai precis flow-ul de date. Însă, în cazul dvs. , pe baza funcţionalităţii pe care trebuie să o testaţi, poate ar fi mai potrivită tehnica BVA sau equivalence partitioning. Indiferent de tehnica aleasă, trebuie să vă asiguraţi că luaţi în considerare fiecare flow posibil. În mod ideal, toate flow-urile trebuie executate cel puţin o dată.
Aceasta este etapa în care exprimaţi în cuvinte toate cunoştinţele, toate informaţiile şi înţelegerea dvs. cu privire la funcţionalitatea pe care o veţi testa. Este, de asemenea, etapa în care experienţa dvs. joacă un rol important. Trebuie să fiţi precişi şi să nu pierdeţi din vedere niciun flow. Trebuie să ţineţi cont de fiecare caz (while, if, else) care execută condiţii şi, de asemenea, ţineţi un ochi deschis pentru condiţii sau specificaţii care lipsesc.
Având în vedere volumul mare de date cu care lucrez de obicei şi logica complexă de business din spatele unui simplu click de buton, eu optez de cele mai multe ori pentru o abordare de tip scenariu, unde, de obicei, specific condiţiile anterioare, acţiunea întreprinsă de utilizator. Apoi descriu flow-ul de date care este declanşat de acţiunea utilizatorului, ce date în ce tabel ajung, ce entităţi sunt actualizate sau şterse, ce flaguri sunt setate, resetate sau actualizate etc. În acest fel mă asigur că majoritatea, dacă nu toate, flow-urile sunt acoperite şi nu există date lipsă sau date suplimentare în sistem.
Indiferent de abordarea aleasă, rezultatul final trebuie să fie test case-uri bune care să execute toate condiţiile de test şi să acopere toate flow-urile noii funcţionalităţi aflate în testare. Pentru ca acest lucru să devină realitate, trebuie să înţelegeţi businessul clientului dvs., ce face, ce vinde, ce anume încearcă să obţină cu acest nou request, cum va ajuta această funcţionalitate nouă utilizatorii, cum va fi folosită de către utilizator etc. În calitate de testeri, trebuie să ne îndeplinim task-urile zilnice, ţinând cont şi de răspunsurile la aceste întrebări şi să încercăm în permanenţă să ne îmbunătăţim abilităţile tehnice şi cunoştinţele noastre de business.
de Marin Vlada