PEST soit des tests unitaires

Posté par  . Édité par Ysabeau 🧶, barmic 🦦, Yves Bourguignon et BAud. Modéré par bobble bubble. Licence CC By‑SA.
Étiquettes :
26
1
déc.
2021
PHP

PEST est un nouveau framework en PHP, permettant de rédiger des tests unitaires.
Basé sur le célèbre (pour ceux qui développent en PHP) PHUnit, PEST a pour lui d’être plus élégant et simple à utiliser, et apporte notamment une plus grande fluidité dans l’écriture des tests unitaires.
Il a été créé par Nuno Maduro, membre de la core team de Laravel, en sponsorware. Depuis le printemps 2020, il est publié sous licence MIT. Il ne possède aucune filiation avec Laravel, on peut tout à fait s’en servir sans.
Petit tour d’horizon !

(tous les exemples de code de cette dépêche proviennent de la documentation officielle de PEST)

Sommaire

Premiers tests

Pour commencer, voyons à quoi ressemble un test basique, avec PEST.

<?php

test('asserts true is true', function () {
    $this->assertTrue(true);

    expect(true)->toBeTrue();
});

Contrairement à PHPUnit, qui regroupe chaque test dans une méthode de classe, chaque test dans PEST consiste à appeler la fonction test en lui passant 2 paramètres : la description du test (texte libre) et une fonction anonyme (Closure) contenant le code du test proprement dit. Le tout dans un simple fichier PHP (pas de classe, pas de namespace, rien de tout ça n’est nécessaire).

Comme PEST repose sur PHPUnit, on peut utiliser les méthodes assertXXX déjà existantes et connues de tous, à travers $this (qui est un objet de type PHPUnit\Framework\TestCase, ce qui montre que PEST est une surcouche à PHPUnit, avec du sucre syntaxique dedans).
Mais on peut aussi utiliser les nouvelles méthodes toBeXXX mises à notre disposition par PEST, et dont on parlera plus loin.

Si le test ci-dessus réussit, PEST affichera :

 asserts true is true

À noter que la fonction test a une petite sœur : it (même signature). La seule différence est que cette dernière affichera ceci, si le test réussit :

 it asserts true is true

Expectations

Bien que PEST puisse s’utiliser comme PHPUnit (ce qui permet doucement de basculer d’un framework à l’autre), on peut bien évidemment aller plus loin, en tirant profit de sa spécificité : les expectations.
Concrètement, cela consiste en une méthode expect, à qui on passe la variable à tester, et toute une série de méthodes fluides (fluent interfaces) qui vont chacune vérifier que la variable respecte telle ou telle règle.

Reprenons l’exemple du début :

<?php

test('expect true to be true', function () {
  // assertion
  $this->assertTrue(true);

  // expectation
  expect(true)->toBe(true);
});

On voit que la méthode toBe vérifie que la valeur true (passée en paramètre de la fonction expect) est bien égale à true.

Voyons maintenant un cas un peu plus poussé (et plus proche de ce qu’on peut être amené à écrire, dans la vraie vie) :

expect($user)->toBeInstanceOf(User::class);

Ici, la méthode toBeInstanceOf vérifie que la variable $user est bien de type User.

On ne va pas lister ici la palanquée de méthodes toBeXXX qui existent : la documentation est là pour ça, avec un exemple pour chacune d’elles.

Rappelons toutefois qu’il est tout à fait possible de chaîner les appels de méthodes, pour tester plusieurs choses sur une même valeur.

Exemple :

expect('{"name":"Nuno","credit":1000.00}')
    ->json()
    ->toHaveCount(2);

Higher Order Tests

Avec le chaînage des méthodes vient aussi une autre possibilité de PEST : pouvoir tester les valeurs au sein de la variable passée à la fonction expect. Ainsi, plutôt que de faire plusieurs appels à expect (ce qui reste tout à fait possible), comme suit :

expect($user->first_name)->toEqual('Nuno');
expect($user->last_name)->toEqual('Maduro');
expect($user->withTitle('Mr'))->toEqual('Mr Nuno Maduro');

On peut tout à fait faire ceci :

expect($user)
    ->first_name->toEqual('Nuno')
    ->last_name->toEqual('Maduro')
    ->withTitle('Mr')->toEqual('Mr Nuno Maduro');

Et là, on commence à toucher à ce qui rend PEST si attrayant : le chaînage est sans limite ! On peut tester toutes les données contenues à l’intérieur de la valeur passée en paramètre à expect, que ce soit les propriétés d’un objet, comme on vient de le voir, ou les différents éléments d’un tableau, comme ci-dessous :

expect(['name' => 'Nuno', 'companies' => ['Pest', 'Laravel']])
    ->name->toEqual('Nuno')
    ->companies->toHaveCount(2)->each->toBeString

(notez l’usage de each, qui permet de boucler sur les sous-éléments du tableau)

Comme dit précédemment, il n’y a pas de limite dans le chaînage, ni dans le parcours en profondeur d’un objet ou d’un tableau.
Ainsi, il est tout à fait possible d’aller chercher les propriétés d’un objet, et de tester qu’elles répondent elles aussi à certains critères.

Exemple :

expect($user)
    ->companies->first()->owner->toBeInstanceOf(User::class)->not->toEqual($user)
    ->name->toEqual('Nuno');

Dans cet exemple, on comprend que l’objet $user a 2 propriétés testées : companies (qui semble renvoyer une liste d’objets), dont on vérifie que le owner du premier élément est bien un objet de type User, mais n’est pas $user. Et name, dont on vérifie la valeur. Ici, name se rapporte à $user, et non à companies : PEST est capable de revenir à l’objet initialement traité (celui passé en paramètre à expect). Et ce, sans limite !

Custom expectations

La liste des expectations a beau être très grande, et de nouvelles méthodes apparaissent chaque semaine, il y a toujours un moment où il manque celle dont on a besoin, pour couvrir son cas particulier.

Fort heureusement, PEST permet l’ajout de nouvelles méthodes, grâce à la méthode extend().

Exemple :

// tests/Pest.php
expect()->extend('toBeWithinRange', function ($min, $max) {
    return $this->toBeGreaterThanOrEqual($min)
                ->toBeLessThanOrEqual($max);
});

test('numeric ranges', function () {
    expect(100)->toBeWithinRange(90, 110);
});

Aller plus loin

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.