Découvrez un prompt de génération de tests Claude Code qui produit des tests à fort signal en ciblant les frontières, invariants et modes de défaillance, plutôt que les happy paths.

Les suites de tests auto-générées ont souvent l'air impressionnantes : des dizaines de tests, beaucoup de code d'initialisation, et chaque nom de fonction apparaît quelque part. Mais beaucoup de ces tests ne sont que des vérifications « ça marche quand tout est normal ». Ils réussissent facilement, attrapent rarement des bugs, et coûtent du temps à lire et à maintenir.
Avec un prompt typique de génération de tests Claude Code, le modèle tend à refléter les exemples qu'il voit. Vous obtenez des variations qui semblent différentes mais qui couvrent le même comportement. Le résultat est une grosse suite avec une couverture faible là où cela compte.
Les tests à fort signal sont différents. Ce sont le petit ensemble qui aurait attrapé l'incident du mois dernier. Ils échouent quand le comportement change de façon risquée, et restent stables lors de refactorings bénins. Un test à fort signal peut valoir vingt vérifications « renvoie la valeur attendue ».
La génération en mode happy-path a habituellement quelques symptômes clairs :
Imaginez une fonction qui applique un code de réduction. Les tests happy-path confirment que « SAVE10 » réduit le prix. Les vrais bugs se cachent ailleurs : prix à 0 ou négatifs, codes expirés, problèmes d'arrondi, plafonds de réduction. Ce sont ces cas qui produisent des totaux incorrects, des clients en colère et des rollbacks nocturnes.
L'objectif est de passer de « plus de tests » à « meilleurs tests » en visant trois cibles : les frontières, les modes de défaillance et les invariants.
Si vous voulez des tests unitaires à fort signal, arrêtez de demander « plus de tests » et commencez à demander trois types précis. C'est le cœur d'un prompt de génération de tests Claude Code qui produit une couverture utile plutôt qu'un tas de vérifications « marche sur des entrées normales ».
Les frontières sont les bords de ce que le code accepte ou produit. Beaucoup de défauts réels sont des erreurs d'indice, des états vides ou des problèmes de timeout qui n'apparaissent jamais dans un happy-path.
Pensez en termes de minima et maxima (0, 1, longueur max), vide vs présent ("", [], nil), off-by-one (n-1, n, n+1) et limites temporelles (près du cutoff).
Exemple : si une API accepte « jusqu'à 100 éléments », testez 100 et 101, pas seulement 3.
Les modes de défaillance sont les façons dont le système peut casser : entrées incorrectes, dépendances manquantes, résultats partiels, ou erreurs en amont. De bons tests de modes de défaillance vérifient le comportement sous contrainte, pas seulement la sortie en condition idéale.
Exemple : quand un appel à la base échoue, la fonction renvoie-t-elle une erreur claire et évite-t-elle d'écrire des données partielles ?
Les invariants sont des vérités qui doivent rester vraies avant et après un appel. Ils transforment la correction vague en assertions nettes.
Exemples :
Quand vous vous concentrez sur ces trois cibles, vous avez moins de tests, mais chacun porte plus de signal.
Si vous demandez des tests trop tôt, vous obtenez habituellement un tas de vérifications polies « marche comme attendu ». Une solution simple est d'écrire d'abord un petit contrat, puis de générer des tests à partir de ce contrat. C'est la manière la plus rapide de transformer un prompt de génération de tests Claude Code en quelque chose qui trouve de vrais bugs.
Un contrat utile est assez court pour être lu en une respiration. Visez 5 à 10 lignes qui répondent à trois questions : quoi entre, quoi sort, et quoi change d'autre.
Rédigez le contrat en langage clair, pas en code, et incluez seulement ce que vous pouvez tester.
Une fois que vous avez ça, parcourez-le pour voir où la réalité peut briser vos hypothèses. Ceux-là deviennent des cas limites (min/max, zéro, overflow, chaînes vides, doublons) et des modes de défaillance (timeouts, permission denied, violation de contrainte d'unicité, entrée corrompue).
Voici un exemple concret pour une fonctionnalité comme reserveInventory(itemId, qty) :
Le contrat pourrait dire que qty doit être un entier positif, que la fonction doit être atomique, et qu'elle ne doit jamais créer de stock négatif. Cela suggère immédiatement des tests à fort signal : qty = 0, qty = 1, qty supérieur au disponible, appels concurrents, et une erreur simulée de base de données au milieu.
Si vous utilisez un outil de type vibe-coding comme Koder.ai, le même workflow s'applique : écrivez d'abord le contrat dans le chat, puis générez des tests qui attaquent directement les frontières, modes de défaillance et la liste « doit ne jamais arriver ».
Utilisez ce prompt de génération de tests Claude Code quand vous voulez moins de tests mais chacun utile. Le mouvement-clé est d'imposer d'abord un plan de tests, puis de générer le code seulement après approbation du plan.
You are helping me write HIGH-SIGNAL unit tests.
Context
- Language/framework: <fill in>
- Function/module under test: <name + short description>
- Inputs: <types, ranges, constraints>
- Outputs: <types + meaning>
- Side effects/external calls: <db, network, clock, randomness>
Contract (keep it small)
1) Preconditions: <what must be true>
2) Postconditions: <what must be true after>
3) Error behavior: <how failures are surfaced>
Task
PHASE 1 (plan only, no code):
A) Propose 6-10 tests max. Do not include “happy path” unless it protects an invariant.
B) For each test, state: intent, setup, input, expected result, and WHY it is high-signal.
C) Invariants: list 3-5 invariants and how each will be asserted.
D) Boundary matrix: propose a small matrix of boundary values (min/max/empty/null/off-by-one/too-long/invalid enum).
E) Failure modes: list negative tests that prove safe behavior (no crash, no partial write, clear error).
Stop after PHASE 1 and ask for approval.
PHASE 2 (after approval):
Generate the actual test code with clear names and minimal mocks.
Une astuce pratique est d'exiger la matrice de frontières sous forme de tableau compact, pour que les lacunes soient évidentes :
| Dimension | Valide (bord) | Juste dehors | Valeur “bizarre” | Comportement attendu |
|---|---|---|---|---|
| length | 0 | -1 | 10 000 | erreur vs clamp vs accept |
Si Claude propose 20 tests, recadrez-le. Demandez-lui de fusionner les cas similaires et de ne garder que ceux qui attraperaient un vrai bug (off-by-one, mauvais type d'erreur, perte silencieuse de données, invariant cassé).
Commencez par un contrat petit et concret. Collez la signature de la fonction, une courte description des entrées et sorties, et les tests existants (même s'ils ne sont que des happy-path). Cela ancre le modèle sur ce que le code fait réellement, pas sur ce qu'il devine.
Ensuite, demandez un tableau de risques avant tout code. Exigez trois colonnes : cas limites (bords des entrées valides), modes de défaillance (entrée mauvaise, données manquantes, timeouts), et invariants (règles qui doivent toujours tenir). Ajoutez une phrase par ligne : « pourquoi ça peut casser ». Un simple tableau révèle les lacunes plus vite qu'un tas de fichiers de tests.
Puis choisissez le plus petit ensemble de tests où chacun a un but unique pour attraper un bug. Si deux tests échouent pour la même raison, gardez le plus fort.
Une règle de sélection pratique :
Enfin, exigez une courte explication par test : quel bug ce test attraperait s'il échoue. Si l'explication est vague (« valide le comportement »), le test est probablement à faible signal.
Un invariant est une règle qui doit rester vraie quel que soit l'entrée valide. Avec les tests basés sur les invariants, écrivez d'abord la règle en langage clair, puis transformez-la en assertion qui peut échouer bruyamment.
Choisissez 1 ou 2 invariants qui vous protègent vraiment des bugs. Les bons invariants portent souvent sur la sécurité (pas de perte de données), la cohérence (mêmes entrées = mêmes sorties), ou les limites (ne jamais dépasser un plafond).
Écrivez l'invariant en une phrase courte, puis décidez quelles preuves votre test peut observer : valeurs de retour, données stockées, événements émis, ou appels aux dépendances. Les assertions fortes vérifient l'issue ET les effets secondaires, car beaucoup de bugs se cachent dans « OK renvoyé mais mauvaise écriture ».
Par exemple, pour une fonction qui applique un coupon à une commande :
Encodez cela en assertions concrètes :
expect(result.total).toBeGreaterThanOrEqual(0)
expect(db.getOrder(orderId).discountCents).toBe(originalDiscountCents)
Évitez les assertions vagues comme « renvoie le résultat attendu ». Assurez l'invariant spécifique (non négatif) et l'effet secondaire spécifique (réduction enregistrée une seule fois).
Pour chaque invariant, ajoutez une courte note dans le test sur quelles données le violeraient. Cela empêche le test de dériver en un simple happy-path plus tard.
Un simple motif qui tient dans le temps :
Les tests à fort signal confirment souvent que votre code échoue en sécurité. Si un modèle ne génère que des tests happy-path, vous n'apprenez presque rien sur le comportement lorsque les entrées et dépendances sont dégradées.
Commencez par définir ce que « échouer en sécurité » signifie pour la fonctionnalité. Renvoie-t-elle une erreur typée ? Repli sur une valeur par défaut ? Réessaie-t-elle une fois puis abandonne ? Écrivez cela en une phrase, puis faites en sorte que les tests le prouvent.
Quand vous demandez des tests de modes de défaillance à Claude Code, gardez l'objectif strict : couvrir les façons dont le système peut casser et assert la réponse exacte souhaitée. Une ligne utile : « Préférez moins de tests avec des assertions plus fortes plutôt que beaucoup de tests superficiels. »
Catégories de défaillance qui produisent les meilleurs tests :
Exemple : un endpoint crée un utilisateur et appelle un service d'email. Un test low-value vérifie « renvoie 201 ». Un bon test de défaillance vérifie que si le service email timeoute, vous (a) créez quand même l'utilisateur et retournez 201 avec un flag « email_pending », ou (b) renvoyez un 503 clair et ne créez pas l'utilisateur. Choisissez un comportement, puis assertez la réponse ET les effets secondaires.
Testez aussi ce que vous ne devez pas divulguer. Si la validation échoue, assurez-vous que rien n'est écrit en base. Si une dépendance renvoie un payload corrompu, assurez-vous de ne pas lancer une exception non gérée ni renvoyer des traces de pile brutes.
Les ensembles de tests à faible valeur surviennent quand le modèle est récompensé pour le volume. Si votre prompt demande « 20 tests unitaires », vous obtenez souvent de petites variations qui semblent complètes mais n'attrapent rien de nouveau.
Pièges courants :
Exemple : une fonction « create user ». Dix tests happy-path peuvent varier la chaine email et manquer l'essentiel : rejeter les emails doublons, gérer un mot de passe vide, garantir l'unicité et la stabilité des IDs retournés.
Garde-fous utiles en revue :
Imaginez une fonctionnalité : appliquer un code promo au checkout.
Contrat (petit et testable) : donné un sous-total de panier en cents et un coupon optionnel, renvoyer un total final en cents. Règles : les coupons en pourcentage sont arrondis vers le bas au centime le plus proche, les coupons fixes soustraient un montant fixe, et les totaux ne doivent jamais être inférieurs à 0. Un coupon peut être invalide, expiré ou déjà utilisé.
Ne demandez pas « tests pour applyCoupon() ». Demandez des tests de cas limites, des tests de modes de défaillance, et des invariants liés à ce contrat.
Choisissez des entrées qui cassent souvent la logique ou la validation : un coupon vide, subtotal = 0, subtotal juste en dessous et au-dessus d'un minimum requis, une réduction fixe plus grande que le subtotal, et un pourcentage comme 33% qui provoque un arrondi.
Supposez que la lookup du coupon peut échouer et que l'état peut être incorrect : service coupon down, coupon expiré, ou coupon déjà réclamé. Le test doit prouver la suite (coupon rejeté avec une erreur claire, total inchangé).
Un ensemble minimal et à fort signal (5 tests) et ce que chacun détecte :
Si ceux-ci passent, vous avez couvert les points de rupture courants sans remplir la suite de tests de duplications happy-path.
Avant d'accepter ce que le modèle génère, faites un rapide contrôle qualité. Le but : des tests où chacun vous protège d'un bug spécifique et probable.
Utilisez cette checklist comme grille d'acceptation :
Une astuce pratique : renommez les tests en « should <comportement> when <condition de bord> » et « should not <mauvais résultat> when <défaillance> ». Si vous ne pouvez pas les renommer proprement, ils ne sont pas assez ciblés.
Si vous construisez avec Koder.ai, ce checklist s'intègre bien aux snapshots et rollback : générez les tests, exécutez-les, et rollbackez si le nouvel ensemble ajoute du bruit sans améliorer la couverture.
Traitez votre prompt comme un outil réutilisable, pas une requête ponctuelle. Sauvegardez un blueprint (celui qui force frontières, modes de défaillance et invariants) et réutilisez-le pour chaque fonction, endpoint ou flux UI.
Une habitude simple qui améliore rapidement les résultats : demandez une phrase par test expliquant quel bug il attraperait. Si la phrase est générique, le test est probablement du bruit.
Conservez une liste vivante des invariants du domaine pour votre produit. Ne la gardez pas en tête. Ajoutez-y les invariants chaque fois que vous trouvez un vrai bug.
Un workflow léger à répéter :
Si vous construisez via le chat, exécutez ce cycle dans Koder.ai (koder.ai) afin que le contrat, le plan et les tests générés restent au même endroit. Quand un refactor change le comportement de façon imprévue, les snapshots et le rollback facilitent la comparaison et l'itération jusqu'à ce que votre ensemble à fort signal reste stable.
Par défaut : visez un petit ensemble susceptible de détecter un vrai bug.
Un bon plafond pratique est 6–10 tests par unité (fonction/module). Si vous avez besoin de plus, cela signifie souvent que votre unité fait trop de choses ou que le contrat est flou.
Les tests « happy path » montrent surtout que votre exemple continue de fonctionner. Ils manquent souvent ce qui casse en production.
Les tests à fort signal ciblent :
Commencez par un petit contrat lisible en une respiration :
Générez ensuite des tests à partir de ce contrat, pas seulement à partir d'exemples.
Testez d'abord :
Un bon test de mode de défaillance prouve deux choses :
Si une écriture en base est impliquée, vérifiez toujours ce qui a été écrit après l'échec.
Approche par défaut : transformez l'invariant en assertion sur les résultats observables.
Exemples :
expect(total).toBeGreaterThanOrEqual(0)Un test happy-path mérite d'être conservé quand il protège un invariant ou une intégration critique.
Bonnes raisons d'en garder un :
Sinon, échangez-le contre des tests de frontières ou de défaillance qui attrapent plus de classes de bugs.
Obligez la modèle à produire PHASE 1 : plan uniquement en premier.
Exigez que le modèle fournisse :
Ce n'est qu'après approbation du plan qu'il doit générer du code. Cela évite les « 20 tests semblables ».
Par défaut : simuler uniquement la frontière que vous ne contrôlez pas (BD/réseau/horloge), et laisser le reste réel.
Pour éviter le sur-mockage :
Si un test casse lors d'un refactor mais que le comportement n'a pas changé, il est souvent trop couplé à l'implémentation ou sur-mocké.
Faites un test simple de suppression :
Cherchez aussi les duplications :
Choisissez une ou deux valeurs par dimension d'entrée pour que chaque test couvre un risque unique.
Privilégiez la vérification à la fois de la valeur de retour et des effets secondaires, car de nombreux bugs se cachent dans « OK renvoyé mais mauvaise écriture ».