La couverture de tests est l’une de ces métriques sur laquelle tout le monde s’accorde qu’elle est importante et que personne ne veut être responsable d’améliorer. Vous connaissez le schéma : une base de code démarre à 90 % de couverture, puis quelques fonctionnalités sont livrées sans tests, puis un refactoring saute la mise à jour des tests parce que « on y reviendra ». Six mois plus tard, vous êtes à 60 % et remonter ressemble à un travail à temps plein que personne n’a signé.
Cette étude de cas détaille comment une équipe a utilisé un workflow JieGou avec l’agent de codage pour passer de 60 % à 94 % de couverture de tests en trois semaines — automatiquement, avec une intervention humaine minimale.
Le problème
La base de code en question était un backend Node.js avec 312 modules. La couverture déclinait régulièrement depuis des mois :
- 60 % de couverture de tests globale, en baisse par rapport à 85 % au début de l’année
- 47 modules sans aucun test — principalement des fonctions utilitaires, des transformateurs de données et de la logique de validation
- De nouvelles fonctionnalités livrées sans tests car les développeurs se concentraient sur les délais de livraison
- Le pipeline CI exécutait les tests mais n’imposait pas de seuils de couverture, donc le déclin était invisible jusqu’à ce que quelqu’un vérifie
L’équipe avait essayé d’allouer des « sprints d’écriture de tests » deux fois. Les deux fois, le sprint avait été dépriorisé quand une escalade client survenait. L’écriture manuelle de tests est lente, fastidieuse et en concurrence avec toutes les autres priorités.
Le workflow
L’équipe a construit un workflow en 4 étapes dans JieGou qui se déclenche automatiquement quand du code est fusionné sur main. Aucune intervention manuelle requise sauf si un test généré nécessite une révision.
Étape 1 : Déclencheur — Webhook GitHub sur fusion de PR
Le workflow démarre avec un déclencheur webhook qui se déclenche quand une pull request est fusionnée sur main.
trigger:
type: webhook
source: github
events: ["pull_request.closed"]
filters:
- field: "action"
value: "closed"
- field: "pull_request.merged"
value: true
- field: "pull_request.base.ref"
value: "main"
La charge utile du webhook inclut la liste des fichiers modifiés, le SHA du commit de fusion et les métadonnées de la PR. Tout cela alimente l’étape suivante.
Étape 2 : Analyser — Identifier les fonctions non testées
Une étape de recette prend la liste des fichiers modifiés du webhook et les croise avec le rapport de couverture le plus récent. Le prompt de la recette est direct :
step: analyze-coverage
type: recipe
model: claude-sonnet-4-6
input:
changed_files: "{{trigger.pull_request.changed_files}}"
coverage_report: "{{secrets.COVERAGE_REPORT_URL}}"
prompt: |
You are a test coverage analyst. Given the list of changed files
and the coverage report (lcov format), identify:
1. Which changed files have less than 80% line coverage
2. Which exported functions in those files have zero coverage
3. The function signatures and a brief description of what each does
Output a JSON array of objects with fields:
file_path, function_name, signature, description, current_coverage
Cette étape produit une liste structurée de exactement quelles fonctions ont besoin de tests. Elle filtre les fichiers qui ont déjà une couverture adéquate, pour que l’agent de codage ne perde pas de temps sur du code déjà testé.
Étape 3 : Générer — L’agent de codage écrit les tests
C’est là que l’agent de codage fait le gros du travail. Il reçoit la liste des fonctions non testées, clone le dépôt et écrit des tests unitaires pour chacune.
step: generate-tests
type: coding-agent
model: claude-sonnet-4-6
repo: "{{secrets.REPO_URL}}"
branch: "test-gen/{{trigger.pull_request.number}}"
maxTurns: 30
sandbox:
memory: "1GB"
timeout: "5m"
network: false
tools:
- read
- write
- edit
- bash
- glob
- grep
input:
untested_functions: "{{steps.analyze-coverage.output}}"
task: |
You are a senior test engineer. Your job is to write unit tests for
the following untested functions:
{{untested_functions}}
Instructions:
1. Read the source file for each function to understand its behavior
2. Read existing test files in the same directory for style conventions
3. Write tests using the project's test framework (vitest)
4. Each test file should cover: happy path, edge cases, error handling
5. Run `npm test -- --reporter=verbose <test-file>` after writing each
test file to verify all tests pass
6. If a test fails, read the error, fix the test, and re-run
7. Do NOT modify source code — only create or update test files
Match the existing test style: describe blocks, clear test names,
arrange-act-assert pattern. Use the existing fixtures and helpers
where available.
Les choix de configuration clés ici :
- Modèle : Claude Sonnet 4.6 — assez rapide pour la génération de tests à haut volume, assez intelligent pour comprendre les signatures de fonctions complexes et les cas limites.
- Sandbox : 1 Go de mémoire, 5 minutes de timeout — assez de marge pour les exécutions
npm testsans risquer des processus incontrôlés. - Réseau désactivé — l’agent n’a pas besoin d’accès externe ; tout est dans le dépôt cloné.
- maxTurns : 30 — permet à l’agent d’itérer sur plusieurs fichiers de tests et de corriger les échecs sur plusieurs cycles.
La description de la tâche inclut une instruction critique : lire les fichiers de tests existants d’abord. Cela garantit que les tests générés correspondent aux conventions de l’équipe — même style d’assertion, même structure describe/it, mêmes schémas de fixtures. Sans cela, les tests générés sont techniquement corrects mais stylistiquement incohérents, ce qui crée des frictions lors de la revue.
Étape 4 : Soumettre — Créer une PR avec les résultats
Après que l’agent de codage a terminé, une dernière étape de recette crée une pull request avec les tests générés et inclut les résultats de tests dans la description de la PR.
step: submit-pr
type: recipe
model: claude-sonnet-4-6
input:
modified_files: "{{steps.generate-tests.modified_files}}"
test_output: "{{steps.generate-tests.output}}"
source_pr: "{{trigger.pull_request.number}}"
prompt: |
Create a pull request with the following:
- Title: "test: auto-generated tests for PR #{{source_pr}}"
- Body: Include a summary of tests added, functions covered,
and the full test output showing all tests passing
- Label: "auto-tests"
- Request review from the original PR author
La PR est étiquetée auto-tests pour que l’équipe puisse filtrer et réviser par lots les PR de tests générés. Demander la revue à l’auteur de la PR originale signifie que la personne la plus familière avec le code voit les tests en premier.
Résultats
L’équipe a exécuté ce workflow pendant trois semaines sur chaque fusion sur main. Voici les chiffres :
| Métrique | Valeur |
|---|---|
| Couverture de départ | 60 % |
| Couverture finale | 94 % |
| Tests générés | 847 |
| Pull requests créées | 42 |
| Temps moyen de revue de PR | 12 minutes |
| Tests ayant détecté des bugs existants | 23 |
| Coût moyen par jour | ~4,50 $ (API Claude) |
Quelques éléments se distinguent.
847 tests sur 42 PR signifie environ 20 tests par PR. La plupart des PR couvraient 2-4 fichiers source, avec 5-6 tests par fonction (chemin nominal, conditions aux limites, cas d’erreur, entrées null/undefined, cas limites de coercition de type).
12 minutes de temps moyen de revue est remarquable. La plupart des tests générés étaient corrects dès la première passe. Les principaux commentaires de revue étaient stylistiques — renommer les descriptions de tests, ajuster les données de fixtures pour qu’elles soient plus réalistes. Très peu de tests nécessitaient des corrections fonctionnelles.
23 tests ont détecté des bugs réels dans le code existant. C’est le résultat le plus intéressant. L’agent de codage a écrit un test pour une fonction d’analyse de dates qui attendait le format ISO 8601, et le test a révélé que la fonction retournait silencieusement Invalid Date pour les horodatages avec des décalages de fuseau horaire. Ce bug était en production depuis quatre mois. Des découvertes similaires dans la logique de validation, des erreurs de décalage unitaire dans les helpers de pagination et une condition de course dans un module de cache.
4,50 $/jour en coûts d’API Claude est trivial comparé au temps d’ingénierie économisé. Une estimation conservatrice : écrire 847 tests manuellement à 15 minutes par test prendrait environ 212 heures — plus de cinq semaines du temps d’un ingénieur à temps plein.
Leçons apprises
Après trois semaines d’exécution de ce workflow, l’équipe a affiné son approche en fonction de ce qui fonctionnait et de ce qui ne fonctionnait pas.
Commencer par les fonctions pures
La première version du workflow ciblait tout le code non testé. Cela fonctionnait bien pour les fonctions pures — utilitaires, validateurs, transformateurs, formateurs — car elles ont des entrées claires, des sorties claires et aucun effet de bord. L’agent pouvait lire la fonction, comprendre le contrat et écrire des tests complets.
Les modules complexes avec des connexions à la base de données, des appels API externes et un état mutable partagé étaient plus difficiles. L’agent générait parfois des tests qui mockaient trop ou testaient les détails d’implémentation plutôt que le comportement. L’équipe a ajusté l’étape d’analyse pour prioriser d’abord les fonctions pures et mettre en file d’attente les modules complexes pour revue manuelle.
Fournir des exemples de tests existants
Le changement le plus impactant a été d’ajouter cette instruction à la description de la tâche : « Lire les fichiers de tests existants dans le même répertoire pour les conventions de style. » Avant cette instruction, l’agent produisait des tests fonctionnellement corrects mais qui utilisaient des schémas d’assertion différents, des structures de blocs describe différentes et des conventions de nommage différentes du reste de la suite de tests. Après l’instruction, les tests générés étaient quasiment indiscernables de ceux écrits à la main.
Exécuter les tests avant la création de la PR
Les premières itérations du workflow soumettaient des PR avec des tests non vérifiés. Certains avaient des erreurs d’import, des fixtures manquantes ou des échecs d’assertion. L’ajout de l’étape npm test à l’intérieur de la tâche de l’agent de codage — et l’instruction de corriger les échecs avant de terminer — a éliminé presque tous ces problèmes. La boucle itérative de l’agent (écrire le test, exécuter le test, lire l’erreur, corriger le test, ré-exécuter) est exactement comme un humain travaillerait, mais plus vite.
Réviser pour la qualité, pas seulement la couverture
La couverture est une métrique proxy. Un test qui asserte expect(result).toBeDefined() couvre techniquement la fonction mais ne vérifie pas le comportement. L’équipe a ajouté une checklist de revue pour les PR de tests générés :
- Chaque test vérifie-t-il un comportement significatif, pas seulement que la fonction s’exécute ?
- Les cas limites sont-ils couverts (entrées null, tableaux vides, valeurs aux limites) ?
- Les descriptions de tests énoncent-elles clairement quel comportement est testé ?
- Les assertions sont-elles spécifiques (
.toBe(42)et non.toBeTruthy()) ?
La plupart des tests générés passaient cette checklist sans modifications. Les quelques-uns qui ne la passaient pas étaient faciles à repérer et à corriger pendant la revue de 12 minutes.
La configuration complète
Pour les équipes qui veulent reproduire ce workflow, voici la configuration complète des étapes :
workflow:
name: "Auto Test Generation"
description: "Generate unit tests for untested code on PR merge"
trigger:
type: webhook
source: github
events: ["pull_request.closed"]
filters:
- field: "pull_request.merged"
value: true
- field: "pull_request.base.ref"
value: "main"
steps:
- id: analyze-coverage
type: recipe
model: claude-sonnet-4-6
# ... (voir Étape 2 ci-dessus)
- id: generate-tests
type: coding-agent
model: claude-sonnet-4-6
dependsOn: [analyze-coverage]
# ... (voir Étape 3 ci-dessus)
- id: submit-pr
type: recipe
model: claude-sonnet-4-6
dependsOn: [generate-tests]
# ... (voir Étape 4 ci-dessus)
Le workflow s’exécute de bout en bout en 8-15 minutes selon le nombre de fonctions non testées. La majeure partie de ce temps est l’agent de codage qui itère à travers les fichiers de tests et exécute npm test après chacun.
Et ensuite
L’équipe étend maintenant le workflow dans deux directions :
- Tests d’intégration — une étape d’agent de codage séparée qui génère des tests d’intégration pour les endpoints API, en utilisant les helpers de test existants et les fixtures de base de données
- Application de la couverture — une vérification CI qui fait échouer le build si la couverture descend en dessous de 90 %, maintenant que la base est assez élevée pour l’imposer
La leçon plus large : la couverture de tests n’a pas à être une corvée manuelle. Avec le bon workflow, vous pouvez la traiter comme un pipeline automatisé qui tourne en parallèle de votre CI/CD existant — pas de sprints dédiés, pas de dépriorisation, pas de dette de couverture.
L’agent de codage est disponible sur les plans Pro et supérieurs. Construisez votre premier workflow de codage.