Asserzioni
Le asserzioni sono utilizzate per affermare che un valore reale corrisponde a un valore atteso. Sono metodi della
classe Tester\Assert
.
Scegliere le asserzioni più accurate. È meglio Assert::same($a, $b)
di Assert::true($a === $b)
perché visualizza un messaggio di errore significativo in caso di fallimento. Nel secondo caso si ottiene solo
false should be true
e non dice nulla sul contenuto delle variabili $a e $b.
La maggior parte delle asserzioni può avere anche un'opzione $description
che appare nel messaggio di errore se
l'aspettativa fallisce.
Gli esempi assumono che sia definito il seguente alias di classe:
use Tester\Assert;
Assert::same ($expected, $actual, ?string $description=null)
$expected
deve essere uguale a $actual
. È uguale all'operatore PHP ===
.
Assert::notSame ($expected, $actual, ?string $description=null)
Opposto a Assert::same()
, quindi uguale all'operatore PHP !==
.
Assert::equal ($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false)
$expected
deve essere uguale a $actual
. A differenza di Assert::same()
, vengono
ignorati l'identità degli oggetti, l'ordine delle coppie chiave ⇒ valore negli array e i numeri decimali marginalmente
diversi, che possono essere modificati impostando $matchIdentity
e $matchOrder
.
I casi seguenti sono identici dal punto di vista di equal()
, ma non per same()
:
Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
['first' => 11, 'second' => 22],
['second' => 22, 'first' => 11],
);
Tuttavia, attenzione, l'array [1, 2]
e [2, 1]
non sono uguali, perché solo l'ordine dei valori
differisce, non le coppie chiave ⇒ valore. L'array [1, 2]
può anche essere scritto come
[0 => 1, 1 => 2]
e quindi [1 => 2, 0 => 1]
sarà considerato uguale.
È inoltre possibile utilizzare le cosiddette aspettative in $expected
.
Assert::notEqual ($expected, $actual, ?string $description=null)
Opposto a Assert::equal()
.
Assert::contains ($needle, string|array $actual, ?string $description=null)
Se $actual
è una stringa, deve contenere la sottostringa $needle
. Se è una matrice, deve contenere
l'elemento $needle
(viene confrontato strettamente).
Assert::notContains ($needle, string|array $actual, ?string $description=null)
Opposto a Assert::contains()
.
Assert::hasKey (string|int $needle, array $actual, ?string $description=null)
$actual
deve essere un array e deve contenere la chiave $needle
.
Assert::notHasKey (string|int $needle, array $actual, ?string $description=null)
$actual
deve essere un array e non deve contenere la chiave $needle
.
Assert::true ($value, ?string $description=null)
$value
deve essere true
, quindi $value === true
.
Assert::truthy ($value, ?string $description=null)
$value
deve essere vero, quindi soddisfa la condizione if ($value) ...
.
Assert::false ($value, ?string $description=null)
$value
deve essere false
, quindi $value === false
.
Assert::falsey ($value, ?string $description=null)
$value
deve essere falso, quindi soddisfa la condizione if (!$value) ...
.
Assert::null ($value, ?string $description=null)
$value
deve essere null
, quindi $value === null
.
Assert::notNull ($value, ?string $description=null)
$value
non deve essere null
, quindi $value !== null
.
Assert::nan ($value, ?string $description=null)
$value
deve essere Not a Number. Utilizzare solo Assert::nan()
per i test NAN. Il valore NAN è
molto specifico e le asserzioni Assert::same()
o Assert::equal()
possono comportarsi in modo
imprevedibile.
Assert::count ($count, Countable|array $value, ?string $description=null)
Il numero di elementi in $value
deve essere $count
. Quindi lo stesso di
count($value) === $count
.
Assert::type (string|object $type, $value, ?string $description=null)
$value
deve essere di un determinato tipo. Come $type
possiamo usare stringa:
array
list
– array indicizzato in ordine crescente di chiavi numeriche a partire da zerobool
callable
float
int
null
object
resource
scalar
string
- nome della classe o dell'oggetto direttamente, allora deve passare
$value instanceof $type
Assert::exception (callable $callable, string $class, ?string $message=null, $code=null)
All'invocazione di $callable
deve essere lanciata un'eccezione dell'istanza $class
. Se si passa
$message
, il messaggio dell'eccezione deve corrispondere. E se si passa
$code
, il codice dell'eccezione deve essere lo stesso.
Ad esempio, questo test fallisce perché il messaggio dell'eccezione non corrisponde:
Assert::exception(
fn() => throw new App\InvalidValueException('Zero value'),
App\InvalidValueException::class,
'Value is to low',
);
Assert::exception()
restituisce un'eccezione lanciata, quindi è possibile testare un'eccezione annidata.
$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)
Controlla che l'invocazione di $callable
generi gli errori previsti (cioè avvisi, notifiche, ecc.). Come
$type
si specifica una delle costanti E_...
, ad esempio E_WARNING
. E se si supera
$message
, anche il messaggio di errore deve corrispondere al modello. Ad esempio:
Assert::error(
fn() => $i++,
E_NOTICE,
'Undefined variable: i',
);
Se il callback genera più errori, dobbiamo aspettarceli tutti nell'ordine esatto. In questo caso, passiamo l'array in
$type
:
Assert::error(function () {
$a++;
$b++;
}, [
[E_NOTICE, 'Undefined variable: a'],
[E_NOTICE, 'Undefined variable: b'],
]);
Se $type
è un nome di classe, questa asserzione si comporta come
Assert::exception()
.
Assert::noError (callable $callable)
Controlla che la funzione $callable
non lanci alcun avviso/nota/errore o eccezione PHP. È utile per testare un
pezzo di codice in cui non ci sono altre asserzioni.
Assert::match (string $pattern, $actual, ?string $description=null)
$actual
deve corrispondere a $pattern
. Si possono usare due varianti di pattern: espressioni regolari
o caratteri jolly.
Se si passa un'espressione regolare come $pattern
, si deve usare ~
or #
per delimitarla.
Altri delimitatori non sono supportati. Ad esempio, il test in cui $var
deve contenere solo cifre esadecimali:
Assert::match('#^[0-9a-f]$#i', $var);
L'altra variante è simile al confronto tra stringhe, ma possiamo usare alcuni caratteri jolly in $pattern
:
%a%
uno o più di qualsiasi cosa tranne i caratteri di fine riga%a?%
zero o più di qualsiasi cosa ad eccezione dei caratteri di fine riga%A%
uno o più di qualsiasi cosa compresi i caratteri di fine riga%A?%
zero o più di qualsiasi cosa, compresi i caratteri di fine riga%s%
uno o più caratteri di spazio bianco, eccetto i caratteri di fine riga%s?%
zero o più caratteri di spazio bianco, eccetto i caratteri di fine riga%S%
uno o più caratteri tranne lo spazio bianco%S?%
zero o più caratteri ad eccezione dello spazio bianco%c%
un singolo carattere di qualsiasi tipo (eccetto la fine della riga)%d%
una o più cifre%d?%
zero o più cifre%i%
valore intero firmato%f%
numero in virgola mobile%h%
una o più cifre HEX%w%
uno o più caratteri alfanumerici%%
un carattere %
Esempi:
# Again, hexadecimal number test
Assert::match('%h%', $var);
# Generalized path to file and line number
Assert::match('Error in file %a% on line %i%', $errorMessage);
Assert::matchFile (string $file, $actual, ?string $description=null)
L'asserzione è identica a Assert::match(), ma il modello viene caricato da $file
.
È utile per testare stringhe molto lunghe. Il file di test è leggibile.
Assert::fail (string $message, $actual=null, $expected=null)
Questa asserzione fallisce sempre. È solo comoda. Si possono passare facoltativamente i valori attesi e quelli effettivi.
Aspettative
Se si vogliono confrontare strutture più complesse con elementi non costanti, le asserzioni precedenti potrebbero non essere
sufficienti. Per esempio, testiamo un metodo che crea un nuovo utente e restituisce i suoi attributi come array. Non conosciamo
il valore hash della password, ma sappiamo che deve essere una stringa esadecimale. L'unica cosa che sappiamo dell'elemento
successivo è che deve essere un oggetto DateTime
.
In questi casi, possiamo usare il parametro Tester\Expect
all'interno del parametro $expected
dei
metodi Assert::equal()
e Assert::notEqual()
, che possono essere usati per descrivere facilmente la
struttura.
use Tester\Expect;
Assert::equal([
'id' => Expect::type('int'), # we expect an integer
'username' => 'milo',
'password' => Expect::match('%h%'), # we expect a string matching pattern
'created_at' => Expect::type(DateTime::class), # we expect an instance of the class
], User::create(123, 'milo', 'RandomPaSsWoRd'));
Con Expect
possiamo fare quasi le stesse asserzioni di Assert
. Abbiamo quindi metodi come
Expect::same()
, Expect::match()
, Expect::count()
, ecc. Inoltre, possiamo
concatenarli come:
Expect::type(MyIterator::class)->andCount(5); # we expect MyIterator and items count is 5
Oppure, possiamo scrivere i nostri gestori di asserzioni.
Expect::that(function ($value) {
# return false if expectation fails
});
Indagine sulle asserzioni fallite
Il Tester mostra dove si trova l'errore quando un'asserzione fallisce. Quando si confrontano strutture complesse, il Tester
crea dei dump dei valori confrontati e li salva nella directory output
. Ad esempio, quando il test immaginario
Arrays.recursive.phpt
fallisce, i dump vengono salvati come segue:
app/
└── tests/
├── output/
│ ├── Arrays.recursive.actual # actual value
│ └── Arrays.recursive.expected # expected value
│
└── Arrays.recursive.phpt # failing test
Possiamo cambiare il nome della directory con Tester\Dumper::$dumpDir
.