Nette Documentation Preview

syntax
Aserțiuni
*********

.[perex]
Aserțiunile sunt folosite pentru a confirma că valoarea reală corespunde valorii așteptate. Acestea sunt metode ale clasei `Tester\Assert`.

Alegeți aserțiunile cele mai potrivite. Este mai bine `Assert::same($a, $b)` decât `Assert::true($a === $b)`, deoarece în caz de eșec afișează un mesaj de eroare semnificativ. În al doilea caz, doar `false should be true`, ceea ce nu ne spune nimic despre conținutul variabilelor `$a` și `$b`.

Majoritatea aserțiunilor pot avea și o descriere opțională în parametrul `$description`, care se va afișa în mesajul de eroare dacă așteptarea eșuează.

Exemplele presupun crearea unui alias:

```php
use Tester\Assert;
```


Assert::same($expected, $actual, ?string $description=null) .[method]
---------------------------------------------------------------------
`$expected` trebuie să fie identic cu `$actual`. Același lucru ca operatorul PHP `===`.


Assert::notSame($expected, $actual, ?string $description=null) .[method]
------------------------------------------------------------------------
Opusul `Assert::same()`, adică același lucru ca operatorul PHP `!==`.


Assert::equal($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false) .[method]
-------------------------------------------------------------------------------------------------------------------------
`$expected` trebuie să fie egal cu `$actual`. Spre deosebire de `Assert::same()`, se ignoră identitatea obiectelor, ordinea perechilor cheie => valoare în array-uri și numerele zecimale marginal diferite, ceea ce poate fi schimbat prin setarea `$matchIdentity` și `$matchOrder`.

Următoarele cazuri sunt egale din punctul de vedere al `equal()`, dar nu și `same()`:

```php
Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
	['first' => 11, 'second' => 22],
	['second' => 22, 'first' => 11],
);
```

Atenție însă, array-urile `[1, 2]` și `[2, 1]` nu sunt egale, deoarece diferă doar ordinea valorilor, nu și a perechilor cheie => valoare. Array-ul `[1, 2]` poate fi scris și ca `[0 => 1, 1 => 2]` și, prin urmare, `[1 => 2, 0 => 1]` va fi considerat egal.

Mai departe, în `$expected` se pot folosi așa-numitele [#așteptări].


Assert::notEqual($expected, $actual, ?string $description=null) .[method]
-------------------------------------------------------------------------
Opusul `Assert::equal()`.


Assert::contains($needle, string|array $actual, ?string $description=null) .[method]
------------------------------------------------------------------------------------
Dacă `$actual` este un șir, trebuie să conțină subșirul `$needle`. Dacă este un array, trebuie să conțină elementul `$needle` (se compară strict).


Assert::notContains($needle, string|array $actual, ?string $description=null) .[method]
---------------------------------------------------------------------------------------
Opusul `Assert::contains()`.


Assert::hasKey(string|int $needle, array $actual, ?string $description=null) .[method]{data-version:2.4}
--------------------------------------------------------------------------------------------------------
`$actual` trebuie să fie un array și trebuie să conțină cheia `$needle`.


Assert::notHasKey(string|int $needle, array $actual, ?string $description=null) .[method]{data-version:2.4}
-----------------------------------------------------------------------------------------------------------
`$actual` trebuie să fie un array și nu trebuie să conțină cheia `$needle`.


Assert::true($value, ?string $description=null) .[method]
---------------------------------------------------------
`$value` trebuie să fie `true`, adică `$value === true`.


Assert::truthy($value, ?string $description=null) .[method]
-----------------------------------------------------------
`$value` trebuie să fie adevărat (truthy), adică îndeplinește condiția `if ($value) ...`.


Assert::false($value, ?string $description=null) .[method]
----------------------------------------------------------
`$value` trebuie să fie `false`, adică `$value === false`.


Assert::falsey($value, ?string $description=null) .[method]
-----------------------------------------------------------
`$value` trebuie să fie fals (falsey), adică îndeplinește condiția `if (!$value) ...`.


Assert::null($value, ?string $description=null) .[method]
---------------------------------------------------------
`$value` trebuie să fie `null`, adică `$value === null`.


Assert::notNull($value, ?string $description=null) .[method]
------------------------------------------------------------
`$value` nu trebuie să fie `null`, adică `$value !== null`.


Assert::nan($value, ?string $description=null) .[method]
--------------------------------------------------------
`$value` trebuie să fie Not a Number. Pentru testarea valorii NAN folosiți exclusiv `Assert::nan()`. Valoarea NAN este foarte specifică și aserțiunile `Assert::same()` sau `Assert::equal()` pot funcționa neașteptat.


Assert::count($count, Countable|array $value, ?string $description=null) .[method]
----------------------------------------------------------------------------------
Numărul de elemente din `$value` trebuie să fie `$count`. Adică același lucru ca `count($value) === $count`.


Assert::type(string|object $type, $value, ?string $description=null) .[method]
------------------------------------------------------------------------------
`$value` trebuie să fie de tipul dat. Ca `$type` putem folosi un șir:
- `array`
- `list` - array indexat după o serie ascendentă de chei numerice începând de la zero
- `bool`
- `callable`
- `float`
- `int`
- `null`
- `object`
- `resource`
- `scalar`
- `string`
- numele clasei sau direct obiectul, atunci `$value` trebuie să fie `instanceof $type`


Assert::exception(callable $callable, string $class, ?string $message=null, $code=null) .[method]
-------------------------------------------------------------------------------------------------
La apelarea `$callable` trebuie să fie aruncată o excepție de clasa `$class`. Dacă specificăm `$message`, trebuie să [corespundă modelului |#Assert::match] și mesajul excepției, iar dacă specificăm `$code`, trebuie să se potrivească strict și codurile.

Următorul test va eșua, deoarece mesajul excepției nu corespunde:

```php
Assert::exception(
	fn() => throw new App\InvalidValueException('Valoare zero'),
	App\InvalidValueException::class,
	'Valoarea este prea mică',
);
```

`Assert::exception()` returnează excepția aruncată, astfel se poate testa și o excepție imbricată.

```php
$e = Assert::exception(
	fn() => throw new MyException('Ceva este în neregulă', 0, new RuntimeException),
	MyException::class,
	'Ceva este în neregulă',
);

Assert::type(RuntimeException::class, $e->getPrevious());
```


Assert::error(string $callable, int|string|array $type, ?string $message=null) .[method]
----------------------------------------------------------------------------------------
Verifică dacă funcția `$callable` a generat erorile așteptate (adică avertismente, notificări etc.). Ca `$type` specificăm una dintre constantele `E_...`, de exemplu `E_WARNING`. Și dacă specificăm `$message`, trebuie să [corespundă modelului |#Assert::match] și mesajul de eroare. De exemplu:

```php
Assert::error(
	fn() => $i++,
	E_NOTICE,
	'Variabilă nedefinită: i',
);
```

Dacă callback-ul generează mai multe erori, trebuie să le așteptăm pe toate în ordinea exactă. În acest caz, transmitem în `$type` un array:

```php
Assert::error(function () {
	$a++;
	$b++;
}, [
	[E_NOTICE, 'Variabilă nedefinită: a'],
	[E_NOTICE, 'Variabilă nedefinită: b'],
]);
```

.[note]
Dacă specificați ca `$type` numele unei clase, se comportă la fel ca `Assert::exception()`.


Assert::noError(callable $callable) .[method]
---------------------------------------------
Verifică dacă funcția `$callable` nu a generat niciun avertisment, eroare sau excepție. Este util pentru testarea bucăților de cod unde nu există nicio altă aserțiune.


Assert::match(string $pattern, $actual, ?string $description=null) .[method]
----------------------------------------------------------------------------
`$actual` trebuie să corespundă modelului `$pattern`. Putem folosi două variante de modele: expresii regulate sau substituenți.

Dacă transmitem ca `$pattern` o expresie regulată, pentru delimitarea sa trebuie să folosim `~` sau `#`, alți delimitatori nu sunt suportați. De exemplu, testul în care `$var` trebuie să conțină doar cifre hexazecimale:

```php
Assert::match('#^[0-9a-f]$#i', $var);
```

A doua variantă este similară cu compararea obișnuită a șirurilor, dar în `$pattern` putem folosi diferiți substituenți:

- `%a%` unul sau mai multe caractere, cu excepția caracterelor de sfârșit de linie
- `%a?%` zero sau mai multe caractere, cu excepția caracterelor de sfârșit de linie
- `%A%` unul sau mai multe caractere, inclusiv caracterele de sfârșit de linie
- `%A?%` zero sau mai multe caractere, inclusiv caracterele de sfârșit de linie
- `%s%` unul sau mai multe caractere spațiu alb, cu excepția caracterelor de sfârșit de linie
- `%s?%` zero sau mai multe caractere spațiu alb, cu excepția caracterelor de sfârșit de linie
- `%S%` unul sau mai multe caractere, cu excepția caracterelor spațiu alb
- `%S?%` zero sau mai multe caractere, cu excepția caracterelor spațiu alb
- `%c%` orice caracter unic, cu excepția caracterului de sfârșit de linie
- `%d%` una sau mai multe cifre
- `%d?%` zero sau mai multe cifre
- `%i%` valoare întreagă cu semn
- `%f%` număr zecimal
- `%h%` una sau mai multe cifre hexazecimale
- `%w%` unul sau mai multe caractere alfanumerice
- `%%` caracterul %

Exemple:

```php
# Din nou test pentru număr hexazecimal
Assert::match('%h%', $var);

# Generalizarea căii către fișier și a numărului liniei
Assert::match('Eroare în fișierul %a% la linia %i%', $errorMessage);
```


Assert::matchFile(string $file, $actual, ?string $description=null) .[method]
-----------------------------------------------------------------------------
Aserțiunea este identică cu [#Assert::match()], dar modelul se încarcă din fișierul `$file`. Acest lucru este util pentru testarea șirurilor foarte lungi. Fișierul cu testul rămâne lizibil.


Assert::fail(string $message, $actual=null, $expected=null) .[method]
---------------------------------------------------------------------
Această aserțiune eșuează întotdeauna. Uneori este pur și simplu util. Opțional, putem specifica și valoarea așteptată și cea reală.


Așteptări
---------
Când dorim să comparăm structuri mai complexe cu elemente neconstante, aserțiunile de mai sus s-ar putea să nu fie suficiente. De exemplu, testăm o metodă care creează un nou utilizator și returnează atributele sale ca array. Nu cunoaștem valoarea hash-ului parolei, dar știm că trebuie să fie un șir hexazecimal. Și despre un alt element știm doar că trebuie să fie un obiect `DateTime`.

În aceste situații putem folosi `Tester\Expect` în interiorul parametrului `$expected` al metodelor `Assert::equal()` și `Assert::notEqual()`, cu ajutorul cărora structura poate fi descrisă ușor.

```php
use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # așteptăm un număr întreg
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # așteptăm un șir care corespunde modelului
	'created_at' => Expect::type(DateTime::class), # așteptăm o instanță a clasei
], User::create(123, 'milo', 'RandomPaSsWoRd'));
```

Cu `Expect` putem efectua aproape aceleași aserțiuni ca și cu `Assert`. Adică avem la dispoziție metodele `Expect::same()`, `Expect::match()`, `Expect::count()` etc. În plus, le putem înlănțui:

```php
Expect::type(MyIterator::class)->andCount(5);  # așteptăm MyIterator și numărul de elemente 5
```

Sau putem scrie proprii handleri de aserțiuni.

```php
Expect::that(function ($value) {
	# returnăm false dacă așteptarea eșuează
});
```


Examinarea aserțiunilor eșuate
------------------------------
Când o aserțiune eșuează, Tester afișează unde este greșeala. Dacă comparăm structuri mai complexe, Tester creează dump-uri ale valorilor comparate și le salvează în directorul `output`. De exemplu, la eșecul testului fictiv `Arrays.recursive.phpt`, dump-urile vor fi salvate astfel:

```
app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # valoarea actuală
	│   └── Arrays.recursive.expected  # valoarea așteptată
	│
	└── Arrays.recursive.phpt          # testul eșuat
```

Numele directorului poate fi schimbat prin `Tester\Dumper::$dumpDir`.

Aserțiuni

Aserțiunile sunt folosite pentru a confirma că valoarea reală corespunde valorii așteptate. Acestea sunt metode ale clasei Tester\Assert.

Alegeți aserțiunile cele mai potrivite. Este mai bine Assert::same($a, $b) decât Assert::true($a === $b), deoarece în caz de eșec afișează un mesaj de eroare semnificativ. În al doilea caz, doar false should be true, ceea ce nu ne spune nimic despre conținutul variabilelor $a și $b.

Majoritatea aserțiunilor pot avea și o descriere opțională în parametrul $description, care se va afișa în mesajul de eroare dacă așteptarea eșuează.

Exemplele presupun crearea unui alias:

use Tester\Assert;

Assert::same($expected, $actual, ?string $description=null)

$expected trebuie să fie identic cu $actual. Același lucru ca operatorul PHP ===.

Assert::notSame($expected, $actual, ?string $description=null)

Opusul Assert::same(), adică același lucru ca operatorul PHP !==.

Assert::equal($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false)

$expected trebuie să fie egal cu $actual. Spre deosebire de Assert::same(), se ignoră identitatea obiectelor, ordinea perechilor cheie ⇒ valoare în array-uri și numerele zecimale marginal diferite, ceea ce poate fi schimbat prin setarea $matchIdentity și $matchOrder.

Următoarele cazuri sunt egale din punctul de vedere al equal(), dar nu și same():

Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
	['first' => 11, 'second' => 22],
	['second' => 22, 'first' => 11],
);

Atenție însă, array-urile [1, 2] și [2, 1] nu sunt egale, deoarece diferă doar ordinea valorilor, nu și a perechilor cheie ⇒ valoare. Array-ul [1, 2] poate fi scris și ca [0 => 1, 1 => 2] și, prin urmare, [1 => 2, 0 => 1] va fi considerat egal.

Mai departe, în $expected se pot folosi așa-numitele așteptări.

Assert::notEqual($expected, $actual, ?string $description=null)

Opusul Assert::equal().

Assert::contains($needle, string|array $actual, ?string $description=null)

Dacă $actual este un șir, trebuie să conțină subșirul $needle. Dacă este un array, trebuie să conțină elementul $needle (se compară strict).

Assert::notContains($needle, string|array $actual, ?string $description=null)

Opusul Assert::contains().

Assert::hasKey(string|int $needle, array $actual, ?string $description=null)

$actual trebuie să fie un array și trebuie să conțină cheia $needle.

Assert::notHasKey(string|int $needle, array $actual, ?string $description=null)

$actual trebuie să fie un array și nu trebuie să conțină cheia $needle.

Assert::true($value, ?string $description=null)

$value trebuie să fie true, adică $value === true.

Assert::truthy($value, ?string $description=null)

$value trebuie să fie adevărat (truthy), adică îndeplinește condiția if ($value) ....

Assert::false($value, ?string $description=null)

$value trebuie să fie false, adică $value === false.

Assert::falsey($value, ?string $description=null)

$value trebuie să fie fals (falsey), adică îndeplinește condiția if (!$value) ....

Assert::null($value, ?string $description=null)

$value trebuie să fie null, adică $value === null.

Assert::notNull($value, ?string $description=null)

$value nu trebuie să fie null, adică $value !== null.

Assert::nan($value, ?string $description=null)

$value trebuie să fie Not a Number. Pentru testarea valorii NAN folosiți exclusiv Assert::nan(). Valoarea NAN este foarte specifică și aserțiunile Assert::same() sau Assert::equal() pot funcționa neașteptat.

Assert::count($count, Countable|array $value, ?string $description=null)

Numărul de elemente din $value trebuie să fie $count. Adică același lucru ca count($value) === $count.

Assert::type(string|object $type, $value, ?string $description=null)

$value trebuie să fie de tipul dat. Ca $type putem folosi un șir:

  • array
  • list – array indexat după o serie ascendentă de chei numerice începând de la zero
  • bool
  • callable
  • float
  • int
  • null
  • object
  • resource
  • scalar
  • string
  • numele clasei sau direct obiectul, atunci $value trebuie să fie instanceof $type

Assert::exception(callable $callable, string $class, ?string $message=null, $code=null)

La apelarea $callable trebuie să fie aruncată o excepție de clasa $class. Dacă specificăm $message, trebuie să corespundă modelului și mesajul excepției, iar dacă specificăm $code, trebuie să se potrivească strict și codurile.

Următorul test va eșua, deoarece mesajul excepției nu corespunde:

Assert::exception(
	fn() => throw new App\InvalidValueException('Valoare zero'),
	App\InvalidValueException::class,
	'Valoarea este prea mică',
);

Assert::exception() returnează excepția aruncată, astfel se poate testa și o excepție imbricată.

$e = Assert::exception(
	fn() => throw new MyException('Ceva este în neregulă', 0, new RuntimeException),
	MyException::class,
	'Ceva este în neregulă',
);

Assert::type(RuntimeException::class, $e->getPrevious());

Assert::error(string $callable, int|string|array $type, ?string $message=null)

Verifică dacă funcția $callable a generat erorile așteptate (adică avertismente, notificări etc.). Ca $type specificăm una dintre constantele E_..., de exemplu E_WARNING. Și dacă specificăm $message, trebuie să corespundă modelului și mesajul de eroare. De exemplu:

Assert::error(
	fn() => $i++,
	E_NOTICE,
	'Variabilă nedefinită: i',
);

Dacă callback-ul generează mai multe erori, trebuie să le așteptăm pe toate în ordinea exactă. În acest caz, transmitem în $type un array:

Assert::error(function () {
	$a++;
	$b++;
}, [
	[E_NOTICE, 'Variabilă nedefinită: a'],
	[E_NOTICE, 'Variabilă nedefinită: b'],
]);

Dacă specificați ca $type numele unei clase, se comportă la fel ca Assert::exception().

Assert::noError(callable $callable)

Verifică dacă funcția $callable nu a generat niciun avertisment, eroare sau excepție. Este util pentru testarea bucăților de cod unde nu există nicio altă aserțiune.

Assert::match(string $pattern, $actual, ?string $description=null)

$actual trebuie să corespundă modelului $pattern. Putem folosi două variante de modele: expresii regulate sau substituenți.

Dacă transmitem ca $pattern o expresie regulată, pentru delimitarea sa trebuie să folosim ~ sau #, alți delimitatori nu sunt suportați. De exemplu, testul în care $var trebuie să conțină doar cifre hexazecimale:

Assert::match('#^[0-9a-f]$#i', $var);

A doua variantă este similară cu compararea obișnuită a șirurilor, dar în $pattern putem folosi diferiți substituenți:

  • %a% unul sau mai multe caractere, cu excepția caracterelor de sfârșit de linie
  • %a?% zero sau mai multe caractere, cu excepția caracterelor de sfârșit de linie
  • %A% unul sau mai multe caractere, inclusiv caracterele de sfârșit de linie
  • %A?% zero sau mai multe caractere, inclusiv caracterele de sfârșit de linie
  • %s% unul sau mai multe caractere spațiu alb, cu excepția caracterelor de sfârșit de linie
  • %s?% zero sau mai multe caractere spațiu alb, cu excepția caracterelor de sfârșit de linie
  • %S% unul sau mai multe caractere, cu excepția caracterelor spațiu alb
  • %S?% zero sau mai multe caractere, cu excepția caracterelor spațiu alb
  • %c% orice caracter unic, cu excepția caracterului de sfârșit de linie
  • %d% una sau mai multe cifre
  • %d?% zero sau mai multe cifre
  • %i% valoare întreagă cu semn
  • %f% număr zecimal
  • %h% una sau mai multe cifre hexazecimale
  • %w% unul sau mai multe caractere alfanumerice
  • %% caracterul %

Exemple:

# Din nou test pentru număr hexazecimal
Assert::match('%h%', $var);

# Generalizarea căii către fișier și a numărului liniei
Assert::match('Eroare în fișierul %a% la linia %i%', $errorMessage);

Assert::matchFile(string $file, $actual, ?string $description=null)

Aserțiunea este identică cu Assert::match(), dar modelul se încarcă din fișierul $file. Acest lucru este util pentru testarea șirurilor foarte lungi. Fișierul cu testul rămâne lizibil.

Assert::fail(string $message, $actual=null, $expected=null)

Această aserțiune eșuează întotdeauna. Uneori este pur și simplu util. Opțional, putem specifica și valoarea așteptată și cea reală.

Așteptări

Când dorim să comparăm structuri mai complexe cu elemente neconstante, aserțiunile de mai sus s-ar putea să nu fie suficiente. De exemplu, testăm o metodă care creează un nou utilizator și returnează atributele sale ca array. Nu cunoaștem valoarea hash-ului parolei, dar știm că trebuie să fie un șir hexazecimal. Și despre un alt element știm doar că trebuie să fie un obiect DateTime.

În aceste situații putem folosi Tester\Expect în interiorul parametrului $expected al metodelor Assert::equal() și Assert::notEqual(), cu ajutorul cărora structura poate fi descrisă ușor.

use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # așteptăm un număr întreg
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # așteptăm un șir care corespunde modelului
	'created_at' => Expect::type(DateTime::class), # așteptăm o instanță a clasei
], User::create(123, 'milo', 'RandomPaSsWoRd'));

Cu Expect putem efectua aproape aceleași aserțiuni ca și cu Assert. Adică avem la dispoziție metodele Expect::same(), Expect::match(), Expect::count() etc. În plus, le putem înlănțui:

Expect::type(MyIterator::class)->andCount(5);  # așteptăm MyIterator și numărul de elemente 5

Sau putem scrie proprii handleri de aserțiuni.

Expect::that(function ($value) {
	# returnăm false dacă așteptarea eșuează
});

Examinarea aserțiunilor eșuate

Când o aserțiune eșuează, Tester afișează unde este greșeala. Dacă comparăm structuri mai complexe, Tester creează dump-uri ale valorilor comparate și le salvează în directorul output. De exemplu, la eșecul testului fictiv Arrays.recursive.phpt, dump-urile vor fi salvate astfel:

app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # valoarea actuală
	│   └── Arrays.recursive.expected  # valoarea așteptată
	│
	└── Arrays.recursive.phpt          # testul eșuat

Numele directorului poate fi schimbat prin Tester\Dumper::$dumpDir.