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 nulybool
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
.