IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Du code généré au code fiable grâce à un agent IA de tests unitaires
Par Amaury Levé

Le , par Amaury Levé

0PARTAGES

4  0 
Une demande courante adressée à un agent de codage se résume à une seule ligne : « Générer des tests unitaires. »

Cette demande laisse toutefois en suspens des questions importantes. Quel code doit faire l’objet de tests ? Quel framework de tests le projet utilise-t-il ? Où les tests doivent-ils être placés ? Comment le processus de build les détecte-t-il ? Que doivent vérifier ces tests ?

L’agent peut extraire ces informations du dépôt, écrire les tests et vérifier qu’ils fonctionnent.

C’est pourquoi nous avons développé code-testing-generator. Il s’agit d’un agent open source et polyglotte dédié à la génération de tests unitaires. Vous le trouverez dans le plugin dotnet-test du répertoire dotnet/skills.

L’agent écrit des tests unitaires. Il isole le code testé et simule les services externes ainsi que les autres dépendances externes.

Les tests d’intégration, de bout en bout, de navigateur et de performances ne font pas partie de son champ d’application actuel.

Il ne se contente pas de faire passer les tests. Il vérifie les assertions, les scénarios demandés et s’assure que l’exécution normale des tests du référentiel détecte bien les nouveaux tests.

Que se passe-t-il après l’instruction générative ?

L’agent ne commence pas immédiatement à écrire des tests. Il apprend d’abord à partir du référentiel, puis planifie, rédige et vérifie les tests.


Tout d’abord, analyse du référentiel

L’agent parcourt le référentiel à la recherche du code nécessitant des tests. Il détecte le langage et le framework de test. Il recherche également les tests existants qui indiquent où les nouveaux tests doivent être placés et quelle forme ils doivent prendre.

Il identifie également les commandes appropriées pour compiler et exécuter les tests.

Cela permet d’éviter un problème courant : un nouveau projet de test peut se compiler et réussir ses tests de manière autonome, mais ne jamais s’exécuter en intégration continue (CI) car il n’a pas été ajouté à la solution ou à la commande de test du référentiel. L’agent vérifie comment le référentiel détecte les tests et s’assure que les nouveaux tests y apparaissent bien.

Ensuite, choisir la charge de travail adéquate

Une méthode isolée ne nécessite pas de plan élaboré. Une solution complète, en revanche, en a besoin.

L’agent choisit parmi trois approches :

- Direct : lire le code concerné, écrire les tests et valider le résultat.
- En un seul passage : effectuer des recherches et planifier une seule fois, puis mettre en œuvre ce plan.
- Itératif : répéter le cycle pour couvrir une demande importante ou atteindre un objectif de couverture.

Ensuite, planifier et écrire les tests

Pour les tâches plus importantes, l’agent répertorie le code nécessitant des tests. Il commence par le code simple, puis passe au code comportant davantage de dépendances. Il associe chaque comportement à un fichier de test.

Le plan suit la demande. Une demande concernant un module ne doit pas modifier tous les projets de test du référentiel.

L’agent respecte les conventions locales et exécute les tests au fur et à mesure de son travail. Si le code généré ne se compile pas, il le corrige. Si une assertion est erronée, il lit le code source et corrige le test.

Il ne modifie pas le code de production pendant la génération des tests. Il évite également les tests unitaires qui appellent des URL externes, ouvrent des ports ou dépendent d’un timing précis.

Enfin, vérifiez que les tests sont utiles

Un test peut réussir tout en n’apportant que peu ou pas de valeur ajoutée. Par exemple, il peut se contenter de vérifier qu’un résultat n’est pas nul. Il peut tester la mauvaise méthode. Il peut même réussir si la méthode renvoie toujours une valeur par défaut.

L’agent vérifie ces problèmes avant de terminer :

1. Il envisage de petites modifications du code qui devraient faire échouer les tests. Il s’agit d’une forme allégée de test par mutation.
2. Il recherche les assertions faibles ou manquantes.
3. Il vérifie que chaque scénario demandé dispose d’un test correspondant.
4. Il construit l’espace de travail complet et exécute la suite de tests complète.
5. Il confirme que la commande de test du référentiel peut trouver les nouveaux tests.

Ce workflow transforme une brève invite en un résultat testé.

Ce que nous avons mesuré

Lors de notre dernier benchmark, l’agent a accompli 140 tâches sur 152. Copilot standard, qui utilisait le même outil sans notre plugin, en a accompli 120 avec le même modèle. Cela représente un taux de réussite de 92,1 % contre 78,9 %, avec 63 % d’échecs en moins.

Les gains les plus importants provenaient de consignes comme celle figurant au début de cet article. Nous appelons ces consignes « vagues » car elles laissent la plupart des décisions à l’agent.

Les instructions génératives vagues montrent pourquoi le flux de travail est important

Les consignes vagues ont produit les résultats les plus nets. L’agent a réussi 79 tâches sur 89, contre 59 pour Copilot en version standard. Le nombre d’échecs est passé de 30 à 10.

Nous avons utilisé notre benchmark interne de tests unitaires. Il contient 152 tâches issues de dépôts réels. Certaines consignes sont détaillées. D’autres sont vagues et laissent la plupart des décisions à l’agent.

Nous avons comparé quatre configurations : GitHub Copilot par défaut, Claude Code par défaut, notre agent dans GitHub Copilot et notre agent dans Claude Code. Sauf indication contraire, les résultats ci-dessous se concentrent sur la comparaison avec GitHub Copilot. Les deux configurations de Copilot utilisaient le même modèle et les mêmes consignes.

Une tâche n’était considérée comme réussie que lorsque :

- Le dépôt s’est compilé.
- Tous les tests ont été réussis.
- L’agent a ajouté au moins un test.
- L’agent n’a supprimé aucun test existant.

La ventilation complète montre que les consignes détaillées ont donné lieu à un ex æquo :


Cela représente 67 % d’échecs en moins sur les consignes vagues. Les 20 gains nets provenaient tous de ces tâches. Cela correspond à l’objectif de l’agent : effectuer les recherches que le développeur n’a pas incluses dans la consigne.

La même tendance s'est manifestée lorsque la consigne faisait référence à une modification du code. Le benchmark comporte 15 tâches demandant de créer des tests pour un diff spécifique. L'agent a réussi les 15. Copilot standard n'en a réussi aucune.

Des résultats plus probants, pas simplement davantage de tests

Les deux configurations ont exécuté les mêmes 152 tâches. Cela nous permet de comparer directement chaque résultat.


- Les deux ont réussi 119 tâches.
- Seul l’agent spécialisé a réussi 21 tâches.
- Seul Copilot en version standard a réussi une tâche.
- Les deux ont échoué à 11 tâches.


L’agent spécialisé a généré 2,3 % de tests en moins, avec une couverture moyenne pratiquement identique. Il a également accompli davantage de tâches et s’est montré environ 5,5 % plus rapide en moyenne. Ce gain provient de la fiabilité, et non de la production d’un plus grand nombre de tests.

Cet avantage s’étend à tous les modèles et langages

Le benchmark contient 45 tâches .NET. Elles couvrent plusieurs référentiels et des tâches de tailles différentes. Nous avons effectué la même comparaison avec trois modèles :


Le workflow a été bénéfique pour tous les modèles. Avec Opus, il a permis de remporter huit victoires supplémentaires sans aucune défaite. Avec Haiku, il a accompli neuf tâches C# de plus que la version standard de Haiku.

Le résultat suggère également qu’un workflow performant peut propulser un modèle de milieu de gamme près des meilleurs résultats. Sur l’ensemble des 152 tâches, la version spécialisée de GPT-5.5 a atteint 90,1 %. Ce résultat se situait à moins de deux points de celui de la version spécialisée d’Opus et à plus de 11 points au-dessus de celui de la version standard d’Opus.

L’agent inclut des recommandations pour .NET, Python, TypeScript, JavaScript, Java, Go, Ruby, Rust, Swift, Kotlin, PowerShell et C++. Il apprend les conventions de chaque référentiel au lieu d’appliquer systématiquement les modèles C#.

Au-delà de .NET, la même exécution d’Opus a couvert plusieurs de ces langages :


L’agent a réussi toutes les tâches Go et a plus que doublé le taux de réussite en Python. Il a également réussi cinq tâches Go et cinq tâches Python qui ciblaient une modification spécifique du code.

Le résultat n’a pas été meilleur dans tous les langages. Stock Copilot a réussi une tâche PowerShell de plus. Ces échantillons sont également de petite taille ; nous les considérons donc comme des indications utiles, et non comme des garanties pour chaque dépôt.

Un deuxième benchmark, plus difficile

Nous avons également testé le workflow sur 44 tâches de tests unitaires issues de SWE Atlas. Ce benchmark vérifie les exigences et utilise des modifications de code pour voir si les tests permettent de détecter des bogues.


L’agent spécialisé a terminé quatre tâches supplémentaires, sans aucune victoire obtenue uniquement avec la version standard.

Les taux de réussite sont bien inférieurs à ceux de notre benchmark interne. SWE Atlas est un défi de taille, et il reste encore beaucoup à améliorer.

Ce que nous avons appris et la suite

Le gain le plus évident concerne la fiabilité. L’agent était plus susceptible de fournir des tests unitaires qui se compilaient, réussissaient et correspondaient à la demande. La différence était la plus marquée lorsque la consigne était vague ou liée à une modification du code.

Une fois que les deux configurations ont produit un résultat valide, aucune n’a dominé tous les indicateurs de qualité. Stock Copilot a obtenu un score légèrement supérieur en matière d’assertions et de couverture. L’agent spécialisé a obtenu un score légèrement supérieur en matière d’hygiène des tests, notamment en termes de structure propre et d’évitement des modèles lents ou fragiles.

Ces groupes comportent des tâches différentes. Le groupe spécialisé comprend des tâches plus difficiles que Stock Copilot n’a pas menées à bien ; il ne s’agit donc pas d’un classement direct de la qualité. Nous considérons ces résultats comme des pistes pour les prochaines améliorations : des assertions plus approfondies et des tests des chemins d’erreur, tout en conservant une hygiène des tests rigoureuse.

L’efficacité est restée comparable après prise en compte des tâches menées à bien. L’agent a utilisé environ 3,2 % de tokens enregistrés en plus par tâche menée à bien. Ces chiffres incluent les entrées mises en cache ; ils ne représentent donc pas directement le coût.

Ces résultats ne concernent que les tests unitaires. Nous étudions actuellement dans quels cas ce même workflow pourrait être utile pour d’autres types de tests, mais nous n’avons pour l’instant aucun projet concret à annoncer.

Essayez l'agent

L'agent, les compétences et les conseils linguistiques sont disponibles en open source dans le répertoire dotnet/skills.

Vous pouvez utiliser le plugin dans la CLI de GitHub Copilot. Il est également disponible dans Visual Studio Code et VS Code Insiders via la prise en charge des plugins, qui est encore en version préliminaire. Nous travaillons également à la prise en charge de Visual Studio.

Ajoutez le marketplace et installez le plugin dans la CLI GitHub Copilot :

Code : Sélectionner tout
1
2
/plugin marketplace add dotnet/skills
/plugin install dotnet-test@dotnet-agent-skills


Redémarrez la CLI. Sélectionnez code-testing-generator dans la liste des agents. Essayez ensuitre l'instruction générative :

Code : Sélectionner tout
Generate unit tests.


Passez en revue le plan, les tests et les vérifications finales. Vous pouvez également demander des tests pour une fonction, un module ou un objectif de couverture spécifique.

Les bons tests ne se contentent pas d’être générés. Ils sont planifiés, créés, exécutés et vérifiés. C’est cette boucle de confiance que nous mettons en place.

Sources : From generated code to trusted code with a unit-test agent, Répertoire dotnet/skills

Et vous ?

Quel est votre avis sur le sujet ?

Voir aussi :

Les instructions génératives de l'IA sont aussi une dette technique, par Sean Goedecke

Couverture de code : comment la mesurer, comprendre les indicateurs et améliorer vos tests, avec Qodana, l'outil d'analyse de la qualité du code de JetBrains

Le jour où j'ai commencé à croire aux tests unitaires, par Benjamin Richner
Vous avez lu gratuitement 1 965 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.

Une erreur dans cette actualité ? Signalez-nous-la !