Să validezi un sistem software nu e o muncă ușoară, deși privind din afară, mulți ar zice că e o joacă. Haideți să ne gândim că suntem testeri pentru o zi. Ce avem de făcut? Primul pas: definim test case-urile, apoi scriem testele, automate sau nu, pregătim infrastructura, iar în final începe "joaca". Pentru a salva din timpul alocat testării automate, ne-am gândit să folosim containere Docker ca targeturi. Astfel, testerii nu vor mai fi nevoiți să își bată capul și cu environmentul.
Pregătirea infrastructurii... Nimănui nu-i place partea aceasta. De ce? Principalul motiv este că durează mult, dar nici nu e cea mai captivantă activitate. Ca "non-testeri" am putea zice că e de ajuns să testăm aplicația pe calculatorul nostru. Dar un tester care se respectă trebuie să se asigure că sistemul funcționează la fel, indiferent de ce sistem de operare folosește utilizatorul.
În timp ce așteptam să fie gata environmentul, ne-am gândit ce ușor ar fi dacă i-am putea pune testului sistemele de operare și cu o singură rulare, am acoperi toate cazurile. Așa am ajuns la ideea de a folosi containerele Docker, în locul mașinilor fizice sau virtuale pe care le foloseam în mod normal.
Docker este o tehnologie "la modă", din ce în ce mai întâlnită în industria IT. Pe lângă această popularitate de care se bucură containerele, noi le-am ales din trei motive.
Docker și cu ajutorul containerelor, ne oferă flexibilitatea și libertatea de a dezvolta aplicații fără grija infrastructurii. Astfel, productivitatea noastră crește, iar aplicațiile sunt dezvoltate și testate "la foc automat".
Modul în care un container îmbină codul cu toate dependințele acestuia, reduce consumul de spațiu la câteva zeci de MB, în comparație cu o mașină virtuală care poartă cu ea o copie a sistemului de operare împreună cu aplicația și toate librăriile necesare, ocupând zeci de GB. Docker 1, VM 0.
Pe scurt, am ales containerele Docker, în defavoarea mașinilor virtuale, pentru eficiența și portabilitatea lor, pentru timpul scurt de pornire și pentru consumul redus de resurse.
Am văzut cum ne-a venit ideea de a folosi containere în testare și ce tehnologie am ales. Ce a rămas? În continuare vom vorbi despre cum am adus această idee la viață. Scenariul ideal la care ne-am gândit suna cam așa: testerul scrie o serie de teste, alege imaginile corespunzătoare sistemelor de operare pe care vrea să le ruleze și apăsând un singur buton, testele sunt executate pe toate platformele. Pare prea simplu să fie adevărat.
Pornind de la scenariul pe care l-am descris, am dat start implementării. Pentru început, am avut nevoie de o modalitate de a executa comenzi specifice Docker remote. Astfel, am implementat un utilitar care conține metode pentru câteva comenzi de bază printre care și listarea imaginilor Docker existente, crearea, ștergerea unei imagini, pornirea unui container și executarea unei comenzi în cadrul lui.
De ce executăm comenzi remote? Compania noastră folosește un framework intern de testare care funcționează în felul următor: este o aplicație de genul client-server; frameworkul este serverul, iar clientul este targetul. Dar ce e un target? Un target poate fi o mașină fizică, un container, o mașină virtuală; pe scurt, este "locul" unde se execută testele. După cum putem vedea mai jos, partea de server este construită din trei mari componente care la rândul lor conțin mai multe elemente:
Framework:
CLI - linia de comandă, folosită pentru integrarea frameworkului în procesele de CI/CD;
Execution - reprezintă tot mecanismul care rulează testele;
Content:
Tests - conține suitele și cazurile de testare specifice unui produs;
Arhitectura framework-ului de testare
Pentru ca instanțele de testare să fie vizibile frameworkului, ele trebuie definite într-un fișier de configurare. Acest fișier conține informații despre conexiune cum ar fi IP-ul sau hostname-ul mașinii, username-ul și parola, sistemul de operare, platforma și opțional niște cuvinte cheie (taguri). Având aceste informații, la pornirea unui test, frameworkul se conectează la target, execută comenzile din test pe acea mașină, adună rezultatele și le afișează utilizatorului.
Având mecanismul de lucru cu containere implementat, următorul pas a fost să creăm fișierul de configurare, despre care am vorbit mai sus, cu structura necesară pentru targeturi. Să începem cu conexiunea. Folosind containere, nu am putut obține detalii despre IP-urile lor înainte de creare, dar în același timp, am avut nevoie de un host, o mașină virtuală sau fizică, pe care să creăm containerele. Un lucru foarte important de menționat: pe mașina pe care o folosim ca host, trebuie să existe Docker instalat înainte de pornirea testului. Astfel, am luat IP-ul și credențialele hostului, i-am dat un nume, și am completat și restul informațiilor despre sistemul de operare. Pentru ca frameworkul nostru să diferențieze între tipurile de instanțe pe care testăm, am adăugat tagul "Docker Host". În acest fel, va ști că IP-ul primit este al unui host și urmează să primească și adevăratele targeturi.
Următoarea etapă a fost să creăm structura aceasta de target și pentru containere. Pentru a putea extrage informațiile necesare, mai întâi a trebuit să avem containerele up and running. După ce containerele au fost create, am folosit una dintre metodele despre care am vorbit mai sus. Executând comenzi în interiorul lor, am luat IP-urile, iar datele despre sistemul de operare le-am completat cu numele imaginilor de la care au fost create containerele.
Ce a rămas de făcut? Cu mecanismul de creare al containerelor și al targeturilor, tot ce ne lipsește sunt testele. Un test acceptat de frameworkul nostru e compus din trei fișiere. Un fișier în care specificăm numele testului, eventuale dependințe și descriem ce testăm. Al doilea fișier reprezintă testul în sine, iar ultimul face setupul, rulează testul și la final "face curățenie". Aici am intervenit noi. Când face setup, testerul trebuie să precizeze următoarele lucruri: sistemele de operare pe care vrea să execute testul, adică numele imaginilor Docker, numărul de containere, din fiecare imagine, pe care vrea să testeze și dacă la final, după ce testul este executat, containerele să fie oprite și imaginile șterse.
Cum se întâmplă magia? Să luăm un exemplu. Avem un test scris și vrem să îl executăm, folosind frameworkul, pe trei distribuții diferite Linux: Ubuntu, CentOS și Debian. În primul rând, precizăm aceste nume în funcția de setup și alegem să folosim câte un container din fiecare imagine. De asemenea. alegem ca la finalizarea testului, să se facă cleanup, să se șteargă imaginile și containerele. În fișierul de configurare, completăm informațiile despre mașina pe care o folosim ca host și pornim testul. Frameworkul se va conecta la host și va verifica dacă imaginile pe care le-am precizat există deja. Dacă nu, va "aduce" imaginile din "depozitul Docker" și va crea câte un container din fiecare. Următorul pas e crearea targeturilor. Se ia IP-ul containerelor și se completează restul datelor pentru a avea structura necesară. Pentru fiecare target se creează și se execută câte o copie a testului. În sfârșit, testul se termină cu succes. Putem răsufla liniștiți. Înainte de oprirea containerelor, rezultatele sunt colectate și trimise înapoi frameworkului, iar noi putem analiza execuția.
Scenariu pentru un test case executat în trei containere
Să recapitulăm ce are de făcut un tester pentru a rula testele repede și ușor în containere Docker. În primul rând, trebuie să scrie testul și să se gândească pe ce platforme vrea să îl execute. Apoi, să dea ca parametri funcției de setup numele imaginilor și numărul de containere. Tot aici trebuie să aleagă dacă vrea ca la final să fie șterse imaginile. Ultimii pași sunt să precizeze hostul în fișierul de configurare al frameworkului și să apese butonul de start.
În concluzie, ce am făcut? Am avut o simplă idee și am pus-o în aplicare. Folosirea containerelor Docker ca targeturi pentru teste ne-a ajutat să facem testarea mai eficientă, mai rapidă și să renunțăm la bătaia de cap adusă de setup. Poate am deviat puțin de la scenariul inițial, dar rezultatul a rămas la fel de satisfăcător. Cu puțin efort consumat, implementând acest mecanism de testare în containere, am economist mult timp. Timpul salvat îl folosim acum pentru a investiga alte probleme, pentru a învăța lucruri noi sau chiar pentru a bea o cafea.
Referințe
https://teaching.alexcoman.com/resurse/tutorial/docker/docker-introducere/
https://miro.medium.com/max/832/1*o-ly1HJGekl0pGc64992Yg.png
http://cdn.rancher.com/wp-content/uploads/2017/02/16175231/VMs-and-Containers.jpg