ShortArrow/test-design

Concevoir des cas de test en remplaçant les mauvaises habitudes de test par des méthodes systématiques : déduction à partir des spécifications, classes d'équivalence, valeurs limites, tables de décision, et transitions d'état.

Qu'est-ce que test-design ?

Ce skill guide les développeurs dans la conception de tests logiciels en remplaçant les intuitions non fiables par des techniques systématiques issues des spécifications. Il couvre le partitionnement en classes d'équivalence (y compris les classes invalides souvent oubliées), l'analyse des valeurs limites (2-value et 3-value BVA), les tables de décision pour les combinaisons de conditions, les tables d'état avec transitions explicites, et l'exploitation des cas d'utilisation (flux principal, alternatifs, d'exception). Il insiste sur la déduction des tests à partir des spécifications plutôt que de l'implémentation, afin d'éviter les suites de tests qui valident les bugs existants. Le skill fournit une grille de vérification concrète pour évaluer la couverture d'une suite de tests en comptant les classes, les limites, les règles et les transitions, en citant les références ISTQB CTFL v4.0.1. Il traite également des cas particuliers : chemins non implémentés (tests d'échec intentionnel), résultats attendus ambigus (une seule issue par cas), et gestion des risques lorsque l'on retire une classe de test.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ShortArrow/skills/tree/HEAD/skills/test-design

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Conception de tests

Les cas déterminent ce que la suite peut détecter. Tout ce qui vient après — le harnais, l'exécuteur, le décompte — ne fait qu'exécuter cette décision.

Les moments que ce skill remplace

Sur le point de…Au lieu de cela…
écrire le chemin heureux et s'arrêterparcourir l'espace d'entrée : pour chaque paramètre, ses classes et ses limites
déduire les cas de l'implémentationles déduire de la spécification, et laisser l'implémentation vous surprendre
tester avec une valeur confortabletester la dernière valeur à l'intérieur et la première valeur à l'extérieur de chaque frontière
tester la limite que la spécification nommetoute plage a deux extrémités ; celle que la phrase a omise est toujours une frontière
énumérer les rôles listés par la spécificationajouter l'appelant qu'elle a oublié : aucun rôle, non authentifié, non attribué
faire varier une condition à partir du chemin heureuxconstruire la table de décision et couvrir les règles où deux conditions échouent ensemble
dessiner une table d'état et passer à autre chosemettre un identifiant de test dans chaque ligne ; la ligne qui dit « non autorisé » en priorité
écrire « rejeté, ou accepté à condition que… » comme résultat attenduun résultat par cas ; une spécification qui ne peut pas décider est une question ouverte, pas un test
ignorer les chemins d'erreur comme improbableschaque mode de défaillance est une classe : entrée invalide, ressource manquante, refus, délai d'attente, à moitié terminé
laisser un chemin non implémenté sans test, avec un marqueur de saut ou un stub sans assertionécrire un test qui vérifie que le chemin est refusé comme non implémenté aujourd'hui ; il passe maintenant et devient rouge le jour où quelqu'un l'implémente (ci-dessous)
tester une fonctionnalité par son flux principallire les flux alternatifs et d'exception du cas d'utilisation ; chacun est une classe
évaluer une suite en la lisantla mesurer par rapport à la spécification : classes, les deux limites, règles, transitions
ajouter un cinquième test à une classe déjà couvertele consacrer à une classe non couverte — le cinquième achète du temps d'exécution et aucune couverture

Attaquer la spécification, pas l'implémentation

Un test écrit en lisant le code vérifie ce que le code fait. Il passe à l'arrivée, encode les bugs actuels comme des attentes, et ses échecs après un changement disent « le code a changé », ce qui était déjà connu. Ceci est mesuré, pas suspecté : sur 24 dépôts Java, les oracles générés par LLM avaient tendance à capturer le comportement réel du programme plutôt que son comportement attendu (Konstantinou, Degiovanni, Papadakis, arXiv:2410.21136).

Déduisez de ce que la chose est censée faire — la spécification, le format filaire, le contrat — et seulement ensuite exécutez. La seule exception légitime est le test de caractérisation qui épingle délibérément le comportement actuel, et il est marqué comme tel pour que personne ne confonde plus tard un bug épinglé avec une exigence.

Un indicateur utile : si chaque test était vert lors de sa première exécution, alors la suite a été déduite de l'implémentation, quelle qu'ait été l'intention.

Classes d'équivalence : compter les classes, pas les tests

Les entrées que le code ne peut pas distinguer forment une classe, et un test par classe suffit — du deuxième au cinquième, ils coûtent du temps d'exécution et ne prouvent rien de nouveau. La discipline fonctionne dans les deux sens : aucune classe laissée non couverte, aucune classe couverte deux fois par accident.

Une suite de dix-sept tests semble exhaustive. Mais cela dépend du nombre de classes dans lesquelles ces dix-sept tests tombent, et ce nombre n'apparaît dans la sortie d'aucun exécuteur. Il doit être compté manuellement par rapport à l'espace d'entrée — ce qu'est un plan de test.

Les classes que la spécification liste sont la moitié facile. Une table de rôles avec trois lignes a une quatrième classe, la requête qui ne correspond à aucune d'elles, et c'est celle qu'une suite construite à partir de la table ne contient jamais. Pour chaque paramètre, écrivez les classes invalides à côté des classes valides avant d'écrire un cas : mauvais type, absent, en dehors de toute valeur listée, l'appelant sans aucun droit.

Le syllabus énonce ceci comme une règle plutôt qu'une habitude : dans le partitionnement d'équivalence, les éléments de couverture sont les partitions, 100% signifie que chaque partition identifiée est exercée « y compris les partitions invalides », et les partitions « ne doivent pas se chevaucher et doivent être des ensembles non vides » (ISTQB CTFL v4.0.1 §4.2.1). Avec plusieurs paramètres, il nomme le critère le plus faible Chaque Choix — chaque partition de chaque paramètre une fois, les combinaisons ignorées — qui est le niveau que la plupart des suites atteignent sans connaître son nom, et là où la table de décision ci-dessous commence.

Limites, parce que c'est là que se trouvent les défauts

L'erreur de décalage d'un est le défaut arithmétique le plus courant, et il est invisible partout sauf à la limite. La paire qui compte à chaque limite est la valeur limite et son plus proche voisin de l'autre côté de la ligne, ce qui est l'analyse de valeurs limites (BVA) à 2 valeurs (CTFL v4.0.1 §4.2.2) ; la BVA à 3 valeurs ajoute le voisin à l'intérieur aussi, et le syllabus montre ce que cela apporte : une vérification prévue comme x ≤ 10 mais écrite comme x == 10 passe à la fois pour 10 et 11, et seul 9 la détecte. Les limites récurrentes : vide, exactement un, plusieurs ; zéro et négatif ; le maximum déclaré et un au-delà ; la valeur qui est exactement le tampon, la limite, le délai d'attente.

Un exemple concret de cette machine : une vérification de chemin qui fonctionne à 8 190 caractères et doit échouer à 8 192 doit être testée à ces deux valeurs. Passer à 4 000 et 12 000 n'établit presque rien sur la ligne entre elles.

Lorsque la limite est une précondition énoncée, la valeur au-delà est un bogue de l'appelant plutôt qu'un cas avec un résultat attendu ; la « conception par contrat » décide lequel des deux une limite donnée est, et le test vérifie ensuite celui qui s'applique.

Le côté que la spécification n'a pas nommé

« Jusqu'à 10 Mo » nomme une extrémité d'une plage. L'autre extrémité existe que la phrase la mentionne ou non, et les cas déduits du texte s'arrêtent là où le texte s'arrête : une suite avec la limite supérieure testée à −1, 0 et +1 octets et rien en dessous de 1 024 a effectué une analyse de limites sur la moitié d'une plage. La définition n'a pas de côté : « les valeurs minimales et maximales d'une partition sont ses valeurs limites » (CTFL v4.0.1 §4.2.2). Écrivez les deux pour chaque partition d'abord, puis prenez chacune avec son plus proche voisin dans la partition adjacente. Le côté non nommé est généralement zéro, vide ou un, et la spécification est généralement silencieuse à ce sujet parce que personne n'a décidé.

Déplacer deux conditions en même temps

Une suite construite en changeant une condition du chemin heureux couvre chaque valeur de chaque condition et presque aucune de leurs combinaisons : format erroné, taille dépassée, nombre dépassé, chacun isolé, et aucun cas où deux échouent ensemble. Quelle erreur l'emporte lorsque les deux s'appliquent est rarement dans la spécification non plus, donc le cas manquant cache une décision manquante.

Disposez les conditions comme une table de décision et comptez les règles, puis couvrez les règles où deux conditions échouent en même temps et la règle où une restriction de rôle et une restriction de format s'appliquent toutes les deux. Dans le test par table de décision, les éléments de couverture sont les colonnes contenant les combinaisons réalisables, et 100% signifie que chaque colonne de ce type est exercée (CTFL v4.0.1 §4.2.3) ; le syllabus ajoute que la table « aide à trouver les lacunes ou contradictions dans les exigences », ce qui est la décision manquante ci-dessus qui fait surface d'elle-même. Le même document peut sembler entièrement couvert sous Chaque Choix et au quart couvert sous les colonnes, et seul le deuxième nombre dit quoi que ce soit sur les combinaisons. Lorsque le nombre de règles est le produit d'un ensemble de drapeaux, la correction la moins chère est en amont : « d'abord l'état » réduit les drapeaux aux états qui peuvent se produire, et la table se réduit à eux.

Une table est une affirmation sur la suite

Une table d'état, une table de décision, la liste des flux d'un cas d'utilisation : chaque ligne affirme qu'un test existe pour elle. Mettez l'identifiant du test dans la ligne. Une ligne sans identifiant est une classe non couverte, et la ligne qui dit « non autorisé » est celle qui a le plus de chances d'être vide, car écrire l'interdiction a semblé suffisant pour la traiter. La transition interdite — le deuxième envoi alors qu'un est en cours — est le test, et la garde qui est censée l'arrêter est la chose testée.

La table d'état rend cette ligne explicite : une transition invalide est une cellule vide, et le plus fort des trois critères que le syllabus définit, la couverture de toutes les transitions, exige de tenter chacune d'elles — un cas de test par transition, afin qu'un défaut ne puisse pas en masquer un autre (CTFL v4.0.1 §4.2.4). La couverture des transitions valides, le commutateur-0 là où la plupart des suites s'arrêtent, ne touche jamais ces cellules, ce qui explique comment une ligne « non autorisé » non testée reste verte.

Ensuite, lisez les deux documents l'un contre l'autre. Un test dont le chemin est en désaccord avec la table (la table dit échec → envoi, le test va échec → sélectionné → envoi) signifie que l'un d'eux est erroné, et rien d'autre que la comparaison ne permet de découvrir lequel. Les auto-transitions et les transitions hors de l'état d'échec comptent aussi comme des lignes.

Un résultat par cas

« Rejeté, ou si accepté, aucune donnée corrompue ne subsiste après l'envoi » ne peut ni passer ni échouer. Il apparaît honnêtement, quand la spécification n'a jamais dit ce qu'il advient d'un fichier de zéro octet et que l'auteur a refusé d'inventer une réponse, et il survit à la relecture parce que le cas est visiblement présent. La disjonction se cache dans la colonne « attendu ». La forme correcte est une question au propriétaire de la spécification, listée comme telle, et pas de test pour cette classe tant qu'elle n'est pas répondue.

Les chemins d'erreur sont aussi des classes

Les chemins malheureux sont là où le code non testé s'accumule, car rien ne les exerce en utilisation normale — un détenteur de verrou qui meurt en cours d'exécution, un dossier mappé qui n'existe pas, une entrée avec le champ attendu manquant. Chacun est une classe d'équivalence avec la même prétention à un test que n'importe quelle plage d'entrée. Une suite qui prouve seulement que l'outil fonctionne quand tout coopère a testé la démo, pas l'outil.

Les cas d'utilisation énumèrent les classes avant qu'il y ait du code

Un cas d'utilisation est une spécification déjà découpée en classes : un flux principal, chaque flux alternatif, chaque flux d'exception, et la précondition sous laquelle chacun peut être entré. Quiconque l'a écrit a fait l'énumération que ce skill demande — les flux alternatifs et d'exception sont les chemins d'erreur — donc une suite qui exerce le flux principal a couvert une classe parmi les N que le document liste.

Lisez les flux du cas d'utilisation et donnez à chacun son test, puis vérifiez les préconditions, car un flux n'est accessible que lorsque sa précondition peut être vraie. Une vérification de parité pour des pages traduites a un flux principal (les deux langues éditées et soumises), un flux alternatif (une modification unilatérale déclarée comme exception), et deux flux d'exception qui n'apparaissent qu'une fois les préconditions écrites : l'arborescence est sale, donc l'obsolescence ne peut pas encore être jugée ; l'historique a été réécrit, donc un commit déclaré ne se résout plus. Les deux ont été rencontrés sur un site en une semaine. Le chemin heureux seul n'aurait montré aucun des deux.

Qu'un flux d'exception puisse réellement se produire n'est pas déterminé par son improbabilité apparente. L'enveloppe opérationnelle dit ce qui est hors de portée et l'analyse des dangers dit ce qui reste ; les deux vivent dans « dossier-d'assurance », et ce skill prend la liste qu'ils produisent au lieu de la tailler par intuition.

La couverture est un nombre sous un critère nommé

Un document de test mesuré de trois façons : 100% des conditions, 90,9% MC/DC, 25% des règles de décision. Les trois sont vrais pour le même fichier, donc un chiffre de couverture sans son critère ne dit rien, et un chiffre dont le critère est choisi après le comptage dit encore moins. Nommez-le avant de compter, dans les propres termes du syllabus pour que le nombre puisse être vérifié : couverture de partition et Chaque Choix (§4.2.1), BVA à 2 ou 3 valeurs (§4.2.2), colonnes de table de décision (§4.2.3), tous les états, transitions valides ou toutes les transitions (§4.2.4). MC/DC ne figure pas dans le syllabus Fondation ; il vient des normes de sécurité (DO-178C, ISO 26262), et le lister à côté des autres sans le dire est le genre de citation mixte qui passe pour de la rigueur. Le reste de ce qu'un nombre doit à son lecteur se trouve dans « affirmations-mesurées ».

Vérifier une suite : compter, ne pas juger

Demander si une suite est adéquate renvoie les angles morts qui l'ont produite. Une deuxième session sur le même modèle les partage, et il en va de même pour la même personne un jour plus tard. Le syllabus appelle cela « une certaine indépendance », le deuxième de ses quatre niveaux, et dit d'où vient la valeur : « les différences entre les biais cognitifs de l'auteur et du testeur » (CTFL v4.0.1 §1.5.3). Une deuxième session sur le même modèle partage les biais et ne garde que l'étiquette. L'étape qui ne les partage pas est de compter : listez les classes de la spécification et cochez celles qui ont un test, listez les deux limites de chaque plage, listez les règles de la table de décision, listez les lignes de chaque table d'état, et citez le fichier et la ligne de chaque lacune. Qu'une valeur attendue soit correcte reste un jugement et reste avec un lecteur qui ne l'a pas écrite ; qu'une classe ait un test est de l'arithmétique. Si le comptage a été échantillonné plutôt qu'exhaustif, le rapport dit échantillonné. Le formulaire de réfutation par défaut pour le verdict se trouve dans « vérification-adversaire » ; ce skill fournit ce qu'il faut compter.

Quand la demande supprime une classe

« Le double envoi ne peut pas se produire parce que le bouton est désactivé, supprimez cette ligne » supprime une classe de la suite en s'appuyant sur une seule garde dans une seule couche. Faites ce qui est demandé, et dites en une phrase quelle classe est maintenant non couverte et ce qui pourrait l'atteindre : la machine d'état accepte toujours un deuxième envoi de la part de tout appelant qui n'est pas le bouton. Une ligne supprimée avec cette phrase est une décision ; une ligne supprimée sans elle est une lacune que personne ne retrouvera.

Un chemin non implémenté a un test qui le dit

Un chemin laissé de côté intentionnellement dans cette modification, un rôle que cette version ne prend pas en charge, un format pas encore analysé, une branche qui lève NotImplementedException ou appelle todo!(), reste une classe de l'entrée, et la suite enregistre ce qui s'y passe aujourd'hui. Écrivez le test qui vérifie que le chemin est refusé comme non implémenté : l'exception ou la valeur d'erreur qu'il soulève, l'élément de menu qui est absent, la requête qui répond « non pris en charge ».

Ce test passe maintenant et fait deux choses. Il place la lacune dans la liste de la suite, où un lecteur qui compte les classes la trouve, au lieu d'être dans un commentaire ou un ticket. Et il devient rouge le jour où quelqu'un implémente le chemin, ce qui est le moment où ses vrais cas doivent être écrits ; le test qui échoue est le rappel, et il est remplacé, pas supprimé.

Un marqueur de saut ou un test en attente n'est pas un test — c'est une promesse que le test existera un jour, et les promesses ne deviennent pas rouges.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

ShortArrow/interface-design

Deciding how a screen looks and behaves, triggered by the moments that hand the decision to the user unworked: about to ask which layout, which colour, which placement, how to draw a diagram, how wide a pane, how to split a view; about to call a visual choice a matter of taste; about to treat "it looks fine" as having checked it; about to ship a control sized by eye; about to read an automated accessibility pass, or a Lighthouse score of 100, as a clean bill of health. Most of these have an answer in a published standard, a measurable threshold, or one screenshot — WCAG 2.2 fixes contrast, target size, reflow and focus; a design system fixes spacing and components; axe-core finds about 57% of WCAG issues, and Lighthouse's accessibility score is that same engine weighted, so both leave the rest to a person. What survives is a genuine question, asked with the evidence attached. Use before asking any question about appearance, layout or interaction, and before calling

ShortArrow/library-design

Designing a library — a published package, or an in-app module from the moment it has a second consumer. The implementation stays editable forever, while the public API, the dependency set, the performance characteristics and every observable behaviour freeze at first external use (Hyrum's law). Five duties to the caller: take no control, add no hidden cost (zero-cost abstraction as the bar), hold no hidden state, hide no failure, block no escape. Use when designing or reviewing a public API, when extracting shared code into a module, when asked to "make this reusable", when a signature-compatible change alters an observable behaviour, and when an application is being shaped around a framework's types and lifecycle.

Skills associés