Reguli mici, diferență mare
Testele bune respectă câteva obiceiuri:
1. Nume clare — testGreetEmpty spune ce s-a rupt fără să deschizi fișierul.
2. Un comportament pe test — testele mici arată direct spre problemă.
3. Furnizori de date pentru multe cazuri — un tabel în loc de teste copiate.
4. Verificări care se explică singure — assertSame arată la eșec valoarea așteptată și cea obținută.
5. Metode separate ca să vezi totul, verificări în șir ca să cadă repede.
6. Rulează testele des — după fiecare schimbare mică, nu o singură dată la final.
7. Testele stau lângă cod — același proiect, sufixul Test.php.
Mesaje de eșec care ajută
Un test căzut este citit într-un moment de confuzie — mesajul trebuie să poarte toată povestea: care apel, ce a venit înapoi, ce se aștepta.
assertSame tipărește singur valoarea așteptată și cea obținută ca diferență; argumentul opțional cu mesajul adaugă piesa lipsă — care apel a produs valoarea.
Un mesaj cu toată povestea
greet returnează salutul greșit. Eșecul arată mesajul propriu care numește apelul, apoi diferența: așteptat 'Hello, Ana!', obținut 'Hi Ana' — eroarea (cuvânt greșit, virgulă și semn de exclamare lipsă) se vede numai din ieșire, fără să deschizi codul.
Ieșire: script PHP
<?php
use PHPUnit\Framework\TestCase;
// greet are o eroare: cuvânt greșit, lipsesc virgula și "!".
function greet(string $name): string {
return "Hi " . $name;
}
final class GreetTest extends TestCase {
public function testGreet(): void {
$this->assertSame("Hello, Ana!", greet("Ana"), 'greet("Ana") a returnat salutul greșit');
}
}
Un comportament pe test
Un test care verifică cinci lucruri are cinci motive să cadă — iar numele lui poate descrie doar unul dintre ele. Împărțirea pe comportamente ține fiecare test mic și numele lui cinstit.
Aici salutul normal și varianta de rezervă pentru numele gol sunt două comportamente, deci primesc două metode de test. Când una cade, numele ei îți spune deja care comportament s-a rupt.
Două comportamente, două teste
testGreetName verifică cazul normal, testGreetEmpty varianta de rezervă. Ieșirea --testdox le listează separat — un eșec ar arăta spre exact un comportament.
Ieșire: script PHP
<?php
use PHPUnit\Framework\TestCase;
function greet(string $name): string {
if ($name === "") {
return "Hello, stranger!";
}
return "Hello, " . $name . "!";
}
final class GreetTest extends TestCase {
public function testGreetName(): void {
$this->assertSame("Hello, Ana!", greet("Ana"));
}
public function testGreetEmpty(): void {
$this->assertSame("Hello, stranger!", greet(""));
}
}
Cât este testat: acoperirea
PHPUnit poate măsura care linii din codul tău execută efectiv testele. Are nevoie de o extensie de acoperire în PHP — PCOV sau Xdebug — și raportează în linia de antet pe cea găsită.
./vendor/bin/phpunit --coverage-filter . --coverage-text DescribeTest.php măsoară fișierele din folderul curent și tipărește raportul după rezultatul testului.
Numărul este o busolă, nu un scop: o valoare mică arată căi netestate — aici testul parcurge numai ramura mild, deci liniile freezing și hot nu sunt executate niciodată. O acoperire de 100% nu dovedește că codul este corect, dar liniile neacoperite sunt cu siguranță neverificate.
Măsoară acoperirea
describeTemperature are trei ramuri, testul parcurge una. Raportul numără fiecare linie executabilă din fișier — inclusiv linia metodei de test — și spune Lines: 66.66% (4/6): cele două linii neacoperite sunt instrucțiunile return pentru freezing și hot. Adăugarea unor teste pentru ramura de frig și cea de cald ar ridica numărul.
Ieșire: script PHP
<?php
use PHPUnit\Framework\TestCase;
function describeTemperature(int $celsius): string {
if ($celsius < 0) {
return "freezing";
}
if ($celsius > 30) {
return "hot";
}
return "mild";
}
final class DescribeTest extends TestCase {
public function testDescribeMild(): void {
$this->assertSame("mild", describeTemperature(20));
}
}