Stratégie de test
Du test unitaire
au stress opérationnel
Comment structurer ses tests pour un développement solo, AI-first.
Le filet de sécurité qui rend l'autonomie possible.
01 — Le problème
Quand un développeur solo travaille avec des agents de codage, la question n'est plus « est-ce que je devrais écrire des tests ? » — c'est « quels tests, dans quel ordre, et pour protéger quoi ? »
Ezkey est une plateforme cryptographique MFA, développée en solo avec une approche AI-first. Le code est modifié autant par l'humain que par l'agent. Sans une stratégie de test claire, chaque modification devient un pari. Avec la bonne stratégie, chaque modification devient vérifiable — rapidement, à moindre coût, et de manière répétable.
Ce qui suit est le résultat d'un an d'itérations. Ce n'est pas une recette académique — c'est ce qui fonctionne en pratique.
02 — Tests unitaires : première ligne de défense
Les tests unitaires sont le point de départ. Ils sont rapides, peu coûteux, et ils offrent un double avantage concret :
- Feedback immédiat — après une modification, l'humain ou l'agent peut les lancer en quelques secondes pour un premier test de santé.
- Garde-fou de régression — ils protègent les comportements critiques sans avoir besoin de démarrer l'infrastructure complète.
Dans Ezkey, les tests unitaires couvrent les services métier, les mappers, la logique de validation et les utilitaires cryptographiques. Ils s'exécutent sans base de données, sans Docker, sans réseau. C'est cette légèreté qui les rend utilisables comme réflexe après chaque changement — que ce soit l'humain qui lance un mvn test ciblé, ou l'agent qui valide son propre travail automatiquement.
03 — La segmentation par tags
Quand le nombre de tests augmente, pouvoir cibler précisément ce qu'on exécute devient essentiel. Ezkey utilise les tags JUnit 5 pour segmenter les tests en sous-groupes. Chaque test porte un ou plusieurs tags, et les profils Maven permettent de combiner ces tags à la ligne de commande.
L'intérêt est direct : on peut lancer uniquement les tests d'encryption après un changement cryptographique, ou uniquement les smoke tests après un déploiement, sans exécuter la suite complète.
| Tag | Rôle |
|---|---|
fast |
Tests complétés en < 5 secondes — exécution par défaut en CI |
slow |
Tests prenant 30+ secondes — builds nocturnes, pré-release |
smoke |
Validation rapide des fonctionnalités critiques — sanity check post-déploiement |
encryption |
Rotation de clés, encryption, synchronisation de keysets |
enrollment |
Flux d'enrôlement et cycle de vie |
authentication |
Tentatives d'authentification, challenge-response, tokens |
admin |
Authentification admin, tokens, opérations administratives |
security |
Frontières de sécurité — échecs 401 et 403 |
multi-tenant |
Isolation entre tenants, permissions, frontières |
cross-instance |
Interaction Admin API ↔ Auth API — comportements distribués |
audit-integrity |
Signatures HMAC, chaînes de checkpoints, détection de falsification |
database |
Tests impliquant un accès SQL direct à la base de données |
time-dependent |
Tests sensibles au timing — potentiellement instables sous charge |
elective |
Vérifications ponctuelles sur demande — jamais en exécution automatique |
operational-churn |
Boucles longue durée simulant une activité de production |
En pratique, les profils Maven les plus courants :
mvn test -pl ezkey-tests
# Tous les tests standard (excl. elective et operational)
mvn test -pl ezkey-tests -P all-tests
# Cibler une famille spécifique
mvn test -pl ezkey-tests -Dgroups="encryption"
mvn test -pl ezkey-tests -Dgroups="authentication,enrollment"
04 — L'environnement Docker : la clean room
Avant de parler de tests fonctionnels, il faut expliquer comment on obtient un environnement fiable. La réponse dans Ezkey, c'est le clean start.
Un script (clean-start.sh) fait tout le travail :
- Arrêt complet du stack Docker, y compris les volumes — un vrai reset.
- Compilation de tous les modules Java avec un build Docker optimisé (BuildKit).
- Démarrage ordonné de tous les services : PostgreSQL, migrations Flyway, Admin API, Auth API, Crypto API, Demo Device.
- Extraction automatique des credentials de bootstrap depuis les logs Docker.
- Initialisation du token admin pour les tests.
Le résultat : un environnement propre, complet, reproductible. C'est la fondation sur laquelle tous les tests fonctionnels reposent. L'agent de codage peut déclencher un clean start, lancer les tests, et avoir un verdict fiable — sans intervention humaine.
05 — Tests fonctionnels : le cœur de la stratégie
C'est ici que les choses deviennent intéressantes. Les tests fonctionnels dans Ezkey sont des tests bout-en-bout qui parlent aux vraies API, sur un vrai stack Docker, avec une vraie base de données. Ce ne sont pas des mocks — c'est le système réel.
La règle du 80-20
On ne cherche pas une couverture exhaustive de chaque cas de figure. On cible les 80 % de valeur avec 20 % de l'effort. Chaque test fonctionnel doit justifier son existence par la valeur qu'il protège, pas par un objectif de couverture abstraite.
Trois principes fondamentaux
Indépendance. Chaque test doit pouvoir s'exécuter seul, dans n'importe quel ordre. Il crée ses propres données (tenants, intégrations, enrôlements) avec des identifiants uniques. Aucune dépendance implicite à l'ordre d'exécution.
Idempotence. Relancer un test deux fois donne le même résultat. Pas de surprise liée à des données résiduelles, pas d'hypothèse sur la pagination ou l'état préexistant.
Opportunisme avec helpers. Les tests ne se limitent pas à valider la réponse HTTP. Quand c'est pertinent, ils vont examiner directement la base de données via SQL pour confirmer qu'un service a bien fait son travail. C'est un angle de validation complémentaire — pas un contournement des API, mais une vérification indépendante que les effets de bord sont corrects.
Pourquoi séquentiels ?
Les tests fonctionnels par défaut s'exécutent de manière séquentielle. C'est un choix délibéré, pas une contrainte technique.
La raison est pragmatique : quand un test échoue, on veut pouvoir identifier la cause rapidement. Un log séquentiel est lisible. On peut le copier-coller dans une conversation avec un agent de codage et obtenir une analyse de root cause efficace. Avec des tests parallèles, les logs s'entrelacent, les effets de bord se multiplient, et l'investigation devient un casse-tête.
Les données restent
Autre choix intentionnel : les tests fonctionnels ne nettoient pas leurs données. Chaque exécution ajoute des tenants, des enrôlements, des tentatives d'authentification dans la base. Ce n'est pas de la négligence — c'est une stratégie.
Ce volume accumulé donne une chance de repérer des problèmes de performance SQL qui n'apparaîtraient jamais sur une base vide. C'est un premier palier, modeste mais gratuit, de validation de performance. Et c'est cette même base enrichie que les tests électifs et opérationnels viennent ensuite exploiter.
06 — Tests électifs : les vérifications ponctuelles
Certains scénarios sont importants mais changent rarement, prennent du temps, ou n'ont de sens que sur un système ayant accumulé des données. Les exécuter à chaque build serait un gaspillage. Les ignorer serait un risque.
La solution : les tests électifs. Ils ne s'exécutent jamais automatiquement. On les lance explicitement, quand le contexte le justifie — après plusieurs jours d'uptime, avant une release, ou après un changement ciblé dans le domaine concerné.
Exemples concrets dans Ezkey :
- Rotation de clés cryptographiques avec activité concurrente — simule une rotation de clé pendant que des opérations d'encryption sont en cours. Test lent, sensible au timing, mais critique pour valider l'intégrité cryptographique.
- Synchronisation de keysets entre instances — valide le comportement distribué (Admin API ↔ Auth API) pendant une fenêtre de rotation. Pertinent surtout en mode haute disponibilité.
- Exclusion mutuelle des batchs (ShedLock) — vérifie que les tâches planifiées ne s'exécutent pas en parallèle sur plusieurs instances. Le code est stable, mais la vérification périodique reste pertinente.
- Intégrité de la chaîne d'audit — valide les signatures HMAC et les checkpoints de la chaîne d'audit. Plus significatif après accumulation de données réelles.
- Intégrité de l'encryption — spot check sur les données encryptées en base pour valider leur cohérence.
mvn test -pl ezkey-tests -P elective-tests
07 — Tests opérationnels : la compression de temps
Et c'est ici que l'approche devient, je crois, assez unique.
Quand j'ai évalué la pertinence d'un framework de test de charge classique (JMeter, Gatling), je me suis rendu compte que le vrai besoin n'était pas de mesurer des requêtes par seconde. Le vrai besoin était de simuler une activité de production réaliste sur une durée étendue — et d'observer ce qui se passe quand le temps est compressé.
Le concept
Un test opérationnel dans Ezkey utilise le même framework JUnit 5 que les autres tests. Pas de framework additionnel, pas de nouvelle dépendance. La différence : il vit beaucoup plus longtemps.
Concrètement, le test opérationnel actuel :
- Crée un tenant, un administrateur, des intégrations, des enrôlements — un setup complet.
- Entre ensuite dans une boucle d'authentifications répétées, pour une durée par défaut de deux heures, avec un volume configurable (léger, moyen, soutenu).
On peut en démarrer plusieurs instances en parallèle dans des terminaux séparés et les laisser tourner. Chaque instance est indépendante — elle crée ses propres données.
Pourquoi pas un framework de test de charge ?
Parce que le coût d'introduction d'un framework dédié ne se justifie pas pour le retour obtenu. Les tests opérationnels d'Ezkey accomplissent quelque chose de différent : ils ne mesurent pas la performance brute, ils exercent le parallélisme et la concurrence dans des conditions réalistes.
Les tests fonctionnels standard sont séquentiels — c'est leur force pour l'investigation. Mais ça veut aussi dire qu'ils ne testent jamais les mécanismes d'exclusion mutuelle, les verrous distribués, ou les problèmes de concurrence entre sessions multiples. Les tests opérationnels comblent ce trou.
La compression de temps
C'est le prolongement le plus intéressant de cette approche. L'idée : prendre des opérations qui, en production, se produisent rarement — une rotation de clé par trimestre, un batch de ré-encryption mensuel — et les comprimer dans un horizon de quelques heures.
Par exemple : introduire une nouvelle clé cryptographique toutes les 5 minutes au lieu de tous les 3 mois, avec les batchs de ré-encryption correspondants, pendant qu'une charge d'authentification continue en parallèle.
Sur quelques heures, on compresse des mois d'opérations. Les comportements qui n'apparaîtraient normalement qu'une fois par trimestre se manifestent des dizaines de fois. Les conditions de course, les interblocages, les problèmes de coordination entre instances — tout ça a une chance de se révéler.
L'accumulation de données — un choix intentionnel
Comme les tests fonctionnels, les tests opérationnels ne nettoient pas après eux. Après quelques sessions de deux heures avec une ou plusieurs instances en parallèle, la base de données contient un volume représentatif : des milliers d'enrôlements, de tentatives d'authentification, d'entrées d'audit.
Ce volume remplit deux fonctions :
- Validation de performance implicite — si une requête SQL commence à ralentir avec du volume, on le voit. Sur une base toujours vide, on ne le verrait jamais.
- Simulation d'un pilote initial — le volume accumulé se rapproche de ce qu'un premier déploiement en environnement réel produirait. C'est un proxy modeste mais concret d'une réalité de production.
08 — La pyramide : vue d'ensemble
La logique est celle d'une escalade progressive :
- Tests unitaires — feedback en secondes, aucune infrastructure. Premier réflexe après chaque modification.
- Tests fonctionnels — un clean start Docker, puis des tests séquentiels bout-en-bout. Le verdict de référence.
- Tests électifs — vérifications spécialisées lancées à la demande, pour les sujets stables mais critiques.
- Tests opérationnels — longue durée, parallélisme, compression de temps. Là où les problèmes subtils se révèlent.
À chaque niveau, on ajoute de la complexité et du temps d'exécution. La règle : commencer par les tests les plus rapides et les moins coûteux, stabiliser à ce niveau, puis monter progressivement. Si un problème peut être détecté par un test unitaire, il ne devrait pas nécessiter un test opérationnel de deux heures pour être trouvé.
Des blocs composables
Un aspect clé de cette architecture : chaque niveau de test est un composant indépendant. On peut les enchaîner librement, les combiner, les répartir. Un humain peut lancer les tests unitaires pendant que l'agent de codage s'occupe d'un clean start et des tests fonctionnels. Ou inversement. Les tests opérationnels tournent dans des terminaux séparés sans interférer avec le reste.
Cette composabilité est essentielle pour un mode de développement AI-first. L'agent a besoin de blocs de validation clairs, autonomes, avec des verdicts sans ambiguïté. Les tests lui donnent exactement ça.
| Scénario | Commande | Durée typique |
|---|---|---|
| Validation rapide (CI) | mvn test -pl ezkey-tests |
~2 min |
| Suite complète standard | mvn test -pl ezkey-tests -P all-tests |
~10 min |
| Smoke check | mvn test -pl ezkey-tests -P smoke-tests |
<1 min |
| Tests électifs | mvn test -pl ezkey-tests -P elective-tests |
~10 min |
| Churn opérationnel | mvn test -pl ezkey-tests -P operational-churn-tests |
2+ heures |
09 — Ce que j'ai appris en chemin
Cette stratégie n'est pas apparue du jour au lendemain. Ezkey est un projet solo, et j'ai commencé comme beaucoup : avec du vibe coding naïf, très peu de tests, et une confiance excessive dans le fait que « ça marche sur ma machine ».
L'introduction des agents de codage en 2025 a changé la donne. Quand un agent modifie du code de manière autonome, sans filet de sécurité, les régressions arrivent vite. Il fallait un contrat clair entre l'humain et l'agent : voici les garde-fous, voici comment on vérifie, voici les verdicts.
La progression a été graduelle :
- D'abord quelques tests unitaires sur les services critiques.
- Puis l'infrastructure Docker pour avoir un environnement reproductible.
- Puis des plans de travail structurés incluant les critères de test.
- Puis les tests fonctionnels bout-en-bout avec la philosophie d'indépendance.
- Puis les tests électifs pour les sujets qui ne justifient pas une exécution systématique.
- Enfin les tests opérationnels, pour aller chercher les problèmes que les autres niveaux ne peuvent pas trouver.
Chaque étape a été motivée par un besoin concret — jamais par une ambition théorique de couverture. Et chaque étape a rendu le cycle suivant plus efficace : plus l'agent dispose de garde-fous fiables, plus on peut lui déléguer de travail avec confiance.
La prochaine frontière : les tests UI avec Playwright. Une base existe déjà pour valider les scénarios critiques de l'Admin UI, mais elle est volontairement limitée. L'objectif est de la faire grandir de façon ordonnée, en s'appuyant sur les mêmes fondations — Docker, clean start, indépendance des tests — et les mêmes valeurs de pragmatisme acquises en cours de route. Pas de big bang, le même chemin incrémental.
La stratégie de test dans Ezkey suit un fil conducteur simple : commencer petit, valider vite, monter en complexité seulement quand c'est justifié. Chaque niveau de test a un rôle précis et complémentaire. Et l'ensemble forme un système où l'humain et l'agent collaborent avec une confiance fondée sur des vérifications concrètes — pas sur des suppositions.
C'est un cercle vertueux : plus les tests sont solides, plus l'autonomie est possible. Plus l'autonomie est possible, plus on investit dans les tests. Et c'est ce cercle qui rend un projet solo AI-first non seulement viable, mais productif.
English: Same article in English
Voir aussi : Manifeste — Le futur du codage est déjà là
Voir aussi : Parallèle IA générative et No-Code / Low-Code