Category: automation testing

Implementarea Setup și Teardown în testele Selenium cu TestNG și Java

Atunci când dezvoltăm teste automate pentru aplicații web cu Selenium și Java, este important să avem un mediu bine configurat pentru a asigura că testele rulează corect și eficient. O abordare comună în acest sens este utilizarea setup și teardown, oferite de framework-ul TestNG. În acest articol, vom explora cum să implementăm setup și teardown în teste Selenium folosind Java și TestNG.

Setarea mediului cu Setup

Metoda @BeforeClass din TestNG este utilizată pentru a executa cod înaintea primului test dintr-o clasă de teste. Această metodă este ideală pentru operațiile de setup care trebuie realizate o singură dată pentru întregul set de teste.

import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class SeleniumTest {

    @BeforeClass
    public void setup() {
        // Configurare WebDriver, deschidere browser, etc.
        System.out.println("Setup pentru testele Selenium");
    }

    @Test
    public void test1() {
        // Test 1
        System.out.println("Execută Test 1");
    }
}

În exemplul de mai sus, metoda setup() va fi apelată înaintea execuției oricărui test din clasă. Aici poți configura WebDriver-ul, deschide browser-ul sau executa orice alte operațiuni de setup necesare.

Cleanup cu Teardown

Metoda @AfterClass din TestNG este utilizată pentru a executa cod după ce toate testele dintr-o clasă au fost rulate. Aceasta este o oportunitate ideală pentru operațiunile de cleanup sau pentru închiderea resurselor deschise în timpul setup-ului.

import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;

public class SeleniumTest {

    @BeforeClass
    public void setup() {
        // Configurare WebDriver, deschidere browser, etc.
        System.out.println("Setup pentru testele Selenium");
    }

    @Test
    public void test1() {
        // Test 1
        System.out.println("Execută Test 1");
    }

    @Test
    public void test2() {
        // Test 2
        System.out.println("Execută Test 2");
    }

    @AfterClass
    public void teardown() {
        // Închidere browser, eliberare resurse, etc.
        System.out.println("Teardown după testele Selenium");
    }
}

Metoda teardown() este apelată după ce toate testele din clasă au fost rulate, oferind un loc potrivit pentru operațiuni de curățare și eliberare a resurselor.

O practică recomandată în dezvoltarea de teste automate este organizarea metodelor de setup și teardown într-o clasă separată, numită adesea TestBase sau BaseTest. Această clasă servește drept punct central pentru toate operațiunile de configurare și curățare și este extinsă de către clasele de teste specifice.

Avantaje ale utilizării unei Clase de Bază (BaseTest):

  1. Reutilizare a Codului: Prin plasarea metodelor de setup și teardown într-o clasă separată, poți reutiliza aceste metode în toate clasele de teste care extind această clasă de bază. Acest lucru conduce la un cod mai curat și mai ușor de întreținut.
  2. Consistență: Toate clasele de teste care extind BaseTest vor beneficia de aceeași configurare și curățare, asigurând consistența întregii suită de teste.
  3. Flexibilitate: Prin intermediul clasei de bază, poți adăuga și gestiona cu ușurință alte funcționalități globale necesare pentru testele tale, cum ar fi gestionarea datelor de test, logging-ul, sau interacțiunea cu servicii externe.

Iată un exemplu simplu pentru o clasă de bază (BaseTest) care conține metodele de setup și teardown:

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;

public class BaseTest {
    protected WebDriver driver;

    @BeforeClass
    public void setup() {
        System.setProperty("webdriver.chrome.driver", "C:\\path\\to\\chromedriver.exe");
        driver = new ChromeDriver();
        driver.manage().window().maximize();
        // Alte operațiuni de configurare
    }

    @AfterClass
    public void teardown() {
        if (driver != null) {
            driver.quit();
        }
        // Alte operațiuni de curățare
    }
}

O dată ce ai definit clasa de bază, poți extinde această clasă în toate clasele tale de teste specifice, precum SeleniumTest:

import org.testng.annotations.Test;

public class SeleniumTest extends BaseTest {

    @Test
    public void test1() {
        // Test specific
        // Nu este nevoie să configurezi WebDriver-ul sau să gestionezi cleanup-ul explicit
    }

    @Test
    public void test2() {
        // Alt test specific
    }
}

Această organizare a codului facilitează menținerea și extinderea suitei de teste și contribuie la un cod mai curat și mai ușor de gestionat în timp.

Utilizarea setup și teardown în teste automate cu Selenium și Java oferă o structură bine organizată și modulară pentru gestionarea configurării și închiderii mediului de test. TestNG facilitează aceste operațiuni prin intermediul anotărilor @BeforeClass și @AfterClass. Implementând aceste practici, poți asigura că testele tale rulează într-un mediu coerent și că resursele sunt gestionate eficient.

Cum să faci un screenshot folosind librăria Selenium în Java

Selenium este o librărie populară pentru automatizarea testelor pe aplicații web. Printre numeroasele funcționalități pe care le oferă, se numără și posibilitatea de a realiza capturi de ecran (screenshot-uri) ale paginilor web. În acest articol, vom explora cum poți utiliza Selenium în Java pentru a realiza un screenshot al unei pagini web.

Pasul 1: Configurarea proiectului Java

Pentru a începe, asigură-te că ai configurat un proiect Java și ai adăugat librăria Selenium în claspath-ul proiectului tău. Poți face acest lucru prin adăugarea dependenței în fișierul de configurare Maven (pom.xml) sau prin descărcarea și adăugarea manuală a bibliotecilor Selenium.

Exemplu pentru Maven:

  <dependencies>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.16.1</version> <!-- Asigură-te că utilizezi ultima versiune disponibilă -->
    </dependency>
</dependencies>

Pasul 2: Inițializarea driver-ului Selenium

Înainte de a realiza un screenshot, trebuie să inițializezi un driver Selenium pentru a controla un browser. În exemplul nostru, vom utiliza ChromeDriver.

public class HotToTakeScreenshotWithSelenium {

	
	 public static void main(String[] args) {

	        // Inițializează un obiect WebDriver pentru Chrome
	        WebDriver driver = new ChromeDriver();

	        // Deschide o pagină web
	        driver.get("https://www.keybooks.ro");

	        // Realizează un screenshot și salvează-l într-un fișier
	        try {
	            // Utilizează metoda getScreenshotAs pentru a realiza captura de ecran
	            File screenshotFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
	            
	            // Salvează captura de ecran într-un fișier
	            FileUtils.copyFile(screenshotFile, new File("poze/screenshot.png"));
	        } catch (IOException e) {
	            e.printStackTrace();
	        }

	        // Închide browser-ul
	        driver.quit();
	    }
	
}

Pasul 3: Adăugarea excepțiilor și importurile necesare

În exemplul de cod de mai sus, am adăugat un bloc try-catch pentru a trata excepțiile care pot apărea în timpul realizării și salvării capturii de ecran. De asemenea, am importat clasele necesare pentru a gestiona aceste operațiuni.

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

Concluzie:
Utilizând librăria Selenium în Java, realizarea unui screenshot al unei pagini web devine o sarcină simplă. Acest exemplu furnizează o bază solidă pentru începerea automatizării testelor și a altor activități care implică capturi de ecran în cadrul proceselor de dezvoltare software.

Exemplul de cod poate fi accesat aici: github.com

5 principii esentiale in testarea automata

1. Setup & teardown

Mecanismul de setup si teardown se refera la crearea datelor de test in metode specifice de pre-run, iar apoi stergerea acestor date sau revenirea la valorile initiale in cazul in care au fost date doar modificate, in metode de post-run. Astfel, inaintea executiei unui test automat, pregatim datele de teste in metode de pre-run(setup), care pot insemna, diverse apelari de RestApi sau interogari si operatiuni in bazele de date ale aplicatiei sau chiar si generarea de date de test aleatorii folosind functii special create pentru aceasta.
Apoi, dupa executia test case-ului, intra in scena metoda de post-run(teardown), care presupune ca toate datele de test care au fost create si folosite in test case (date create in setup) sa fie sterse sau modificate la valorile lor initiale. Atentie, metodele de post-run trebuie sa se execute indiferent de statusul test case-ului, daca a fost Pass sau Fail.
O greseala comuna in testarea autoamata este nerularea metodelor de teardown in cazul in care test case-ul este Fail. In acest caz, tot ce a fost creat si modificat in setup, va ramane, putand astfel sa cauzeze un rezultat neasteptat la urmatoarele rulari.

2. Testele trebuie sa fie independente

Fiecare test case automat ar trebui sa poata rula independent fara a depinde de alt test case-uri. Aceasta dependenta se refera atat la datele de test, cat si la locul de unde incepe test case-ul sa se execute in aplicatie. De foarte multe ori am observat situatii in care se incepe automatizarea unui test case din locul unde test case-ul precendent a lasat aplicatia. De exemplu: daca aplicatia noastra are 10 ecrane si noi avem 10 test case-uri pentru fiecare ecran, daca test case-ul 5 incepe de unde a terminat test case-ul 4, este gresit. Aceasta presupune dependenta fata de testul precedent. Daca test case-ul 4 esueaza in momentul executiei, niciun alt test care urmeaza si care depinde de el nu va putea sa se execute cu succes.
Este valabil si in cazul datelor de test. Daca un test are nevoie de anumite date care ar trebui create de catre un alt test case, este deasemenea gresit. Datele de test pentru teste ar trebui intodeauna create folosind un mecanism de setup & teardown, asa cum a fost  descris la punctul 1.
Dependenta intre teste, omoara testarea paralela.

3. Introducerea unui mecanism de retry(re-rulare a testului)

De multe ori in testarea automata nu putem controla tot mediul de testare. Spre exemplu, nu avem niciun control asupra timpului in care un browser incarca aplicatia sau un API raspunde in urma unui apel HTTP. Acestea pot depinde de foarte multi factori care pot tine de la incarcarea masinii pe care executam testele, pana la latenta vitezei internetului in acel moment. Cum ne afecteaza lucrul acesta executia testelor automate? Prin faptul ca ne poate introduce rezultate neasteptate in momentul executiei. O greseala comuna este aceaa de a adauga timpi de asteptare de fiecare data cand intalnim astfel de comportamente. Este o abordare gresita pentru ca aceste asteptari nu fac nimic altceva decat sa adauge ceea ce numim timp mort in executie. Timpul mort este reprezentat de timpul in care suita de teste asteapta, de multe ori fara sa fie nevoie de aceste asteptari. Pe termen mediu si lung, privit macro (la nivel de suita de teste, nu de test case), acele cateva secunde pe care le adaugam se vor transforma in minute bune, cand le vom aduna din toate test case-urile. Nu este optim sa avem o suita de teste care sa se execute in zeci de minute sau poate chiar ore. Aici intervin mecanismele de retry, unde, atunci avem nevoie de o anumita conditie pentru a trece mai departe, in loc sa asteptam ca acea conditie sa se indepliuneasca dupa un anumit timp, introducem un mecanism de retry al pasului respectiv si incercam din nou in loc sa asteptam.

4. Executia paralela

Executia paralela ne ajuta sa micsoram timpul de rulare a testelor. Pentru orice companie care dezvolta software, “time to market” este esential si se traduce de cele mai multe ori in veniturile pe care compania respectiva este capabila sa le genereze. Time to market se refera la capacitatea organizatiei de a a livra un produs finit catre publicul tinta. Masoara timpul de la concept, pana in punctul in care utilizatorul final poate folosi produsul respectiv. Cu cat acest timp este mai mic, cu atat compania isi creste sansele de succes. In cazul in care timpul mare, exista riscul ca utilizatorii finali sa primeasca acealsi produs de la o companie concurenta, ceea ce va scadea sansele de succes. In acest context, cand totul se reduce la viteza de livrare,  o suita de teste automate care ofera un feedback dupa o executie care poate dura ore (pentru fiecare instalare in productie a aplicatiei) nu este cea mai buna idee. In aceste situatii, din pacate, exista riscul de a se sari peste executia testelor, pentru ca dureaza prea mult.

Dar ce facem daca testele sunt pur si simplu foarte multe si dureaza foarte mult? Solutia este: testarea in paralel. In primul rand, ca sa putem testa in paralel, toate principiile de mai sus trebuie sa fie implementate in suita noastra de teste. Daca testele sunt independente si nu depind unele de altele sau de acelasi seturi ale datelor de test, atunci, imaginati-va o suita de teste care in mod normal se executa in 4 ore, ca poate fi impartita pe 4 fire de executie separate (chiar si pe 4 masini diferite) si vor rula in doar 1 ora. Daca adaugam alte 4, vor rula in 30 de minute. O astfel de abordare ne ajuta sa economisim timp. Alt avantaj este si faptul ca reducand timpul de executie total, putem rula suita la fiecare schimbare de cod pe care programatorii o fac, astfel crescand increderea in calitatea software-ului livrat.

5. Executia in CI/CD

CI (Continuous Integration)/CD(Continuous deployment) sunt practici din dezvoltarea software care presupun o integrare si instalare continua in productie a schimbarilor aduse in programul software. Ele ne ajuta sa atingem acel time to market de care  aminteam mai sus. Ca aceste lucruri sa se poata intampla fara incidente majore, software-ul trebuie sa fie testat. Insa nu putem atinge acea viteza si integrare continua daca trebuie sa ne oprim tot timpul sa asteptam ore sau zile testarea aplicatiei. Testarea trebuie sa se intample rapid, continuu si fara sa aduca timpi de asteptare mari. Am intalnit multe situatii in care se scriu teste automate si se executa de pe calculatorele individuale. Altfel spus, sunt teste automate executate manual. Este o abordare gresita! Testele automate trebuie sa fie executate in mod automat, declansate de schimbarile in cod pe care le fac programatorii aplicatiei si sa actioneze ca o poarta de siguranta. Daca un programator schimba ceva in cod, se vor declansa testele automate, iar daca un test pica, atunci schimbarea facuta de programator nu trebuie sa treaca mai departe pana nu analizam daca a cauzat testul care a fost fail. Acesta este unul din scopurile testelor automate: sa te aiguri ca ceea ce ai implementat nu strica ceea ce exista deja in aplicatie.

In concluzie, nu este suficient sa stim doar sintaxa unui limbaj de programare si metodele unei librarii pentru a scrie teste automate.

O suita de teste construita corect trebuie sa se bazeze inca de la inceput pe principii corecte, pe un  mod corect de a structura codul scris, astfel incat sa putem avea atat stabilitate, perfomanta, scalabilitate pentru noi teste , dar si un nivel de mententanta cat mai redus.