Nette Documentation Preview

syntax
Aserce
******

.[perex]
Aserce se používají k potvrzení, že skutečná hodnota odpovídá očekávané hodnotě. Jde o metody třídy `Tester\Assert`.

Vybírejte co nejvhodnější aserce. Je lepší `Assert::same($a, $b)` než `Assert::true($a === $b)`, protože při selhání zobrazí smysluplnou chybovou zprávu. Ve druhém případě pouze `false should be true` což nám o obsahu proměnných `$a` a `$b` nic neříká.

Většina assercí také může mít volitelnou popisku v parametru `$description`, která se zobrazí v chybové hlášce, pokud očekávání selže.

Příklady předpokládají vytvořený alias:

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


Assert::same($expected, $actual, ?string $description=null) .[method]
---------------------------------------------------------------------
`$expected` musí být totožný s `$actual`. To samé jako PHP operátor `===`.


Assert::notSame($expected, $actual, ?string $description=null) .[method]
------------------------------------------------------------------------
Opak `Assert::same()`, tedy to samé jako PHP operátor `!==`.


Assert::equal($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false) .[method]
-------------------------------------------------------------------------------------------------------------------------
`$expected` musí být stejný s `$actual`. Na rozdíl od `Assert::same()` se ignoruje identita objektů, pořadí dvojic klíčů => hodnota v polích a marginálně odlišná desetinná čísla, což lze změnit nastavením `$matchIdentity` a `$matchOrder`.

Následující případy jsou shodné z pohledu `equal()`, ale nikoliv `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],
);
```

Ovšem pozor, pole `[1, 2]` a `[2, 1]` stejné nejsou, protože se liší jen pořadí hodnot, nikoliv dvojic klíč => hodnota. Pole `[1, 2]` lze zapsat také jako `[0 => 1, 1 => 2]` a za stejné se proto bude považovat `[1 => 2, 0 => 1]`.

Dále lze v `$expected` použít tzv. [#očekávání].


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


Assert::contains($needle, string|array $actual, ?string $description=null) .[method]
------------------------------------------------------------------------------------
Pokud je `$actual` řetězec, musí obsahovat podřetězec `$needle`. Pokud je pole, musí obsahovat prvek `$needle` (porovnává se striktně).


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


Assert::hasKey(string|int $needle, array $actual, ?string $description=null) .[method]{data-version:2.4}
--------------------------------------------------------------------------------------------------------
`$actual` musí být pole a musí obsahovat klíč `$needle`.


Assert::notHasKey(string|int $needle, array $actual, ?string $description=null) .[method]{data-version:2.4}
-----------------------------------------------------------------------------------------------------------
`$actual` musí být pole a nesmí obsahovat klíč `$needle`.


Assert::true($value, ?string $description=null) .[method]
---------------------------------------------------------
`$value` musí být `true`, tedy `$value === true`.


Assert::truthy($value, ?string $description=null) .[method]
-----------------------------------------------------------
`$value` musí být pravdivý, tedy splní podmínku `if ($value) ...`.


Assert::false($value, ?string $description=null) .[method]
----------------------------------------------------------
`$value` musí být `false`, tedy `$value === false`.


Assert::falsey($value, ?string $description=null) .[method]
-----------------------------------------------------------
`$value` musí být nepravdivý, tedy splní podmínku `if (!$value) ...`.


Assert::null($value, ?string $description=null) .[method]
---------------------------------------------------------
`$value` musí být `null`, tedy `$value === null`.


Assert::notNull($value, ?string $description=null) .[method]
------------------------------------------------------------
`$value` nesmí být `null`, tedy `$value !== null`.


Assert::nan($value, ?string $description=null) .[method]
--------------------------------------------------------
`$value` musí být Not a Number. Pro testování NAN hodnoty používejte vyhradně `Assert::nan()`. Hodnota NAN je velmi specifická a aserce `Assert::same()` nebo `Assert::equal()` mohou fungovat neočekávaně.


Assert::count($count, Countable|array $value, ?string $description=null) .[method]
----------------------------------------------------------------------------------
Počet prvků ve `$value` musí být `$count`. Tedy to samé jako `count($value) === $count`.


Assert::type(string|object $type, $value, ?string $description=null) .[method]
------------------------------------------------------------------------------
`$value` musí být daného typu. Jako `$type` můžeme použít řetězec:
- `array`
- `list` - pole indexované podle vzestupné řady numerických klíčů od nuly
- `bool`
- `callable`
- `float`
- `int`
- `null`
- `object`
- `resource`
- `scalar`
- `string`
- název třídy nebo přímo objekt, potom musí být `$value instanceof $type`


Assert::exception(callable $callable, string $class, ?string $message=null, $code=null) .[method]
-------------------------------------------------------------------------------------------------
Při zavolání `$callable` musí být vyhozena výjimka třídy `$class`. Pokud uvedeme `$message`, musí [odpovídat vzoru|#assert-match] i zpráva výjimky a pokud uvedeme `$code`, musí se striktně shodovat i kódy.

Následující test selže, protože neodpovídá zpráva výjimky:

```php
Assert::exception(
	fn() => throw new App\InvalidValueException('Zero value'),
	App\InvalidValueException::class,
	'Value is to low',
);
```

`Assert::exception()` vrací vyhozenou výjimku, lze tak otestovat i výjimku zahnízděnou.

```php
$e = Assert::exception(
	fn() => throw new MyException('Something is wrong', 0, new RuntimeException),
	MyException::class,
	'Something is wrong',
);

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


Assert::error(string $callable, int|string|array $type, ?string $message=null) .[method]
----------------------------------------------------------------------------------------
Kontroluje, že funkce `$callable` vygenerovala očekávané chyby (tj. varování, notices atd). Jako `$type` uvedeme jednu z konstant `E_...`, tedy například `E_WARNING`. A pokud uvedeme `$message`, musí [odpovídat vzoru|#assert-match] i chybová zpráva. Například:

```php
Assert::error(
	fn() => $i++,
	E_NOTICE,
	'Undefined variable: i',
);
```

Pokud callback vygeneruje více chyb, musíme je všechny očekávat v přesném pořadí. V takovém případě předáme v `$type` pole:

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

.[note]
Pokud jako `$type` uvedete název třídy, chová se stejně jako `Assert::exception()`.


Assert::noError(callable $callable) .[method]
---------------------------------------------
Kontroluje, že funkce `$callable` nevygenerovala žádné varování, chybu nebo výjimku. Hodí se pro testování kousků kódu, kde není žádná další aserce.


Assert::match(string $pattern, $actual, ?string $description=null) .[method]
----------------------------------------------------------------------------
`$actual` musí vyhovět vzoru `$pattern`. Můžeme použít dvě varianty vzorů: regulární výrazy nebo zástupné znaky.

Pokud jako `$pattern` předáme regulární výraz, k jeho ohraničení musíme použít `~` nebo `#`, jiné oddělovače nejsou podporovány. Například test, kdy `$var` musí obsahovat pouze hexadecimální číslice:

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

Druhá varianta je podobná běžnému porovnání řetězců, ale v `$pattern` můžeme použít různé zástupné znaky:

- `%a%` jeden nebo více znaků, kromě znaků konce řádku
- `%a?%` žádný nebo více znaků, kromě znaků konce řádku
- `%A%` jeden nebo více znaků, včetně znaků konce řádku
- `%A?%` žádný nebo více znaků, včetně znaků konce řádku
- `%s%` jeden nebo více bílých znaků, kromě znaků konce řádku
- `%s?%` žádný nebo více bílých znaků, kromě znaků konce řádku
- `%S%` jeden nebo více znaků, kromě bílých znaků
- `%S?%` žádný nebo více znaků, kromě bílých znaků
- `%c%` jakýkoli jeden znak, kromě znaku konce řádku
- `%d%` jedna nebo více číslic
- `%d?%` žádná nebo více číslic
- `%i%` znaménková celočíselná hodnota
- `%f%` číslo s desetinnou čárkou
- `%h%` jedna nebo více hexadecimálních číslic
- `%w%` jeden nebo více alfanumerických znaků
- `%%` znak %

Příklady:

```php
# Opět test na hexadecimální číslo
Assert::match('%h%', $var);

# Zobecnění cesty k souboru a čísla řádky
Assert::match('Error in file %a% on line %i%', $errorMessage);
```


Assert::matchFile(string $file, $actual, ?string $description=null) .[method]
-----------------------------------------------------------------------------
Aserce je totožná s [Assert::match() |#assert-match], ale vzor se načítá ze souboru `$file`. To je užitečné pro testování velmi dlouhých řetězců. Soubor s testem zůstane přehledný.


Assert::fail(string $message, $actual=null, $expected=null) .[method]
---------------------------------------------------------------------
Tato aserce vždy selže. Někdy se to prostě hodí. Volitelně můžeme uvést i očekávanou a aktuální hodnotu.


Očekávání
---------
Když chceme porovnat složitější struktury s nekonstantními prvky, nemusí být výše uvedené aserce dostatečné. Například testujeme metodu, která vytváří nového uživatele a vrací jeho atributy jako pole. Hodnotu hashe hesla neznáme, ale víme, to že musí být hexadecimální řetězec. A o dalším prvku víme jen, že to musí být objekt `DateTime`.

V těchto situacích můžeme použít `Tester\Expect` uvnitř `$expected` parametru metod `Assert::equal()` a `Assert::notEqual()`, pomocí kterých lze strukturu snadno popsat.

```php
use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # očekáváme celé číslo
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # očekáváme řetězec vyhovující vzoru
	'created_at' => Expect::type(DateTime::class), # očekáváme instanci třídy
], User::create(123, 'milo', 'RandomPaSsWoRd'));
```

S `Expect` můžeme provádět téměř stejné aserce jako s `Assert`. Tedy jsou nám k dispozici metody `Expect::same()`, `Expect::match()`, `Expect::count()` atd. Navíc je můžeme zřetězit:

```php
Expect::type(MyIterator::class)->andCount(5);  # očekáváme MyIterator a počet prvků 5
```

Anebo můžeme psát vlastní handlery asercí.

```php
Expect::that(function ($value) {
	# vrátíme false, pokud očekávání selže
});
```


Zkoumání chybných asercí
------------------------
Když aserce selže, Tester vypíše, v čem je chyba. Pokud porovnáváme složitější struktury, Tester vytvoří dumpy porovnávaných hodnot a uloží je do adresáře `output`. Například při selhání smyšleného testu `Arrays.recursive.phpt` budou dumpy uloženy následovně:

```
app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # aktuální hodnota
	│   └── Arrays.recursive.expected  # očekávaná hodnota
	│
	└── Arrays.recursive.phpt          # selhávající test
```

Název adresáře můžeme změnit přes `Tester\Dumper::$dumpDir`.

Aserce

Aserce se používají k potvrzení, že skutečná hodnota odpovídá očekávané hodnotě. Jde o metody třídy Tester\Assert.

Vybírejte co nejvhodnější aserce. Je lepší Assert::same($a, $b) než Assert::true($a === $b), protože při selhání zobrazí smysluplnou chybovou zprávu. Ve druhém případě pouze false should be true což nám o obsahu proměnných $a a $b nic neříká.

Většina assercí také může mít volitelnou popisku v parametru $description, která se zobrazí v chybové hlášce, pokud očekávání selže.

Příklady předpokládají vytvořený alias:

use Tester\Assert;

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

$expected musí být totožný s $actual. To samé jako PHP operátor ===.

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

Opak Assert::same(), tedy to samé jako PHP operátor !==.

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

$expected musí být stejný s $actual. Na rozdíl od Assert::same() se ignoruje identita objektů, pořadí dvojic klíčů ⇒ hodnota v polích a marginálně odlišná desetinná čísla, což lze změnit nastavením $matchIdentity a $matchOrder.

Následující případy jsou shodné z pohledu equal(), ale nikoliv same():

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

Ovšem pozor, pole [1, 2] a [2, 1] stejné nejsou, protože se liší jen pořadí hodnot, nikoliv dvojic klíč ⇒ hodnota. Pole [1, 2] lze zapsat také jako [0 => 1, 1 => 2] a za stejné se proto bude považovat [1 => 2, 0 => 1].

Dále lze v $expected použít tzv. očekávání.

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

Opak Assert::equal().

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

Pokud je $actual řetězec, musí obsahovat podřetězec $needle. Pokud je pole, musí obsahovat prvek $needle (porovnává se striktně).

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

Opak Assert::contains().

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

$actual musí být pole a musí obsahovat klíč $needle.

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

$actual musí být pole a nesmí obsahovat klíč $needle.

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

$value musí být true, tedy $value === true.

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

$value musí být pravdivý, tedy splní podmínku if ($value) ....

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

$value musí být false, tedy $value === false.

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

$value musí být nepravdivý, tedy splní podmínku if (!$value) ....

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

$value musí být null, tedy $value === null.

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

$value nesmí být null, tedy $value !== null.

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

$value musí být Not a Number. Pro testování NAN hodnoty používejte vyhradně Assert::nan(). Hodnota NAN je velmi specifická a aserce Assert::same() nebo Assert::equal() mohou fungovat neočekávaně.

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

Počet prvků ve $value musí být $count. Tedy to samé jako count($value) === $count.

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

$value musí být daného typu. Jako $type můžeme použít řetězec:

  • array
  • list – pole indexované podle vzestupné řady numerických klíčů od nuly
  • bool
  • callable
  • float
  • int
  • null
  • object
  • resource
  • scalar
  • string
  • název třídy nebo přímo objekt, potom musí být $value instanceof $type

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

Při zavolání $callable musí být vyhozena výjimka třídy $class. Pokud uvedeme $message, musí odpovídat vzoru i zpráva výjimky a pokud uvedeme $code, musí se striktně shodovat i kódy.

Následující test selže, protože neodpovídá zpráva výjimky:

Assert::exception(
	fn() => throw new App\InvalidValueException('Zero value'),
	App\InvalidValueException::class,
	'Value is to low',
);

Assert::exception() vrací vyhozenou výjimku, lze tak otestovat i výjimku zahnízděnou.

$e = Assert::exception(
	fn() => throw new MyException('Something is wrong', 0, new RuntimeException),
	MyException::class,
	'Something is wrong',
);

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

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

Kontroluje, že funkce $callable vygenerovala očekávané chyby (tj. varování, notices atd). Jako $type uvedeme jednu z konstant E_..., tedy například E_WARNING. A pokud uvedeme $message, musí odpovídat vzoru i chybová zpráva. Například:

Assert::error(
	fn() => $i++,
	E_NOTICE,
	'Undefined variable: i',
);

Pokud callback vygeneruje více chyb, musíme je všechny očekávat v přesném pořadí. V takovém případě předáme v $type pole:

Assert::error(function () {
	$a++;
	$b++;
}, [
	[E_NOTICE, 'Undefined variable: a'],
	[E_NOTICE, 'Undefined variable: b'],
]);

Pokud jako $type uvedete název třídy, chová se stejně jako Assert::exception().

Assert::noError(callable $callable)

Kontroluje, že funkce $callable nevygenerovala žádné varování, chybu nebo výjimku. Hodí se pro testování kousků kódu, kde není žádná další aserce.

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

$actual musí vyhovět vzoru $pattern. Můžeme použít dvě varianty vzorů: regulární výrazy nebo zástupné znaky.

Pokud jako $pattern předáme regulární výraz, k jeho ohraničení musíme použít ~ nebo #, jiné oddělovače nejsou podporovány. Například test, kdy $var musí obsahovat pouze hexadecimální číslice:

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

Druhá varianta je podobná běžnému porovnání řetězců, ale v $pattern můžeme použít různé zástupné znaky:

  • %a% jeden nebo více znaků, kromě znaků konce řádku
  • %a?% žádný nebo více znaků, kromě znaků konce řádku
  • %A% jeden nebo více znaků, včetně znaků konce řádku
  • %A?% žádný nebo více znaků, včetně znaků konce řádku
  • %s% jeden nebo více bílých znaků, kromě znaků konce řádku
  • %s?% žádný nebo více bílých znaků, kromě znaků konce řádku
  • %S% jeden nebo více znaků, kromě bílých znaků
  • %S?% žádný nebo více znaků, kromě bílých znaků
  • %c% jakýkoli jeden znak, kromě znaku konce řádku
  • %d% jedna nebo více číslic
  • %d?% žádná nebo více číslic
  • %i% znaménková celočíselná hodnota
  • %f% číslo s desetinnou čárkou
  • %h% jedna nebo více hexadecimálních číslic
  • %w% jeden nebo více alfanumerických znaků
  • %% znak %

Příklady:

# Opět test na hexadecimální číslo
Assert::match('%h%', $var);

# Zobecnění cesty k souboru a čísla řádky
Assert::match('Error in file %a% on line %i%', $errorMessage);

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

Aserce je totožná s Assert::match(), ale vzor se načítá ze souboru $file. To je užitečné pro testování velmi dlouhých řetězců. Soubor s testem zůstane přehledný.

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

Tato aserce vždy selže. Někdy se to prostě hodí. Volitelně můžeme uvést i očekávanou a aktuální hodnotu.

Očekávání

Když chceme porovnat složitější struktury s nekonstantními prvky, nemusí být výše uvedené aserce dostatečné. Například testujeme metodu, která vytváří nového uživatele a vrací jeho atributy jako pole. Hodnotu hashe hesla neznáme, ale víme, to že musí být hexadecimální řetězec. A o dalším prvku víme jen, že to musí být objekt DateTime.

V těchto situacích můžeme použít Tester\Expect uvnitř $expected parametru metod Assert::equal() a Assert::notEqual(), pomocí kterých lze strukturu snadno popsat.

use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # očekáváme celé číslo
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # očekáváme řetězec vyhovující vzoru
	'created_at' => Expect::type(DateTime::class), # očekáváme instanci třídy
], User::create(123, 'milo', 'RandomPaSsWoRd'));

S Expect můžeme provádět téměř stejné aserce jako s Assert. Tedy jsou nám k dispozici metody Expect::same(), Expect::match(), Expect::count() atd. Navíc je můžeme zřetězit:

Expect::type(MyIterator::class)->andCount(5);  # očekáváme MyIterator a počet prvků 5

Anebo můžeme psát vlastní handlery asercí.

Expect::that(function ($value) {
	# vrátíme false, pokud očekávání selže
});

Zkoumání chybných asercí

Když aserce selže, Tester vypíše, v čem je chyba. Pokud porovnáváme složitější struktury, Tester vytvoří dumpy porovnávaných hodnot a uloží je do adresáře output. Například při selhání smyšleného testu Arrays.recursive.phpt budou dumpy uloženy následovně:

app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # aktuální hodnota
	│   └── Arrays.recursive.expected  # očekávaná hodnota
	│
	└── Arrays.recursive.phpt          # selhávající test

Název adresáře můžeme změnit přes Tester\Dumper::$dumpDir.