Article 05·general·2 min de lecture

Property-based testing avec fast-check

1000 inputs aléatoires trouvent des bugs qu'aucun exemple ne pourrait détecter.

Les tests par exemple vérifient que f(input1) = output1 pour un set d'inputs choisis manuellement. Les tests par propriété (property-based testing, PBT) vérifient une propriété universelle ∀ input, P(f(input)) pour un grand nombre d'inputs générés aléatoirement. La bibliothèque de référence en JavaScript est fast-check (https://github.com/dubzzz/fast-check), inspirée de QuickCheck (Haskell, 2000). Exemple concret : tester une fonction de tri. Test par exemple : sort([3,1,2]) === [1,2,3]. Test par propriété : ∀ array, sort(array).length === array.length ∧ sort(array) est trié ∧ sort(sort(array)) === sort(array) (idempotence). fast-check génère 100 arrays aléatoires (taille 0 à 100, valeurs -1000 à 1000) et vérifie les 3 propriétés sur chaque. Sur SallyCards, on a attrapé 3 bugs en production via PBT : (a) le moteur Spider acceptait des mouvements de runs mixed-suit quand la pioche était vide — découvert par fast-check qui a généré un état de jeu exotique impossible à reproduire manuellement ; (b) le sérialiseur de replays produisait `undefined` pour les dates avant 1970 (epoch Unix) — découvert par une propriété ∀ replay, JSON.parse(JSON.stringify(replay)) === replay ; (c) l'action describer crashait sur les noms de cartes avec emojis — découvert par fast-check qui utilise Unicode complet par défaut. L'écriture d'une propriété demande un peu de pratique. Le piège classique : tester f(x) = f(x) (toujours vrai, ne teste rien). Les bonnes propriétés sont : (1) invariants (taille, somme, ordre), (2) rond-trip (encode/decode, save/load), (3) idempotence (sort(sort(x)) = sort(x)), (4) symétrie/commutativité (a + b = b + a), (5) métamorphes (sort([...a, x]) contient x). Sur la suite SallyCards, on a 38 propriétés actives qui couvrent les 7 moteurs de jeu. La performance : fast-check tourne 100 runs par défaut, ~50-200 ms par propriété. Pour le CI on garde 100, pour le pre-commit on descend à 30 (rapide). Lors d'une investigation de bug, on peut booster à 10 000 runs pour augmenter la chance de reproduction. fast-check fait du "shrinking" : quand un test échoue sur un input complexe (array de 87 éléments), il essaie automatiquement de trouver le plus petit input qui échoue (peut-être array de 2 éléments) — précieux pour debugger. Anti-pattern : remplacer TOUS les tests par exemple par des propriétés. Garde des examples pour les cas critiques connus (régression d'un bug spécifique), où l'input précis a une valeur documentaire. Les propriétés viennent en complément, pas en remplacement. Ratio typique : 80 % exemples + 20 % propriétés sur les modules métier.
property-basedfast-check
IK

ÉCRIT PAR

Idriss Kriouile

Fondateur de SallyStar · Full-stack engineer · Morocco

Idriss Kriouile — DevOps · Test QA Manager · Tech Lead DevOps & QA