Assertions
Les assertions servent à confirmer qu'une valeur réelle correspond à la valeur attendue. Ce sont des méthodes
de la classe Tester\Assert.
Choisissez les assertions les mieux adaptées. Assert::same($a, $b) vaut mieux
qu'Assert::true($a === $b), car elle affiche un message d'erreur parlant en cas d'échec. Dans le second cas, nous
n'obtenons que false should be true, ce qui ne nous dit rien du contenu des variables $a et
$b.
La plupart des assertions peuvent aussi recevoir une description facultative dans le paramètre $description,
affichée dans le message d'erreur si l'attente n'est pas satisfaite.
Les exemples supposent que l'alias suivant a été créé :
use Tester\Assert;
Assert::same ($expected, $actual, ?string $description=null)
$expected doit être identique à $actual. C'est la même chose que l'opérateur PHP
===.
Assert::notSame ($expected, $actual, ?string $description=null)
Contraire d'Assert::same(), autrement dit la même chose que l'opérateur PHP !==.
Assert::equal ($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false)
$expected doit être égal à $actual. Contrairement à Assert::same(), l'identité des
objets, l'ordre des paires clé ⇒ valeur dans les tableaux et des nombres décimaux très légèrement différents sont
ignorés, ce qui peut être modifié en définissant $matchIdentity et $matchOrder.
Les cas suivants sont égaux du point de vue d'equal(), mais pas de same() :
Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
['first' => 11, 'second' => 22],
['second' => 22, 'first' => 11],
);
Attention cependant : les tableaux [1, 2] et [2, 1] ne sont pas identiques, car seul l'ordre des
valeurs diffère, pas les paires clé ⇒ valeur. Le tableau [1, 2] peut aussi s'écrire
[0 => 1, 1 => 2], et [1 => 2, 0 => 1] sera donc considéré comme identique.
Vous pouvez aussi utiliser dans $expected ce qu'on appelle les Expectations.
Assert::notEqual ($expected, $actual, ?string $description=null)
Contraire d'Assert::equal().
Assert::contains ($needle, string|array $actual, ?string $description=null)
Si $actual est une chaîne, elle doit contenir la sous-chaîne $needle. Si c'est un tableau, il doit
contenir l'élément $needle (comparaison stricte).
Assert::notContains ($needle, string|array $actual, ?string $description=null)
Contraire d'Assert::contains().
Assert::hasKey (string|int $needle, array $actual, ?string $description=null)
$actual doit être un tableau et doit contenir la clé $needle.
Assert::hasNotKey (string|int $needle, array $actual, ?string $description=null)
$actual doit être un tableau et ne doit pas contenir la clé $needle.
Assert::true ($value, ?string $description=null)
$value doit être true, c'est-à-dire $value === true.
Assert::truthy ($value, ?string $description=null)
$value doit être truthy, c'est-à-dire satisfaire la condition if ($value) ....
Assert::false ($value, ?string $description=null)
$value doit être false, c'est-à-dire $value === false.
Assert::falsey ($value, ?string $description=null)
$value doit être falsey, c'est-à-dire satisfaire la condition if (!$value) ....
Assert::null ($value, ?string $description=null)
$value doit être null, c'est-à-dire $value === null.
Assert::notNull ($value, ?string $description=null)
$value ne doit pas être null, c'est-à-dire $value !== null.
Assert::nan ($value, ?string $description=null)
$value doit être Not a Number. Pour tester les valeurs NAN, utilisez exclusivement Assert::nan(). La
valeur NAN est très particulière et des assertions comme Assert::same() ou Assert::equal() peuvent se
comporter de façon inattendue.
Assert::count ($count, Countable|array $value, ?string $description=null)
Le nombre d'éléments de $value doit être $count. C'est la même chose que
count($value) === $count.
Assert::type (string|object $type, $value, ?string $description=null)
$value doit être du type donné. Comme $type, nous pouvons utiliser une chaîne :
arraylist– tableau indexé par une suite croissante de clés numériques à partir de zéroboolcallablefloatintnullobjectresourcescalarstring- un nom de classe, ou directement un objet, et alors
$value instanceof $typedoit être vrai
Assert::exception (callable $callable, string $class, ?string $message=null, $code=null)
Lors de l'appel de $callable, une exception de la classe $class doit être levée. Si nous indiquons
$message, le message de l'exception doit en plus correspondre au motif. Et si nous
indiquons $code, les codes doivent aussi correspondre strictement.
Par exemple, le test suivant échouera, car le message de l'exception ne correspond pas :
Assert::exception(
fn() => throw new App\InvalidValueException('Zero value'),
App\InvalidValueException::class,
'Value is too low',
);
Assert::exception() renvoie l'exception levée, ce qui vous permet de tester aussi une exception imbriquée.
$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)
Vérifie que la fonction $callable a généré les erreurs attendues (c'est-à-dire warnings, notices, etc.).
Comme $type, indiquez l'une des constantes E_..., par exemple E_WARNING. Et si nous
indiquons $message, le message d'erreur doit en plus correspondre au motif. Par
exemple :
Assert::error(
fn() => $i++,
E_NOTICE,
'Undefined variable: i',
);
Si le callback génère plusieurs erreurs, nous devons toutes les attendre dans l'ordre exact. Dans ce cas, passez un tableau
dans $type :
Assert::error(function () {
$a++;
$b++;
}, [
[E_NOTICE, 'Undefined variable: a'],
[E_NOTICE, 'Undefined variable: b'],
]);
Si vous indiquez un nom de classe comme $type, elle se comporte comme
Assert::exception().
Assert::noError (callable $callable)
Vérifie que la fonction $callable n'a généré aucun warning, erreur ni exception. C'est utile pour tester des
morceaux de code où il n'y a aucune autre assertion.
Assert::match (string $pattern, $actual, ?string $description=null)
$actual doit correspondre au motif $pattern. Nous pouvons utiliser deux variantes de motifs : les
expressions régulières ou les caractères génériques.
Si nous passons une expression régulière comme $pattern, nous devons utiliser ~ ou #
comme délimiteur. Les autres délimiteurs ne sont pas pris en charge. Par exemple, un test où $var ne doit contenir
que des chiffres hexadécimaux :
Assert::match('#^[0-9a-f]+$#i', $var);
La seconde variante ressemble à la comparaison de chaînes ordinaires, mais nous pouvons utiliser dans $pattern
différents caractères génériques :
%a%un ou plusieurs caractères quelconques, sauf les caractères de fin de ligne%a?%zéro ou plusieurs caractères quelconques, sauf les caractères de fin de ligne%A%un ou plusieurs caractères quelconques, y compris les caractères de fin de ligne%A?%zéro ou plusieurs caractères quelconques, y compris les caractères de fin de ligne%s%un ou plusieurs caractères blancs, sauf les caractères de fin de ligne%s?%zéro ou plusieurs caractères blancs, sauf les caractères de fin de ligne%S%un ou plusieurs caractères autres que des caractères blancs%S?%zéro ou plusieurs caractères autres que des caractères blancs%c%un seul caractère de n'importe quelle sorte (sauf fin de ligne)%d%un ou plusieurs chiffres%d?%zéro ou plusieurs chiffres%i%valeur entière signée%f%nombre à virgule flottante%h%un ou plusieurs chiffres hexadécimaux%w%un ou plusieurs caractères alphanumériques%ds%séparateur de répertoires (/ou\)%%un caractère %
Exemples :
# De nouveau, test d'un nombre hexadécimal
Assert::match('%h%', $var);
# Généralisation du chemin de fichier et du numéro de ligne
Assert::match('Error in file %a% on line %i%', $errorMessage);
Assert::notMatch (string $pattern, $actual, ?string $description=null)
Contraire d'Assert::match().
Assert::matchFile (string $file, $actual, ?string $description=null)
Cette assertion est identique à Assert::match(), mais le motif est chargé depuis le fichier
$file. C'est utile pour tester de très longues chaînes. Le fichier de test reste clair.
Assert::fail (string $message, $actual=null, $expected=null)
Cette assertion échoue toujours. C'est parfois simplement utile. Nous pouvons éventuellement indiquer la valeur attendue et la valeur réelle.
Expectations
Lorsque nous voulons comparer des structures plus complexes comportant des éléments non constants, les assertions évoquées
plus haut peuvent ne pas suffire. Par exemple, nous testons une méthode qui crée un nouvel utilisateur et renvoie ses attributs
sous forme de tableau. Nous ne connaissons pas la valeur du hachage du mot de passe, mais nous savons que ce doit être une
chaîne hexadécimale. Et de l'élément suivant, nous savons seulement que ce doit être un objet DateTime.
Dans ces situations, nous pouvons utiliser Tester\Expect à l'intérieur du paramètre $expected des
méthodes Assert::equal() et Assert::notEqual(), ce qui permet de décrire facilement la structure.
use Tester\Expect;
Assert::equal([
'id' => Expect::type('int'), # nous attendons un entier
'username' => 'milo',
'password' => Expect::match('%h%'), # nous attendons une chaîne correspondant au motif
'created_at' => Expect::type(DateTime::class), # nous attendons une instance de la classe
], User::create(123, 'milo', 'RandomPaSsWoRd'));
Avec Expect, nous pouvons effectuer presque les mêmes assertions qu'avec Assert. Nous disposons donc
des méthodes Expect::same(), Expect::match(), Expect::count(), etc. Nous pouvons en outre
les chaîner :
Expect::type(MyIterator::class)->andCount(5); # nous attendons un MyIterator dont le nombre d'éléments est 5
Nous pouvons aussi écrire nos propres gestionnaires d'assertions.
Expect::that(function ($value) {
# renvoie false si l'attente n'est pas satisfaite
});
Explorer les assertions échouées
Lorsqu'une assertion échoue, Tester affiche en quoi consiste l'erreur. Si nous comparons des structures complexes, Tester
crée des dumps des valeurs comparées et les enregistre dans le répertoire output. Par exemple, si le test fictif
Arrays.recursive.phpt échoue, les dumps seront enregistrés ainsi :
app/
└── tests/
├── output/
│ ├── Arrays.recursive.actual # valeur réelle
│ └── Arrays.recursive.expected # valeur attendue
│
└── Arrays.recursive.phpt # test échoué
Nous pouvons changer le nom du répertoire via Tester\Dumper::$dumpDir.