Assertions

Les assertions sont utilisées pour affirmer qu'une valeur réelle correspond à une valeur attendue. Ce sont des méthodes de l'application Tester\Assert.

Choisissez les assertions les plus précises. Il est préférable d'utiliser Assert::same($a, $b) plutôt que Assert::true($a === $b) car il affiche un message d'erreur significatif en cas d'échec. Dans le deuxième cas, nous obtenons uniquement false should be true et il ne dit rien sur le contenu des variables $a et $b.

La plupart des assertions peuvent également avoir une option $description qui apparaît dans le message d'erreur si l'attente échoue.

Les exemples supposent que l'alias de classe suivant est défini :

use Tester\Assert;

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

$expected doit être le même que $actual. Il est identique à l'opérateur PHP ===.

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

Opposé à Assert::same(), il est donc identique à l'opérateur PHP !==.

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

$expected doit être le même que $actual. Contrairement à Assert::same(), l'identité de l'objet, l'ordre des paires de clés ⇒ valeur dans les tableaux, et les nombres décimaux légèrement différents sont ignorés, ce qui peut être modifié en paramétrant $matchIdentity et $matchOrder.

Les cas suivants sont identiques du point de vue de equal(), mais pas pour same():

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

Cependant, attention, le tableau [1, 2] et [2, 1] ne sont pas égaux, car seul l'ordre des valeurs diffère, pas les paires clé ⇒ valeur. Le tableau [1, 2] peut aussi être écrit comme [0 => 1, 1 => 2] et donc [1 => 2, 0 => 1] seront considérés comme égaux.

Vous pouvez également utiliser ce que l'on appelle les attentes dans $expected.

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

A l'opposé de Assert::equal().

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

Si $actual est une chaîne de caractères, elle doit contenir la sous-chaîne $needle. Si c'est un tableau, il doit contenir l'élément $needle (il est comparé strictement).

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

Opposé à Assert::contains().

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

$actual doit être un tableau et doit contenir la clé $needle.

Assert::notHasKey (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, donc $value === true.

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

$value doit être véridique, donc il satisfait la condition if ($value) ....

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

$value doit être false, donc $value === false.

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

$value doit être faux, donc il remplit la condition if (!$value) ....

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

$value doit être null, donc $value === null.

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

$value ne doit pas être null, donc $value !== null.

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

$value doit être Not a Number. Utilisez uniquement le site Assert::nan() pour les tests NAN. La valeur NAN est très spécifique et les assertions Assert::same() ou Assert::equal() peuvent se comporter de manière imprévisible.

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

Le nombre d'éléments dans $value doit être $count. Donc identique à count($value) === $count.

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

$value doit être d'un type donné. Comme $type nous pouvons utiliser une chaîne de caractères :

  • array
  • list – tableau indexé dans l'ordre croissant des clés numériques à partir de zéro.
  • bool
  • callable
  • float
  • int
  • null
  • object
  • resource
  • scalar
  • string
  • nom de la classe ou de l'objet directement, alors il faut passer $value instanceof $type

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

Lors de l'invocation de $callable, une exception de l'instance $class doit être levée. Si nous passons $message, le message de l'exception doit correspondre. Et si nous passons $code, le code de l'exception doit être le même.

Par exemple, ce test échoue car le message de l'exception ne correspond pas :

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

Le site Assert::exception() renvoie une exception levée, vous pouvez donc tester 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 l'invocation de $callable génère les erreurs attendues (c'est-à-dire les avertissements, les avis, etc.). Comme $type nous spécifions une des constantes E_..., par exemple E_WARNING. Et si on passe $message, le message d'erreur doit également correspondre au modèle. Par exemple :

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

Si le callback génère plus d'erreurs, nous devons les attendre toutes dans l'ordre exact. Dans ce cas, nous passons le tableau à $type:

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

Si $type est le nom de la classe, cette assertion se comporte de la même manière que Assert::exception().

Assert::noError (callable $callable)

Vérifie que la fonction $callable ne lève aucun avertissement/notice/erreur ou exception PHP. Elle est utile pour tester un morceau de code où il n'y a pas d'autre assertion.

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

$actual doit correspondre à $pattern. Nous pouvons utiliser deux variantes de motifs : les expressions régulières ou les caractères génériques.

Si nous transmettons une expression régulière à $pattern, nous devons utiliser ~ or # pour la délimiter. Les autres délimiteurs ne sont pas pris en charge. Par exemple, test où $var ne doit contenir que des chiffres hexadécimaux :

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

L'autre variante est similaire à la comparaison de chaînes de caractères, mais nous pouvons utiliser certains caractères génériques dans $pattern:

  • %a% un ou plusieurs caractères, à l'exception des caractères de fin de ligne
  • %a?% zéro ou plus de tout sauf les caractères de fin de ligne
  • %A% un ou plusieurs caractères, y compris les caractères de fin de ligne
  • %A?% zéro ou plus de tout, y compris les caractères de fin de ligne
  • %s% un ou plusieurs caractères d'espace blanc, à l'exception des caractères de fin de ligne
  • %s?% zéro ou plusieurs caractères d'espace blanc, à l'exception des caractères de fin de ligne
  • %S% un ou plusieurs caractères sauf l'espace blanc
  • %S?% zéro ou plus de caractères sauf l'espace blanc
  • %c% un seul caractère de n'importe quelle sorte (sauf les caractères de fin de ligne)
  • %d% un ou plusieurs chiffres
  • %d?% zéro ou plusieurs chiffres
  • %i% valeur entière signée
  • %f% un nombre à virgule flottante
  • %h% un ou plusieurs chiffres HEX
  • %w% un ou plusieurs caractères alphanumériques
  • %% un caractère %.

Exemples :

# 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'assertion est identique à Assert::match() mais le motif est chargé depuis $file. Elle est utile pour tester des chaînes très longues. Le fichier de test est lisible.

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

Cette assertion échoue toujours. Elle est simplement pratique. Nous pouvons optionnellement passer les valeurs attendues et réelles.

Attentes

Si nous voulons comparer des structures plus complexes avec des éléments non constants, les assertions ci-dessus peuvent ne pas être suffisantes. 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 de hachage du mot de passe, mais nous savons qu'il doit être une chaîne hexadécimale. Et la seule chose que nous savons de l'élément suivant est qu'il doit être un objet DateTime.

Dans ces cas, nous pouvons utiliser le paramètre Tester\Expect à l'intérieur du paramètre $expected des méthodes Assert::equal() et Assert::notEqual(), qui peuvent être utilisées pour décrire facilement la structure.

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'));

Avec Expect, nous pouvons faire presque les mêmes affirmations qu'avec Assert. Nous avons donc des méthodes comme Expect::same(), Expect::match(), Expect::count(), etc. De plus, nous pouvons les enchaîner comme suit :

Expect::type(MyIterator::class)->andCount(5);  # we expect MyIterator and items count is 5

Ou bien, nous pouvons écrire nos propres gestionnaires d'assertions.

Expect::that(function ($value) {
	# return false if expectation fails
});

Enquête sur les assertions échouées

Le testeur montre où se trouve l'erreur lorsqu'une assertion échoue. Lorsque nous comparons des structures complexes, le testeur crée des dumps des valeurs comparées et les enregistre dans le répertoire output. Par exemple, lorsque le test imaginaire Arrays.recursive.phpt échoue, les vidages seront enregistrés comme suit :

app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # actual value
	│   └── Arrays.recursive.expected  # expected value
	│
	└── Arrays.recursive.phpt          # failing test

Nous pouvons changer le nom du répertoire par Tester\Dumper::$dumpDir.