Teste de scriere
Scrierea testelor pentru Nette Tester este unică prin faptul că fiecare test este un script PHP care poate fi rulat de sine stătător.. Acest lucru are un mare potențial. Pe măsură ce scrieți testul, îl puteți executa pur și simplu pentru a vedea dacă funcționează corect. Dacă nu, puteți trece cu ușurință prin în IDE și căutați un bug.
Puteți chiar să deschideți testul într-un browser. Dar, mai presus de toate – executându-l, veți efectua testul. Veți afla imediat dacă a trecut sau a eșuat.
În capitolul introductiv, am arătat un test foarte banal de utilizare a unui array PHP. Acum vom crea propria noastră clasă, pe care o vom testa, deși va fi, de asemenea, simplă.
Să începem cu o dispunere tipică a directoarelor pentru o bibliotecă sau un proiect. Este important să separăm testele de restul codului, de exemplu din cauza implementării, deoarece nu dorim să încărcăm testele pe server. Structura poate fi după cum urmează:
├── src/ # code that we will test
│ ├── Rectangle.php
│ └── ...
├── tests/ # tests
│ ├── bootstrap.php
│ ├── RectangleTest.php
│ └── ...
├── vendor/
└── composer.json
Și acum vom crea fișiere individuale. Vom începe cu clasa testată, pe care o vom plasa în
fișierul src/Rectangle.php
<?php
class Rectangle
{
private float $width;
private float $height;
public function __construct(float $width, float $height)
{
if ($width < 0 || $height < 0) {
throw new InvalidArgumentException('The dimension must not be negative.');
}
$this->width = $width;
$this->height = $height;
}
public function getArea(): float
{
return $this->width * $this->height;
}
public function isSquare(): bool
{
return $this->width === $this->height;
}
}
și vom crea un test pentru ea. Numele fișierului de test trebuie să se potrivească cu masca *Test.php
sau
*.phpt
, noi vom alege varianta RectangleTest.php
:
<?php
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
// general alungit
$rect = new Rectangle(10, 20);
Assert::same(200.0, $rect->getArea()); # we will verify the expected results
Assert::false($rect->isSquare());
După cum puteți vedea, metodele de aserțiune, cum ar fi Assert::same()
, sunt
utilizate pentru a afirma că o valoare reală corespunde unei valori așteptate.
Ultimul pas este crearea fișierului bootstrap.php
. Acesta conține un cod comun pentru toate testele. De exemplu,
clasele de încărcare automată, configurarea mediului, crearea de directoare temporare, ajutoare și altele similare. Fiecare
test încarcă bootstrap-ul și acordă atenție doar testării. Bootstrap-ul poate arăta astfel:
<?php
require __DIR__ . '/vendor/autoload.php'; # load Composer autoloader
Tester\Environment::setup(); # initialization of Nette Tester
// și alte configurații (doar un exemplu, în cazul nostru nu sunt necesare)
date_default_timezone_set('Europe/Prague');
define('TmpDir', '/tmp/app-tests');
Acest bootstrap presupune că autoloaderul Composer va fi capabil să încarce și clasa
Rectangle.php
. Acest lucru poate fi realizat, de exemplu, prin setarea secțiunii autoload în composer.json
, etc.
Acum putem rula testul din linia de comandă ca orice alt script PHP independent. Prima rulare va dezvălui orice erori de sintaxă, iar dacă nu ați făcut o greșeală de scriere, veți vedea:
$ php RectangleTest.php
OK
Dacă schimbăm în test declarația în fals Assert::same(123, $rect->getArea());
, se va întâmpla
acest lucru:
$ php RectangleTest.php Failed: 200.0 should be 123 in RectangleTest.php(5) Assert::same(123, $rect->getArea()); FAILURE
Atunci când se scriu teste, este bine să se surprindă toate situațiile extreme. De exemplu, dacă intrarea este zero, un număr negativ, în alte cazuri un șir gol, null etc. De fapt, acest lucru vă obligă să vă gândiți și să decideți cum ar trebui să se comporte codul în astfel de situații. Testele fixează apoi comportamentul.
În cazul nostru, o valoare negativă ar trebui să arunce o excepție, pe care o verificăm cu Assert::exception():
// lățimea nu trebuie să fie un număr negativ
Assert::exception(
fn() => new Rectangle(-1, 20),
InvalidArgumentException::class,
'The dimension must not be negative.',
);
Și adăugăm un test similar pentru înălțime. În cele din urmă, testăm că isSquare()
returnează
true
dacă ambele dimensiuni sunt identice. Încercați să scrieți astfel de teste ca exercițiu.
Teste bine aranjate
Dimensiunea fișierului de test poate crește și poate deveni rapid aglomerat. Prin urmare, este practic să se grupeze domeniile testate individual în funcții separate.
Mai întâi, vom prezenta o variantă mai simplă, dar elegantă, utilizând funcția globală test()
. Testerul
nu o creează automat, pentru a evita o coliziune în cazul în care ați avut o funcție cu același nume în codul
dumneavoastră. Ea este creată doar de metoda setupFunctions()
, pe care o apelați în fișierul
bootstrap.php
:
Tester\Environment::setup();
Tester\Environment::setupFunctions();
Folosind această funcție, putem împărți frumos fișierul de testare în unități denumite. La execuție, etichetele vor fi afișate una după alta.
<?php
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
test('general oblong', function () {
$rect = new Rectangle(10, 20);
Assert::same(200.0, $rect->getArea());
Assert::false($rect->isSquare());
});
test('general square', function () {
$rect = new Rectangle(5, 5);
Assert::same(25.0, $rect->getArea());
Assert::true($rect->isSquare());
});
test('dimensions must not be negative', function () {
Assert::exception(
fn() => new Rectangle(-1, 20),
InvalidArgumentException::class,
);
Assert::exception(
fn() => new Rectangle(10, -1),
InvalidArgumentException::class,
);
});
Dacă aveți nevoie să executați codul înainte sau după fiecare test, treceți-l la setUp()
sau
tearDown()
:
setUp(function () {
// codul de inițializare care trebuie executat înainte de fiecare test()
});
A doua variantă este obiect. Vom crea așa-numitul TestCase, care este o clasă în care unitățile individuale sunt reprezentate de metode ale căror nume încep cu test-.
class RectangleTest extends Tester\TestCase
{
public function testGeneralOblong()
{
$rect = new Rectangle(10, 20);
Assert::same(200.0, $rect->getArea());
Assert::false($rect->isSquare());
}
public function testGeneralSquare()
{
$rect = new Rectangle(5, 5);
Assert::same(25.0, $rect->getArea());
Assert::true($rect->isSquare());
}
/** @throws InvalidArgumentException */
public function testWidthMustNotBeNegative()
{
$rect = new Rectangle(-1, 20);
}
/** @throws InvalidArgumentException */
public function testHeightMustNotBeNegative()
{
$rect = new Rectangle(10, -1);
}
}
// Executarea metodelor de testare
(new RectangleTest)->run();
De data aceasta am folosit o adnotare @throw
pentru a testa excepțiile. Pentru mai multe informații,
consultați capitolul TestCase.
Funcții ajutătoare
Nette Tester include mai multe clase și funcții care vă pot ușura testarea, de exemplu, ajutători pentru a testa conținutul unui document HTML, pentru a testa funcțiile de lucru cu fișiere și așa mai departe.
Puteți găsi o descriere a acestora pe pagina Helpers.
Adnotare și săritură de teste
Executarea testelor poate fi afectată de adnotările din comentariul phpDoc de la începutul fișierului. De exemplu, ar putea arăta astfel:
/**
* @phpExtension pdo, pdo_pgsql
* @phpVersion >= 7.2
*/
Adnotările spun că testul ar trebui să fie executat numai cu versiunea PHP 7.2 sau mai mare și dacă extensiile PHP pdo
și pdo_pgsql sunt prezente. Aceste adnotări sunt controlate de linia de comandă test runner,
care, în cazul în care condițiile nu sunt îndeplinite, sare testul și îl marchează cu litera s
– skipped.
Cu toate acestea, ele nu au niciun efect atunci când testul este rulat manual.
Pentru o descriere a adnotărilor, consultați secțiunea Adnotări de test.
Testul poate fi, de asemenea, sărit pe baza unei condiții proprii cu Environment::skip()
. De exemplu, vom sări
peste acest test pe Windows:
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
Tester\Environment::skip('Requires UNIX.');
}
Structura directoarelor
Pentru biblioteci sau proiecte puțin mai mari, se recomandă împărțirea directorului de testare în subdirectoare în funcție de spațiul de nume al clasei testate:
└── tests/
├── NamespaceOne/
│ ├── MyClass.getUsers.phpt
│ ├── MyClass.setUsers.phpt
│ └── ...
│
├── NamespaceTwo/
│ ├── MyClass.creating.phpt
│ ├── MyClass.dropping.phpt
│ └── ...
│
├── bootstrap.php
└── ...
Veți putea executa testele dintr-un singur subdirectorat namespace ie:
tester tests/NamespaceOne
Edge Cases
Un test care nu apelează nicio metodă de aserțiune este suspect și va fi evaluat ca fiind eronat:
Error: This test forgets to execute an assertion.
Dacă testul fără apelarea aserțiunilor trebuie într-adevăr să fie considerat valid, apelați, de exemplu,
Assert::true(true)
.
De asemenea, poate fi înșelător să se utilizeze exit()
și die()
pentru a încheia testul cu un
mesaj de eroare. De exemplu, exit('Error in connection')
încheie testul cu un cod de ieșire 0, care semnalează
succesul. Utilizați Assert::fail('Error in connection')
.