7 idei pentru un cod de testare automata eficient
Atunci cand ajungem intr-o pozitie de Automation QA prin angajare sau promovare dinspre sfera de testare manuala, una dintre cele mai importante activitati devine scrierea de cod pentru cazurile noastre de test ce trebuie automatizate. Aceasta este o activitate foarte faina, interesanta, dar care vine la pachet cu provocarile ei inerente.
Mai ales la inceput nu e deloc usor sa scriem un cod prea bun (cod „perfect” nu va exista niciodata), si suntem tentati sa facem anumite greseli sau lucruri care ne vor ingreuna proiectul in timp. De aceea, e recomandat sa urmam anumite sfaturi si idei care ne vor ghida la modul universal in a scrie un cod eficient pentru testare automata.
Acum ceva timp am vorbit de bune practici in testarea automata, ce tin de procesele generale de organizare a testarii. Acuma ne vom referi mai specific la scrierea efectiva de cod pentru automation.
7 idei pentru un cod de testare automata eficient
In cele ce urmeaza vom discuta un numar de sapte idei ce le consider necesare si importante daca vrem sa scriem un cod pentru niste teste automate care sa fie eficient, usor de inteles, reutilizabil si sa i se poata face mentenanta cat mai facil posibil. Aceste idei le-am invatat treptat in ultimul an de cand contribui pe partea de testare automata, practica efectiva ajutandu-ma sa le descopar si sa le pot sintetiza in aceasta forma pe blog.
1. Parametrizarea tuturor variabilelor
O prima idee ce mi-a fost explicata inca de la primele linii de cod scrise a fost aceea de a incerca sa nu adaug date de test efective in pasii si metodele din codul de automatizare. Aceasta situatie se cheama propriu-zis hardcodarea datelor in cod (”hardcoded test data”), si trebuie evitata din mai multe motive.
In primul rand, codul nostru devine mult mai greu de updatat in viitor daca vrem sa schimbam datele respective din toate locurile unde se afla ele plasate in cod. Presupunem ca avem un test automat care intr-un pas cauta un articol vestimentar pe un site de e-commerce, daca acolo am trecut efectiv ”T-Shirt” si ulterior vrem sa cautam ”Shoes”, atunci trebuie sa inlocuim manual din toti pasii respectivi, din toate testele acest element de test.
In loc de asta, solutia este sa parametrizam pasul sau metoda respectiva de cautare cu o variabila, sa zicem ipotetic shopArticle=”T-shirt”, pe care o punem in toti pasii din testele noastre de Search, iar daca vrem sa schimbam articolul, doar inlocuim valoarea din acea variabila si se updateaza peste tot ulterior. Eficienta este mult sporita astfel.
Un al doilea motiv pentru care e bine sa evitam pe cat posibil datele hardcodate in proiectul nostru este din ratiuni de securitate. De exemplu, la testele caracteristice functiei de login pe anumite aplicatii in mod evident folosim date de test precum usernames (sau adrese de email) si parole. Acestea e bine sa fie stocate in fisiere dedicate, eventual encriptate si parametrizate de asemenea pentru a putea fi updatate usor in toate locurile din proiect daca situatia o cere.
Desigur, la inceput e mult mai usor sa iei cu copy-paste anumite test data si sa le pui direct in pasi sau metode, dar nu e recomandat, iar timpul a demonstrat ca e mult mai usor sa fie parametrizate toate aceste elemente pentru o mentenanta mai precisa a testelor.
2. Reutilizarea codului deja scris
A doua idee din aceasta lista ce merita mentionata pentru a eficientiza cat mai bine codul nostru pentru testare automata este aceea de a incerca sa refolosim codul deja scris in proiect, in loc sa adaugam noi totul de la 0.
Acest din urma lucru este extrem de prezent mai ales la inceputul carierei, cand din dorinta de a experimenta lucruri noi si de a scrie cod, avem tendinta de a lua totul de la 0, de la cele mai de baza chestii ce le construim pentru a automatiza un caz de testare, in loc sa aruncam mai intai o privire in proiect si sa vedem daca un anumit element de care avem nevoie (locator, metoda, pas de test etc.) nu este deja construit.
Desigur, nu e nimic gresit, dorinta de a experimenta scrierea de cod e perfect naturala la inceput pentru a ne familiariza cu conceptele de baza. Insa daca proiectul nu e unul abia infiintat, exista riscul sa ingreunam eficienta si mentenanta acestuia prin a-i spori nejustificat marimea si complexitatea reinventand apa calda a doua oara, lucruri de evitat daca nu e nevoie de ele in mod special.
Astfel, daca automatizezi un caz de test ce implica macar partial lucruri ce tin de alte teste, cauta mai intai in proiect sa vezi daca anumite element nu pot fi deja preluate de acolo, respectand desigur logica testului. Pot fi si situatii cand anumite elemente deja existente din cod trebbuie updatate pentru a fi folosite si in alte teste. Din nou, mai degraba e recomandat sa updatam ceva deja existent decat sa construim lucruri complet noi.
3. O metoda = o actiune
Urmatoarea idee ce ajuta simtitor in scrierea de teste automate eficiente este sa ne organizam elementele din cod ca sa fie cat mai distinctive si unice din punct de vedere al actiunilor pe care le vor executa.
Mai precis, este de dorit ca testele noastre sa fie cat mai detaliate ca actiuni, si nu sa adaugam blocuri de cod mamut, cu zeci de linii care fac zeci de actiuni. Sigur, poate unele lucruri necesita un cod ceva mai complex, dar in esenta testele noastre trebuie facute step-by-step, cu metode dedicate per actiune.
Deci daca sa zicem ca folosim modelul Page Object de a structura fisierele noastre de teste automate, si ne structuram pasii in limbaj natural cu plug-in-urile Cucumber si Gherkin, atunci pasii nostri ar trebui sa fie cat mai simpli, de forma: Given the user is logged in the website / When the user selects the Main page / When the user is searching ”pantofi” etc.
Daca vrei un punct de plecare spre partea de testare automata, aici sunt 2 cursuri foarte bune pe Udemy adresate incepatorilor: 1. Testare software (manual si automata) 2. Testare automata cu Python si Selenium
Fiecarui pas in limbaj natural ar trebui s ii corespunda un pas detaliat de cod in spate, care sa conduca spre metodele si locatorii folositi, fiecare cu scopul sau bine definit. Daca o metoda va face 10 lucruri o data, va fi mult mai greu de urmarit ca mentenanta, de updatat si de refolosit in alte teste automate.
4. Evita adaugarea de cod ce nu e utilizat efectiv
Tot in legatura cu ideea numarul 2 privind reutilizarea codului, un alt sfat extrem de valoros este sa nu adaugam cod ce nu va fi utilizat efectiv in testele noastre. Aceste doua idei sunt in corelatie deoarece pornesc de la premisa ca la inceput dorim sa exersam cat mai mult scrierea de cod, si atunci poate aparea si aceasta situatie cand suntem mai harnici decat e nevoie.
Codul neutilizat propriu-zis trebuie evitat din aceleasi motive, incarca prea mult proiectul si ii mareste in mod nejustificat marimea, complexitatea si posibilitatea de mentenanta in caz de refactorizare. Ideal e sa ne limitam exact la adaugarea acelor elemente si metode de care chiar avem nevoie, care nu exista deja si care executa concret actiuni ce compun cazurile noastre de testare, fara alte digresiuni.
Un exemplu concret unde se poate intampla aceasta situatie este atunci cand adaugam ca element in cod switch cases, adica o functionalitate care cuprinde mai multe cazuri, si in functie de optiunile ce le vrem a fi verificate, alegem o optiune sau alta, plus o optiune default in caz ca niciuna nu e gasita la rularea testului automat.
Aici putem avea tentatia sa punem mai multe cazuri decat cele (sau cea) testata propriu-zis, iar celelalte sa ramana de umplutura, ca „poate vor fi folosite in viitor”. Daca chiar va fi nevoie de acestea, se pot adauga la momentul respectiv, codul nostru fiind usor refactorizabil astfel, o alta trasatura importanta pentru un proiect de automation testing.
5. Anticiparea eventualelor situatii ce pot bloca testele
Desi acest lucru suna mai complicat si se aplica ca urmare a experientei noastre anterioare cu scrierea de cod pentru automatizare si cu proiectul respectiv, totusi e la fel de valabil sa incercam sa anticipam, pe cat posibil, acele situatii ce constituie blockere pentru testele noastre, ca sa le putem evita.
Acest lucru se manifesta in special la nivelul acelor puncte din executia testului unde se poate bloca, si implicit sa pice, din cauza anumitor setari pe care nu le-am luat in calcul la inceputul dezvoltarii sale. De exemplu, daca pe un website de cumparaturi vrem sa adaugam un produs in cosul de cumparaturi, apoi sa trecem direct la finalizarea platii, dar exista o setare prin care de fiecare data cand vrem sa adaugam un produs in cos, apare un pop-up in care ne intreaba daca suntem siguri ca dorim asta. Daca noi uitam sa dezactivam sau sa cream un pas de inchidere al acelui pop-up, testul nostru nu o sa mearga pana la capat.
Anticiparea situatiilor de test block nu e la indemana oricui, si in mod clar necesita putina experienta cu aplicatia / proiectul in cauza. Aici parcurgerea documentatiei tehnice (daca exista) poate ajuta in intelegerea feature-urilor ce se doresc a fi automatizate.
6. Asigura-te ca testul verifica mereu ceea ce trebuie
Dupa ce terminam de scris si organizat codul pentru unul sau mai multe teste automate care verifica o anumita componenta din produsul software in cauza, trebuie sa ne asiguram ca acesta este valid ca rezultat in 2 sensuri: ca trece (este passed) atunci cand trebuie sa treaca si ca pica (este failed) atunci cand chiar trebuie sa esueze.
De ce este extrem de important acest lucru? Pentru a evita ca testele noastre automate sa devina instabile (termenul consacrat este de flaky tests) si sa returneze rezultate fie fals pozitive, fie fals negative. Un rezultat fals pozitiv e atunci cand la final ni se zice in consola ca testul nostru e passed, dar de fapt el nu a verificat ce si cum trebuie, iar fals negativ e atunci cand pica desi nu e niciun motiv temeinic pentru asta (adica un bug).
Ambele situatii trebuie evitate pentru a avea teste automate stabile si a fi siguri ca ele verifica ceea ce ne-am propus, fara a exista posibilitatea sa se strecoare bug-uri prin aplicatia software. Putem sa ne asiguram destul de usor in prima faza de acest lucru ruland testul nostru mai intai cu datele corecte ca sa vedem ca trece, apoi sa schimbam anumite date din el la anumite asertii si sa vedem ca pica asa cum ne asteptam.
Ca exemplu, putem lua clasicul login: scriem un test care aserteaza ca atunci cand parola e gresita, user-ul nu se poate loga, iar daca e corecta, atunci se poate loga fara probleme in platforma. Daca prin absurd testul automat trece cand parola e gresita, atunci e o problema cu codul din spate (sau e un bug).
7. Respectarea unui design pattern de automation
Pentru a avea un cod de testare automata eficient, este mai mult decat necesar sa respectam design-ul de organziare si structurare al proiectului respectiv. Un design pattern reprezinta o maniera distincta prin care fisierele noastre cu teste automate sunt organziate pentru a exista o separatie cat mai clara intre pasii in limbaj natural si codul efectiv care pune lucrurile in miscare.
Un astfel de design pattern in automation este Page Object model, despre care am mai discutat tot aici in trecut. In acest caz, testele noastre automate sunt construite in doua parti distincte, care separa pasii propriu-zisi de logica de executie care e mentinuta separat (filele cu variabile, locatori, metode etc.).
Folosirea POM sau a altor pattern-uri structurale intr-un proiect de automation conduce la o mai buna intelegere a proiectului. E greu ca dupa ce ai scris un test ce implica 3 variabile, 4 locatori si 5 metode sa mai tii minte exact ce si cum ai facut dupa un timp, si mai ales sa le cauti prin tot proiectul.
De asemenea, e important sa nu schimbam de la sine putere un design pattern deja urmat, pentru ca altfel vom dat peste cap structura proiectului si nu o sa mai avem acea consistenta necesara pentru a fi eficient si usor de lucrat cu el.
Concluzii
In incheiere, pentru a scrie un cod de testare automata eficient, este necesar sa respectam cateva idei de baza, care cu timpul ne vor simplifica in mod vizibil munca in aceasta sfera. Dupa cum ziceam, multe le-am descoperit lovindu-ma de aceste situatii direct in practica, dar cunoasterea lor e utila inca de la inceput, de dinainte de a ne apuca sa scriem primele teste automate, ca sa ne ghideze mai usor si sa progresam mai bine pe scrierea de cod de automation.