Prompt Claude Code pour générer des tests de cas limites
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.

Pourquoi la génération de tests happy-path fait perdre du temps
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 :
- Beaucoup de tests ne diffèrent que par des libellés d'entrée, pas par ce qui peut casser.
- Les assertions sont superficielles (« pas null », « statut est 200 ») au lieu de vérifier le sens.
- L'initialisation est plus lourde que le comportement testé, donc les gens arrêtent de mettre à jour les tests.
- La couverture semble élevée, mais les cas limites sont ignorés.
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.
Les trois cibles : frontières, modes de défaillance, 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 ».
1) Frontières (où les bugs se cachent)
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.
2) Modes de défaillance (prouver que ça échoue en sécurité)
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 ?
3) Invariants (règles qui doivent toujours tenir)
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 :
- « Le solde ne devient jamais négatif » après une tentative de retrait.
- « Les IDs sont uniques » même si on crée des éléments rapidement.
- « En cas d'erreur, aucun changement d'état » (pas de nouvelles lignes, pas de flags basculés).
Quand vous vous concentrez sur ces trois cibles, vous avez moins de tests, mais chacun porte plus de signal.
Préparation : extraire un petit contrat avant d'écrire les tests
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.
Un template de contrat (5–10 lignes)
Rédigez le contrat en langage clair, pas en code, et incluez seulement ce que vous pouvez tester.
- Entrées : types, plages autorisées, et ce qui compte comme « vide » ou « manquant ».
- Sortie : valeur de retour ou forme d'erreur, et quelles garanties en cas de succès.
- Effets secondaires : changements d'état, lignes BD, appels réseau, fichiers, logs.
- Hypothèses : choses que les appelants se trompent souvent (timezone, encodage, auth, ordre).
- « Doit ne jamais arriver » : plantage, perte silencieuse de données, double facturation, écritures partielles.
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 ».
Schéma de prompt : le blueprint pour tests à fort signal
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é).
Étape par étape : exécuter le prompt et transformer la sortie en tests
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 :
- Gardez des tests qui touchent des frontières différentes (min, max, vide, off-by-one).
- Gardez des tests qui prouvent un comportement sûr en cas d'échec (erreur claire, pas d'écriture partielle, pas de crash).
- Gardez des tests qui valident un invariant (ordre, totaux, idempotence, pas de doublons).
- Supprimez les tests qui répètent seulement « fonctionne avec des entrées normales ».
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.
Comment coder les invariants en assertions
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).
Transformer un invariant en vérification testable
É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 :
- Invariant : le total final n'est jamais négatif.
- Invariant : appliquer le même coupon deux fois ne réduit pas deux fois.
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).
Ajoutez une note contre-exemple pour garder le test pertinent
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 :
- Mettez l'invariant dans le nom du test.
- Assert l'invariant sur la sortie.
- Assert l'effet secondaire clé (ou son absence).
- Ajoutez un commentaire qui décrit un cas violant l'invariant (par ex. coupon énorme ou application dupliquée).
Modes de défaillance : écrire des tests qui prouvent un échec sûr
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 :
- Entrées incorrectes : formats invalides, champs requis manquants, valeurs hors plage
- Dépendances défaillantes : timeouts, 500, réponses vides, payload corrompu
- Problèmes d'ordre : événements hors ordre, doublons, écritures partielles
- Concurrence : mises à jour en compétition, vérifications d'idempotence
- Comportement de récupération : quand renvoyer une erreur vs se replier vs réessayer
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.
Pièges courants qui produisent des tests à faible valeur
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 :
- Tests semblables : même test « entrée valide » répété avec différentes chaînes ou nombres.
- Tests qui reflètent l'implémentation : assertion d'étapes privées ou d'appels de helpers au lieu du comportement observable.
- Mocker tout : remplacer base, horloge, réseau et config en même temps.
- Assertions faibles : seulement vérifier « pas d'erreur », « non null », ou « status 200 ».
- État partagé sale : laisser des données initialisées, des globals modifiés, ou du cache.
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 :
- Exiger que chaque test nomme le risque couvert (frontière, mode de défaillance, ou invariant).
- Éviter les vérifications liées uniquement à l'implémentation sauf si elles modifient le comportement observable.
- Limiter le mocking, et autoriser un petit nombre de tests qui touchent l'intégration réelle quand c'est faisable.
- Exiger des assertions fortes : sorties exactes, changements d'état, types/messages d'erreur.
- Ajouter des règles de nettoyage pour que les tests ne dépendent pas de l'ordre.
Exemple : transformer une fonctionnalité en un petit ensemble de tests solides
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.
Frontières pour forcer des comportements limites
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.
Modes de défaillance pour prouver un comportement sûr
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 :
- Rejeter un code vide ou composé d'espaces : détecte l'acceptation d'une chaîne vide comme valide et les erreurs de trimming.
- Arrondi pour un coupon en pourcentage (subtotal 101, 33%) : détecte les erreurs d'arrondi et les off-by-one de centimes.
- Remise fixe supérieure au subtotal (subtotal 500, remise 1000) : prouve l'invariant que le total ne devient pas négatif.
- Seuil minimal (subtotal 999 vs 1000) : détecte une mauvaise comparaison (< vs <=).
- Échec de lookup du coupon ou timeout : prouve le comportement de repli sûr (pas de remise appliquée) et la gestion d'erreur stable.
Si ceux-ci passent, vous avez couvert les points de rupture courants sans remplir la suite de tests de duplications happy-path.
Checklist rapide pour des tests IA à fort signal
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 :
- Frontières par entrée : pour chaque champ d'entrée (chaînes, IDs, timestamps, flags), incluez au moins un cas limite (vide vs uniquement espaces, longueur max, zéro vs négatif, champs optionnels manquants, juste au-dessus/au-dessous de la limite).
- Défaillances de dépendances : incluez au moins un test où une dépendance se comporte mal (timeout BD, API tierce 500, token expiré). Prouvez un comportement sûr (erreur claire, pas d'écriture partielle).
- Invariants avec assertions fortes : choisissez 1–3 règles qui doivent toujours se vérifier et assert-les directement. Évitez « réponse OK » vague.
- Un bug unique par test : lisez chaque titre et demandez « Quel bug précis ceci attraperait ? » Si deux tests répondent à la même question, fusionnez-les.
- Test de suppression : essayez de supprimer un test. Si rien d'essentiel n'est perdu (pas de frontière, pas de mode de défaillance, pas d'invariant), il n'avait pas sa place.
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.
Prochaines étapes : rendre ce workflow répétable
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 :
- Extraire un petit contrat : entrées, sorties, gestion d'erreur et 3 à 5 invariants.
- Lancer le prompt blueprint et demander frontières, modes de défaillance, invariants et justifications en une ligne.
- Implémenter seulement les 5–10 tests principaux couvrant des risques distincts.
- Refactorer, puis relancer le prompt pour voir quels nouveaux risques apparaissent.
- Élaguer les duplications et garder les tests qui auraient attrapé des incidents passés.
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.
FAQ
Combien de tests unitaires devrais-je générer par fonction ?
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.
Qu'est-ce qui pose problème à générer beaucoup de tests happy-path ?
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 :
- Frontières (0/1/max, vide/null, off-by-one)
- Modes de défaillance (timeouts, entrées invalides, erreurs de dépendance)
- Invariants (règles qui doivent toujours tenir, comme « pas d'écriture partielle en cas d'erreur »)
Qu'est-ce que je dois écrire avant de demander à une IA de générer des tests ?
Commencez par un petit contrat lisible en une respiration :
- Entrées : types, plages autorisées, ce qui compte comme vide/manquant
- Sorties : forme de succès et forme d'erreur
- Effets secondaires : ce qui peut être écrit/modifié (BD, fichiers, réseau)
- « Doit ne jamais arriver » : plantage, perte silencieuse de données, double facturation, écritures partielles
Générez ensuite des tests à partir de ce contrat, pas seulement à partir d'exemples.
Quelles frontières valent généralement la peine d'être testées ?
Testez d'abord :
- Min/max (0, 1, max, max+1)
- Vide vs présent ("", [], null)
- Off-by-one (n-1, n, n+1)
- Bords de formatage (chaînes composées uniquement d'espaces, zéros en tête)
- Découpes temporelles (juste avant/après expiration)
Choisissez une ou deux valeurs par dimension d'entrée pour que chaque test couvre un risque unique.
Comment écrire un bon test de « mode de défaillance » au lieu d'un test superficiel ?
Un bon test de mode de défaillance prouve deux choses :
- La fonction renvoie une erreur claire et attendue (type/message/status).
- Elle échoue en sécurité :
- pas d'écriture partielle
- pas de fuite de détails internes
- pas de retries ou d'effets de bord non désirés
Si une écriture en base est impliquée, vérifiez toujours ce qui a été écrit après l'échec.
Comment transformer un invariant en assertion de test ?
Approche par défaut : transformez l'invariant en assertion sur les résultats observables.
Exemples :
- « Le total n'est jamais négatif » →
expect(total).toBeGreaterThanOrEqual(0) - « En cas d'erreur, aucun changement d'état » → vérifier aucune nouvelle ligne / aucun flag basculé
- « Idempotence » → appeler deux fois et vérifier que le second appel ne change rien
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 ».
Quand un test happy-path vaut-il encore la peine ?
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 :
- Il affirme un invariant clé sur des entrées normales (par ex. règles d'arrondi)
- Il verrouille un contrat d'API sur lequel les clients comptent
- Il protège contre une régression déjà rencontrée
Sinon, échangez-le contre des tests de frontières ou de défaillance qui attrapent plus de classes de bugs.
Que dois-je demander que le modèle produise avant de générer du code de test ?
Obligez la modèle à produire PHASE 1 : plan uniquement en premier.
Exigez que le modèle fournisse :
- 6–10 tests proposés max
- Pour chacun : intention, configuration, entrée, résultat attendu, pourquoi c'est à fort signal
- Une petite matrice de frontières
- Une liste de modes de défaillance
- 3–5 invariants et comment les assert-er
Ce n'est qu'après approbation du plan qu'il doit générer du code. Cela évite les « 20 tests semblables ».
Comment éviter des tests fragiles parce qu'ils mockent trop ?
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 :
- Ne mockez pas des helpers internes juste pour refléter l'implémentation
- Utilisez une version en mémoire réelle quand c'est faisable, ou un faux simple au comportement clair
- Mockez l'horloge/hasard seulement si cela affecte l'assertion
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é.
Comment savoir rapidement si un test généré par l'IA a peu de valeur ?
Faites un test simple de suppression :
- Si vous supprimez le test et perdez aucune frontière, aucun mode de défaillance et aucun invariant, il n'avait pas sa place.
Cherchez aussi les duplications :
- Si deux tests échoueraient pour le même bug, gardez celui avec l'assertion la plus forte.
- Si les assertions se limitent à « non null » ou « status 200 », renforcez-les ou supprimez le test.