Stratégie de test du noyau
1) Principes
L'équilibre pyramidal-trophée. Base - tests modulaires et contractuels rapides ; ci-dessus - composantes et intégration ; au sommet se trouve la couche minimale mais de valeur e2e.
Shift-left. Plus tôt nous attrapons un défaut (linter, analyse statique, property-based), moins cher.
Deterministic by design. Nous gérons le temps, le réseau, le random et les dépendances externes.
L'économie de la qualité. Tout test est une « assurance » : l'objectif est de minimiser les coûts totaux (défauts + accompagnement des tests).
L'orientation risque. La couverture se concentre sur les invariants commerciaux et les protocoles (contrats, idempotence, consistance).
2) Niveaux d'essai et zones de responsabilité
2. 1 unité (modulaire)
Vérifie la logique pure sans I/O.
Nous ne mouillons que les frontières (port/adaptateur), nous utilisons les usines pour les données.
Rapides (≤50 -100 ms/test), parallèles.
2. 2 Contrat (fournisseur ↔ consommateur)
Les contrats API (HTTP/gRPC/event) entre les services sont enregistrés.
Nous utilisons l'approche consumer-driven : les contrats sont stockés dans le VCS, vérifiés par le fournisseur CI.
Réduire la fragilité de l'intégration e2e.
2. 3 Composants (au-dessus du module, avec stockage réel)
Nous exécutons une partie du service avec une véritable base de données/cache dans le conteneur (Testcontainers).
Nous validons les migrations de schémas, les index, les transactions, les verrous.
2. 4 Intégration/Système (chemins de passage entre les services)
Nous soulevons un ensemble de services dans un environnement isolé.
Nous vérifions les invariants de bout en bout : transactionalité, retrai, idempotence, traitement des erreurs.
2. 5 E2E (couche minimale de « valeur »)
Protocoles réels et environnement « comme dans la vente », mais jeu de scénarios limité : paiement → confirmation → câblage ; inscription → vérification → connexion.
Utilisé pour la sortie de versions et la régression des fiches à haut risque.
3) Architecture de test
Ports/adaptateurs (Hexagonal). Le noyau d'affaires ne connaît pas HTTP/SQL ; les dépendances sont introduites via des interfaces.
Injection de temps/randome. 'Clock', 'Random' - dépendances ; dans les tests, nous fixons.
Abstraction configurable I/O. Files d'attente, OBD, KMS - via des interfaces avec des implémentations de test.
Invariants fonctionnels. Clairement, nous formulons des posts et des prédicats - ils sont plus faciles à tester et à surveiller.
4) Données pour les tests
Usines/builders au lieu de fictions JSON statiques : moins de fragilité.
Idempotent et reset-hook OBD avant le test (migrations → truncate → seed).
Catalogues de cas : « normes », « bords », « erreurs », « chaos ».
Synthétiques au lieu de vrais PD : générateurs, masquage, profils de vie privée.
5) Concurrence et idempotence
Épreuves de course (course) : entrées/réserves/blocages compétitifs.
Vérification des clés idempotentes (par exemple, '(opération, external_id)') : les appels répétés ne changent pas d'état.
Retraits et temporisations : nous garantissons l'exactitude des erreurs temporaires.
dedupe_key = hash(op + external_id)
if exists(executions, dedupe_key): return previous_result else:
reserve(dedupe_key)
result = do_operation()
store(executions, dedupe_key, result)
return result
6) Heure, temps, fuseaux horaires
Tous les temps stockés sont UTC ; dans les tests, on utilise 'FixedClock'.
Nous testons les cas DST (prises/sauts d'horloge), les fenêtres du « jour local ».
On vérifie les timates avec une montre monotonique ; simulons le tremblement NTP.
7) Résilience et chaos
Fault-injection : erreurs réseau, 5xx, retards, dégradation partielle (cache non disponible).
Tests de chaos dans un environnement pré-prod : déconnexion des nœuds, surcharge de files d'attente, ruptures BGP/Anycast (émulation).
Politiques fallback et dégradation UX : les tests doivent confirmer une « degradation graceful » correcte.
8) Performance
Micro-repères pour algorithmes critiques (avec fixation CPU/alloc).
Profils de charge : baseline (p50/p95), stress (pic), prolongé (soak) pour les fuites de mémoire.
Jeu de régression : build échoue si p95 latence est pire que baseline> X %.
9) Sécurité et conformité
SAST/Lint : recherche de vulnérabilités/antisubventions.
DAST/IAST : scripts de base sur le stand (échantillons XSS/SQLi/SSRF).
Secret-scan : absence de clés/mots de passe dans le code et les artefacts.
Tests de confidentialité : absence de PD dans les logs/pistes, respect de la « gestion des consentements », profils d'anonymisation pour les décharges.
10) Métriques de qualité et SLO
Taux de test et indice de flaky (kol dans les tests instables/semaine).
Coverage-cible :- 90-100 % pour les modules critiques du noyau,
- 70-80 % pour la périphérie (avec un accent sur les invariants).
- Release risk score : agrégat : changements dans les fichiers critiques × chute des repères × nouveaux flaky.
- Budget erroné : lien prod-SLO (aptyme/bugs) avec les expériences et la fréquence des sorties.
11) CI/CD et gates
Matrice des étapes :1. Lint/Format/TypeCheck
2. Unit + Property-based
3. Contract provider/consumer
4. Component (Testcontainers)
5. Integration + Perf smoke
6. Security (SAST/Secrets)
7. Build/Package + SBOM
8. Deploy to pre-prod + e2e + chaos smoke
Gates : stop à la chute des contrats, à l'augmentation de la latence, aux nouvelles vulnérabilités critiques.
Cache et sharding : nous accélérons la pipeline grâce au parallélisme et aux coups incrémentiels (par modules modifiés).
12) Tests de flaky : détection et traitement
Automoteur + quorum (2/3 des essais).
Détecteur de flocons-patterns : dépendance au temps/random/attentes implicites.
Quarantaine avec SLA : le test ne bloque pas les sorties, mais doit être corrigé/réécrit en N jours.
Tolérance zéro au flacon dans le « noyau » de la voie critique.
13) Property-based, mutations et tests de phase
Property-based : formulons des propriétés (commutation, idempotence, monotonie), générateurs de données limites.
Test de mutation : nous mesurons la « force » des tests (s'ils tuent les mutations apportées).
Fuzzing : protocoles/parsers/formats (JSON, Protobuf, CSV), en particulier aux frontières de sécurité.
prop "serialize/deserialize roundtrip":
forAll(randomModel()):
decode(encode(model)) == model
14) Observabilité et association avec les tests
Traces de test (trace-id dans les logs) : commode à repérer en pré-prod.
Les métriques snapshots sont stockées comme un artefact.
Contrôle des logs : pas de champs sensibles, taille des logs dans le SLO.
15) Documentation et procédures
Test Handbook : Où exécuter les tests, comment écrire les usines, comment mettre à jour les contrats.
Runbooks : réplique d'incident, diagnostic rapide, retour à la sortie.
Annuaire des invariants : liste des garanties système et liens vers les tests/alertes correspondants.
16) Chèque de l'architecte
1. Les invariants du noyau et les voies critiques sont-ils décrits ?
2. Y a-t-il une matrice des niveaux de test et de leur SLO (temps, stabilité) ?
3. Les contrats sont-ils versionnés et validés en IC par le fournisseur et le consommateur ?
4. Le temps/rand/réseau est contrôlé dans les tests (FixedClock, Fault-injector) ?
5. Testcontainers/OBD isolées configurés, les migrations sont-elles vérifiées ?
6. Y a-t-il des bazlines de performance et un gate de régression ?
7. SAST/Secrets-scan et contrôle de la confidentialité des logs inclus ?
8. Un registre de flaky est tenu et un SLA est-il en cours de correction ?
9. L'association des tests avec le prod-SLO et le budget erroné est-elle transparente ?
10. Documenté runbook 'et le catalogue des invariants ?
Conclusion
La stratégie de test du noyau n'est pas une liste d'outils, mais une capacité architecturale : conception de test, hiérarchie rigoureuse des niveaux, données gérables, tolérance aux pannes et métriques intégrées à CI/CD. En suivant les pratiques décrites, l'équipe reçoit des commentaires rapides et fiables, et les versions deviennent prévisibles et sécurisées.