Unit testing – testarea low level a codului
Procesul de testare a produselor software este destul de complex, unul dintre motive fiind acela ca presupune mai multe niveluri ale testarii. Despre acestea am mai discutat si in trecut aici pe blog, fiind vorba de cele 3 niveluri principale ale testarii care sunt grupate in conceptul de Piramida nivelurilor testarii (automate): unit testing, testarea de integrare (integration testing) si testarea End-to-end.
Primul nivel si cel mai larg din punct de vedere al numarului de teste implicate care se foloseste in contextul testarii unui produs este reprezentat de Unit testing, despre care vom detalia in continuare.
Ce reprezinta Unit testing?
Dupa cum mentionam si anterior, testarea de tip Unit constituie baza cu care porneste procesul de testare software. Concret, Unit testing-ul reprezinta un tip de testare White Box, unde verificarea se face prin interactiunea directa cu codul astfel incat sa i se verifice functionalitatea lui primara, low level.
Prin testele de unitate, codul este ”segmentat” in mai multe mini-componente (units) si pentru fiecare sunt scrise astfel de teste, care verifica aproape linie cu linie functionalitatea acelui cod, sa se vada daca prin introducerea anumitor seturi de date este returnat rezultatul corect (sau nu).
Ca sa distingem mai bine intre nivelul de Unit testing si celelalte doua din Pirmaida testarii, ne putem imagina prepararea unei prajituri. Prin Unit testing verificam individual ingredientele de baza pe care urmeaza sa le folosim (zaharul, faina, esentele etc.), lucru extrem de important si necesar, dar nu suficient. Prin testarea End-to-end, verificam produsul final in care am folosit ingredientele precedente, adica prajitura.
Desigur, in mod ideal, daca avem ingrediente de baza de buna calitate, ne putem gandi ca si produsul final va fi de o calitate inalta, insa acest lucru nu este deloc obligatoriu (e o premisa necesara, nu si suficienta), si de aceea se impune o testare constanta, pe niveluri, a tuturor componentelor, cat mai detaliata, incepand cu Unit testing.
Cine, ce si cum testeaza folosind testele Unit?
Exista cateva aspecte foarte importante de stiut despre Unit testing, pentru a nu face eventuale confuzii si pentru a intelege mai bine acest nivel cu care incepe testarea automatizata.
Un prim lucru foarte important este cine se ocupa de scrierea testelor de Unit, iar raspunsul il reprezinta programatorii. Da, asa este, programatorii se ocupa de scrierea si mentenanta testelor Unit, si nu inginerii QA. De ce se intampla asa, cand de testare se ocupau cei de pe QA?
Raspunsul e foarte simplu. Dupa cum mentionam si in paragrafele anterioare, prin Unit testing se verifica direct codul unei anumite functionalitati, la nivelul cel mai apropiat de acesta. De aici rezulta ca programatorii sunt cei mai indreptatiti sa scrie aceste unit teste deoarece ei cunosc cel mai bine acel cod la nivel basic si stiu ce ar trebui sa faca fiecare functie/ metoda/ constructor etc.
Ar fi mult mai greu pentru un QA sa scrie acest tip de teste in primul rand pentru ca nu el a scris codul, deci ar dura extrem de mult ca timp (iar timpul inseamna energie si bani) sa invete acel cod, ca sa il poata testa ulterior low level. De aceea, eficienta Unit testing-ului e data de faptul ca developerii cunosc codul, ei il inteleg si pot sa scrie mai usor si mai rapid aceste teste.
Asta nu inseamna ca testerii (QA) nu mai au niciun rol si aici se incheie procesul de testare, dimpotriva. Unit testing este doar inceputul, si presupune ca verificam baza acelui cod dezvoltat de programatori, ca la nivel minimal si izolat face exact ceea ce trebuie.
Rolul unui QA este predominant acela de a verifica calitatea produsului in ansamblu, dintr-un punct de vedere cat mai apropiat de utilizatorul final, si intr-o maniera cat mai realista. Un user normal nu va folosi niciodata doar o functie din codul aplicatiei, de aceea programatorii scriu in 99% din cazuri aceste teste automate.
Codul este divizat in mai multe componente si testate initial separat, linie cu linei, functie cu functie, ceea ce inseamna testare de Unit independenta. Exista si situatii cand pentru a testa o anumita functie prin acest tip de teste, ea apeleaza la randul ei alta functie (sau rezultatul altei metode) si atunci deja putem vorbi de o testare de Unit integrata care aduce mai degraba a urmatorul nivel din Piramida testarii, integration testing.
Pentru scrierea de teste Unit se pot folosi foarte multe instrumente, limbaje de programare si framework-uri, unele dedicate, altele generale. Instrumentul de testare Cypress este o optiune foarte buna pentru scrierea acestor teste cu limbajul JavaScript. Si Seleniumul poate fi folosit in acest scop in combinatie cu unul din tool-urile JUnit sau TestNG care sa permita rularea acestor teste, in corelatie cu mai multe limbaje (Java, C#, Python etc.).
Exemple de teste Unit
Ca sa intelegem foarte bine ce sunt testele automatizate care compun Unit testing-ul si de ce programatorii se ocupa de scrierea lor, vom vedea cateva exemple foarte simple.
Un prim exemplu este cel al unei functii extrem de basic care nu face altceva decat sa inmulteasca 2 numere pe care noi le alegem.
Astfel, la inceput este ilustrata functia ce urmeaza a fi testata, si ulterior este declarat testul de unit denumit cat mai sugestiv ”Test the function of multiplying 2 numbers”. Sunt declarate mai apoi doua variabile (a si b) care inglobeaza doua valori alese la decizia noastra, si o constanta result care va ingloba mereu rezultatul functiei de inmultire a celor doua valori declarate anterior.
Foarte important la un test este elementul care verifica rezultatul final numit asertie, reprezentata aici de ultima linie de cod. Mai precis, aceasta verifica (se asteapta) ca rezultatul inmultirii celor doua numere alese de noi sa fie cel corect. Daca echivalenta este adevarata, testul este trecut (passed), daca este falsa atunci testul este picat (failed).
In continuare o sa mai vedem niste exemple preluate ca model din documentatia oficiala a framework-ului de testare Cypress. Practic, mai sus am vazut doar o posibila functie a unui calculator, mai jos putem vedea o suita care cuprinde functii pentru toate operatiile de baza, adunare, scadere, inmultire si impartire.
In imaginea de mai sus sunt sistematizate unitar cele 4 functii principale de calcul pentru operatiile de baza, iar in imaginea de mai jos sunt testele Unit scrise cu ajutorul framework-ului Cypress care verifica si aserteaza corectitudinea rezultatelor fiecarei functii din cele 4.
In acest caz nu este doar un test, ci o suita (o colectie) de mai multe teste care e denumita ”Unit test our math functions”, si fiecare test se poate observa in mod distinct si usor de inteles. Se poate deci observa ca aceste unit teste sunt destul de simple, scopul lor fiind verificarea functionala la nivel basic a codului.
De ce avem nevoie de Unit testing?
Unit testing-ul este o componenta importanta cu care debuteaza testarea software automatizata. Ea este realizata in primul rand de developeri pentru ca ei inteleg cel mai bine acel cod si pot dezvolta aceste teste mai eficient si rapid, si prezinta avantaje si dezavantaje, ca orice lucru din aceasta lume.
Principalul avantaj oferit de Unit testing este reprezentat de faptul ca este o testare functionala destul de rapida, care ofera siguranta ca acel cod livreaza rezultatul dorit in mod individual. Un al doilea avantaj este acela ca testele unit ofera o siguranta mai mare programatorilor sa asigure calitatea acelui cod.
Dezvoltarea de catre programatori a acestor teste ii ajuta in mod substantial sa inteleaga si mai bine codul dezvoltat pentru functionalitatea respectiva, sa depisteze mai usor eventualele erori si sa le corecteze cat mai din timp, inainte sa se ajunga la fazele urmatoare ale testarii.
Un dezavantaj al Unit testing-ului este dat de faptul ca in destul de multe situatii nu poate acoperi 100% functionalitatile si componentele testarii. Mai precis, chiar daca ne asiguram ca la nivel minimal si separat codul nostru trece acele teste, nu reprezinta o garantie ca si produsul final in ansamblu va fi unul foarte calitativ.
Revenind la metafoara noastra de la inceputul articolului, nu inseamna ca daca zaharul e de mare calitate, si prajitura finala va fi cea mai gustoasa, existand multe alte lucruri care intervin in proces si de care trebuie sa tinem cont.
Concluzii
In incheiere, nivelul de Unit testing este cel care alcatuieste baza si partea cea mai numeroasa a conceptului de piramida a testarii automate. Cele mai multe teste sunt cele de Unit pentru ca sunt extrem de mici din punct de vedere al complexitatii si verifica exact functiile separate ale codului nostru, la nivel primar.
Unit testing este o testare White box, si este realizata preponderent de programatori deoarece ei cunosc cel mai acel cod, fiind cel mai eficient mod astfel de a scrie aceste teste unitare. Ele ofera un prim nivel de calitate al viitoarelor aplicatii software, dar in niciun caz testarea nu se opreste aici, iar rolul unui QA este important in continuarea acestui proces.
Surse
Informatii generale despre Unit testing gasesti pe Guru99, pe Geeksforgeeks si AWS Amamzon.
Sfaturi si bune practici legate de Unit testing.
Diverse exemple de teste Unit.
Exemplu de Unit test oferit de Cypress.
Avantaje si dezavantaje ale Unit testing
Conceptul de Piramida a testarii si nivelul de Unit testing.
Multumesc colegilor mei Robert si Alex pentru opiniile lor despre Unit testing.