# Property-based testing avec Eris : les cas limites qu’on n’imagine pas

> Plutôt que de choisir des exemples fixes, décrire une propriété que le code doit toujours respecter, puis laisser un générateur chercher le cas qui la met en défaut.

- Auteur : Clément Hadrot
- Publié le : 2026-04-09
- Mis à jour le : 2026-04-09
- Catégorie : Tests
- URL : https://wpmoderne.dev.wordpress-developpement.fr/tests/property-based-testing-eris-cas-limites/

## L’essentiel

- On décrit une propriété universelle plutôt qu'un exemple précis
- Le générateur explore des milliers d'entrées automatiquement
- Un cas trouvé est réduit au minimum reproductible

Un test classique choisit ses exemples : on vérifie qu'un slug de trois caractères fonctionne, qu'un slug avec un accent est correctement translittéré, qu'un slug vide renvoie une erreur. Ce sont de bons exemples, mais ce sont les exemples auxquels le développeur a pensé, ce qui signifie qu'ils testent surtout les cas déjà anticipés dans sa tête au moment d'écrire le code. Le property-based testing renverse cette logique : plutôt que de choisir les entrées, on décrit une propriété que le code doit toujours vérifier, quelle que soit l'entrée, et on laisse un générateur aléatoire chercher méthodiquement le cas qui la met en défaut.

## Définition : une propriété plutôt qu'un exemple

Une propriété est une affirmation générale sur le comportement d'une fonction, indépendante d'une valeur d'entrée précise. Pour une fonction de génération de slug, une propriété raisonnable serait : « pour toute chaîne de caractères en entrée, le slug généré ne contient jamais d'espace ni de caractère majuscule ». Cette propriété doit rester vraie pour n'importe quelle chaîne, pas seulement pour les trois exemples qu'un développeur aurait pensé à tester.

## Fonctionnement interne : génération, réduction, vérification

[Eris](https://github.com/giorgiosironi/eris), bibliothèque de property-based testing pour PHPUnit, génère un grand nombre d'entrées aléatoires conformes à un type déclaré (chaîne, entier, tableau), exécute la propriété sur chacune, et si l'une d'elles échoue, applique un algorithme de réduction (« shrinking ») qui cherche automatiquement l'entrée la plus simple possible reproduisant encore l'échec, pour éviter de livrer au développeur une chaîne aléatoire de deux cents caractères illisible.

## Écrire une propriété avec Eris

> L'essentiel à retenir : On décrit une propriété universelle plutôt qu'un exemple précis ; Le générateur explore des milliers d'entrées automatiquement ; Un cas trouvé est réduit au minimum reproductible

```
use Eris\TestTrait;
use Eris\Generator;

class Test_Generation_Slug extends \PHPUnit\Framework\TestCase {
    use TestTrait;

    public function test_slug_ne_contient_jamais_espace_ni_majuscule() {
        $this
            ->forAll( Generator\string() )
            ->then( function( $chaine_aleatoire ) {
                $slug = generer_slug( $chaine_aleatoire );

                $this->assertStringNotContainsString( ' ', $slug );
                $this->assertSame( strtolower( $slug ), $slug );
            } );
    }
}
```

Sur notre fonction `generer_slug()`, cette propriété a immédiatement trouvé un contre-exemple que trois années de tests manuels n'avaient jamais couvert : une chaîne contenant le caractère turc « İ » (I majuscule pointé) produisait, après passage dans `strtolower()` sans locale explicite, un résultat contenant encore un caractère non ASCII qui échappait à la translittération attendue.

```
1) Test_Generation_Slug::test_slug_ne_contient_jamais_espace_ni_majuscule
Falsified after 23 tests, shrinking found minimal failing case:
["İ"]
Failed asserting that 'i̇' is identical to 'i̇'.
// shrinking a réduit une chaîne aléatoire complexe à ce seul caractère isolé
```

## Cas d'usage pertinents pour ce type de test

- Fonctions de validation ou de normalisation qui doivent respecter un invariant quelle que soit l'entrée (slugs, e-mails, numéros de téléphone).
- Fonctions mathématiques ou de tri où une propriété d'ordre ou de conservation doit toujours tenir (un tri ne doit jamais changer le nombre d'éléments).
- Sérialisation et désérialisation : encoder puis décoder une structure doit toujours redonner la structure d'origine, une propriété dite d'aller-retour.

## Pièges à connaître avant d'adopter cette approche

Le premier piège est le temps d'exécution : chaque propriété exécute par défaut une centaine de cas, ce qui multiplie mécaniquement la durée d'une suite si elle est appliquée sans discernement à toutes les fonctions du projet. Le second piège est la difficulté à écrire une propriété pertinente : une propriété trop faible (« la fonction ne lève jamais d'exception ») ne détecte presque rien, alors qu'une propriété trop stricte peut échouer sur des cas qui ne sont en réalité pas des bugs, faute d'avoir correctement borné le générateur d'entrées.

> Une propriété mal choisie est pire qu'aucune propriété : elle donne un faux sentiment de couverture tout en consommant du temps de calcul en CI pour ne rien vérifier d'utile.

## En résumé

Ce billet ne traite pas du mutation testing, une autre approche visant à mesurer la qualité d'une suite existante plutôt qu'à générer de nouveaux cas d'entrée. Le property-based testing complète les tests par exemple classiques sans les remplacer : il excelle à trouver des cas limites qu'aucun développeur n'aurait pensé à écrire à la main, au prix d'un temps d'exécution plus long et d'un effort de conception des propriétés elles-mêmes.
