Azi vreau să-ți povestesc puțin despre tipurile de teste care sunt implementate chiar de către dezvoltatorii software.
Da, știu că există rolul de “tester” în echipele de dezvoltare software, dar până ca aplicația noastră să ajungă pe mâinile unui tester, trebuie să ne asigurăm și noi, ca dezvoltatori, nu numai că aplicația respectă regulile funcționale sau de business, dar și că anumite elemente tehnice din componența aplicației noastre se integrează corect.
Ce este testarea unitară
Dacă nu știi ce e testarea unitară, ei bine e modul prin care un programator își testează logica instrucțiunilor definite în cod, tot prin cod.
Cum adică tot prin cod?
Adică nu sunt folosite alte unelte externe pentru a crea teste unitare, ci ele sunt create tot prin cod, folosind biblioteci sau resurse dedicate pentru testare.
Testarea unitară se face în general la nivelul de metodă sau funcție și nu pe întreaga aplicație o dată.
Hai să-ți dau un exemplu.
Ai o clasă în aplicația ta care expune o metodă care calculează vârsta utilizatorului în funcție de data nașterii.
Adică metoda ta preia din exterior data nașterii printr-un parametru, calculează și returnează vârsta în funcție de data curentă.
public int CalculateAge(DateTime dateOfBirth)
{
DateTime today = DateTime.Today;
int age = today.Year - dateOfBirth.Year;
// verificăm dacă ziua și luna nașterii
// au fost depășite în anul curent.
// Dacă nu, reducem vârsta calculată cu 1.
if (dateOfBirth.Date > today.AddYears(-age))
{
age--;
}
return age;
}
Astfel va trebui să scrii câteva teste pentru această metodă care să-ți confirme ție că modalitatea prin care ai calculat vârsta în metoda respectivă e corectă.
Mai pe scurt, că implementarea ta nu are bug-uri.
Cum vei face asta?
Ei bine, vei crea câteva teste unitare.
Testele unitare sunt metode care pot fi executate de probabil majoritatea editoarelor de cod existente.
În metodele respective vei crea diferite scenarii în mod artificial și vei compara rezultatul returnat de funcția ta cu ceea ce tu știi că e corect, cu ceea ce tu te aștepți să returneze.
Trebuie să te gândești la toate scenariile posibile și să te asiguri că ai câte un test pentru fiecare în parte.
Uite câteva exemple ce se folosesc de biblioteca denumită Xunit:
using System;
using Xunit;
public class AgeCalculatorTests
{
private readonly AgeCalculator _calculator = new AgeCalculator();
[Fact]
public void CalculateAge_WhenBirthdayHasOccurredThisYear_ReturnsCorrectAge()
{
// setăm data nașterii
DateTime dateOfBirth = new DateTime(2000, 1, 1);
// setăm căt ar trebui să fie vârsta,
// bazat pe data definită anterior
int expectedAge = DateTime.Today.Year - 2000;
// executăm metoda de calcul
int age = _calculator.CalculateAge(dateOfBirth);
// comparăm daca rezultatul obținut e același
// cu cel pe care îl așteptam
Assert.Equal(expectedAge, age);
}
[Fact]
public void CalculateAge_WhenBirthdayIsToday_ReturnsCorrectAge()
{
// simulăm exact ziua datei de naștere ca și
// cum ar fi ziua curentă
DateTime dateOfBirth = DateTime.Today.AddYears(-25);
// executăm metoda de calcul
int age = _calculator.CalculateAge(dateOfBirth);
// verificăm că vârsta e exact cea de la care
// am plecat
Assert.Equal(25, age);
}
}
Sună complicat și pe alocuri este, dar o dată ce ai înțeles cum anume să abordezi testarea unitară, vei vedea că o să găsești mult mai multe probleme decât te așteptai în codul tău, la care înainte nu te gândisei.
Astfel vei evita mult mai multe potențiale probleme și plus de asta, vei obține o baterie de foarte multe teste pentru toate clasele din aplicația ta, care te va ajuta să nu introduci alte probleme la modificări ulterioare de cod.
Testele unitare nu trebuie să conțină utilizarea de resurse externe, precum baze de date, servicii web, sau altele.
Există metode prin care să izolezi codul tău de toate aceste resurse și să-l poți testa.

Ce este testarea de integrare
Testele de integrare sunt puțin mai diferite față de cele unitare.
Dacă la testele unitare spuneam că vrem să testăm fiecare metodă individial, în completă izolare, testele unitare reprezintă fix opusul.
Se cheamă “de integrare” pentru că sunt teste care validează interacțiunea aplicatiei cu diferite componente de infrastructură: baze de date, servicii web externe, interacțiunea cu datele de pe disc etc.
În cazul ăsta nu mai vrem să izolăm componentele, din contră, vrem ca ele să conlucreze, pentru ca noi să putem valida că funcționează corect împreună.
De aici și numele de “integrare”.
Verificăm că două sau mai multe componente se integrează corect.
De exemplu, vrem să vedem că într-un anumit scenariu, aplicația noastră citește o înregistrare din baza de date și returnează răspunsul corect.
Sau citește un fișier de la o anumită locatie de pe disc, pe care îl procesează și în funcție de asta returnează un anumit răspuns.
Trebuie să validăm că răspunsul respectiv e ceea ce ne așteptăm noi să fie în scenariul dat.
Și totodată că aplicația noastră nu a întâmpinat o eroare de runtime.
În general testele de integrare sunt mai greu de abordat, de asta e bine să tratezi cât mai multe cazuri posibile în testele unitare și să păstrezi pentru cele de integrare strict cazurile care țin de infrastructura externă a aplicației.
Cele 2 tipuri de teste nu reprezintă opțiuni, nu se exclud unul pe pe celălalt ci trebuie utilizate împreună pentru a obține cele mai bune rezultate și cele mai puține bug-uri când aplicația noastră ajunge cu adevărat în faza de testare.
Adică ajunge la o echipă de QA care va revalida la rândul ei că aplicația execută toate acțiunile în concordanță cu regulile impuse, dar și că respectă anumite criterii tehnice de performanță și reziliență.

Toate proiectele software folosesc teste?
Realitatea e că în foarte multe companii și echipe de dezvoltare software, testele de genul ăsta nu sunt luate foarte în serios și toată presiunea de validare a calității e aruncată în mâinile oamenilor de testare.
Problema e că un tester nu are acces la toate detaliile interne ale aplicației în momentul în care își execută testele și deși un tester bun va ști cum să abordeze lucrurile astfel încât să acopere o plajă cât mai mare de scenarii, dar cu siguranță multe pot fi omise.
Împreună cu testele unitare și testele de integrare, testele funcționale făcute de un QA va asigura integritatea aplicației tale într-un număr cât mai mare de scenarii.
De asta e bine ca echipele de dezvoltatori să lucreze proactiv și să preîntâmpine multe din potențialele probleme încă din faza de dezvoltare.
Totodată o baterie de teste unitare cât mai mare va face și viața programatorilor mult mai ușoară, pentru că își vor valida la fiecare pas că o modificare adusă codului nu introduce un nou bug.
Testele unitare sunt în general, dacă sunt scrise corect, extrem de rapide, chiar și pentru un număr de câteva mii de teste.
Subiectul testării făcute în faza de dezvoltare e unul mult mai amplu și conține o grămadă de concepte pe care trebuie să le înțelegi pentru a putea scrie teste executate programatic.
Cam atât pentru azi, pe data viitoare!