Tests automatisés sur Business Central : ce que montrent nos propres dépôts AL
Le niveau de tests automatisés d’une extension Business Central dépend moins de l’équipe que de celui qui les paie, comme le montrent deux de nos dépôts AL. Le premier, une app AppSource de facturation électronique, porte 1 770 tests pour 223 objets de production. Le second, un produit métier de près de mille objets, en porte 17. Même entreprise, même source de rulesets, même socle DevOps.
Ce second chiffre ne nous flatte pas, et nous le publions exprès. C’est ce que nous savons de plus utile sur les tests en Business Central, et presque personne ne publie le sien.
Comment avons-nous compté ?
La méthode est assez simple pour être refaite sur votre propre dépôt.
- Un test, c’est une méthode qui porte l’attribut
[Test]. La documentation Microsoft est explicite : cet attribut ne peut être posé que dans une codeunit dont la propriétéSubTypevautTest. - Une codeunit de test, c’est une codeunit avec
Subtype = Test. - Un objet de production, c’est toute table, page, codeunit, état, requête, XMLport, énumération, jeu d’autorisations ou leurs extensions, déclaré hors des apps de test. Les dossiers de traduction française ne contiennent aucun objet AL et n’ajoutent rien.
Tous les comptes portent sur les fichiers AL suivis de la branche distante de chaque dépôt : pas d’échantillon, pas d’estimation.
| App AppSource | Produit métier | |
|---|---|---|
| Objets de production | 223 | 976 |
| Codeunits de test | 166 | 2 |
| Méthodes de test | 1 770 | 17 |
| Tests par objet de production | environ 7,9 | environ 0,02 |
Le produit est environ 4,4 fois plus gros. Il compte 104 fois moins de tests en valeur absolue, et à peu près 460 fois moins par objet.
L’écart est-il une question de compétence ?
Non, et c’est ce qui rend la mesure digne d’être publiée. Les deux bases de code sortent de la même entreprise, avec les mêmes standards et le même socle DevOps. Les deux dépôts tournent sur AL-Go for GitHub, le modèle de Microsoft pour le DevOps Business Central. Les deux activent CodeCop et UICop, et les deux tirent leurs rulesets d’analyse du même dépôt commun à l’organisation. Personne chez Asio ne pense que les tests sont facultatifs.
Ce qui sépare les deux dépôts, c’est la ligne de budget qui finance le code.
Qui paie les tests ?
Sur l’app AppSource, Asio est propriétaire. Personne ne valide de devis pour la suite de tests, donc personne ne peut la rayer. Un test fait simplement partie de ce que « terminé » veut dire pour un changement, au même titre que la compilation.
Sur le produit métier, le travail est financé projet par projet. Les tests apparaissent dans le chiffrage comme une ligne à part, à côté de la fonctionnalité qu’ils protègent. Et une ligne qu’on peut chiffrer, on peut la décliner. Dans un arbitrage budgétaire, c’est la ligne la plus facile à retirer : la fonctionnalité part quand même en production, et la perte ne se voit pas le jour de la livraison.
Chaque arbitrage, pris seul, se défend. La somme, sur des années, donne 17 tests pour près de mille objets. Nous décrivons ici un mécanisme, pas une personne : ce format de chiffrage, c’est nous qui l’avons écrit, et c’est ce format qui produit le résultat.
À quoi ressemble une suite de tests non financée ?
À une suite qui s’est arrêtée.
Dans le dépôt du produit, l’app de test existe toujours, avec ses deux codeunits de test. Mais les paramètres du projet AL-Go indiquent "testFolders": []. L’app de test n’est pas déclarée au pipeline : les 17 tests ne font tout simplement pas partie du build d’intégration continue. Ils passent ou échouent seulement si quelqu’un les lance à la main.
C’est l’état final habituel. Une suite de tests qui n’est pas financée en continu n’est pas maintenue ; un test non maintenu finit par échouer pour des raisons qui n’ont rien à voir avec le code ; et un test rouge qui ne bloque rien finit retiré du pipeline. La suite survit sous forme de dossier.
À comparer avec la configuration de l’app AppSource : deux dossiers de test déclarés, l’analyseur AppSourceCop activé, et un jeu d’analyseurs supplémentaires dont un consacré à l’automatisation des tests. L’outillage n’est pas plus sophistiqué. Il est simplement allumé.
Que se passe-t-il quand les tests font partie de la définition de « terminé » ?
La suite accélère.
Nous avons compté l’app AppSource à plusieurs dates de 2026, avec la même méthode :
| Date | Objets de production | Tests | Tests par objet |
|---|---|---|---|
| 15 juin | 47 | 60 | 1,3 |
| 15 juillet | 75 | 210 | 2,8 |
| 15 septembre | 118 | 682 | 5,8 |
| 1er octobre | 184 | 1 100 | 6,0 |
| 3 novembre | 223 | 1 770 | 7,9 |
En moins de cinq mois, l’app a grossi de 4,7 fois, et sa suite de tests de 29 fois. Quand nous avons décrit la discipline de mise à jour de nos extensions AL fin août, le chiffre que nous publiions pour la même app était de 433 tests. Il a à peu près quadruplé depuis.
Le ratio monte parce que chaque bug corrigé devient un test, chaque cas limite repéré en revue devient un test, et chaque test rend le changement suivant moins cher à vérifier. La suite se rembourse sur la vitesse du changement d’après, pas sur celui pour lequel elle a été écrite.
Pourquoi est-ce plus important sur Business Central qu’ailleurs ?
Parce que le code que vous possédez n’est pas stable, même quand vous n’y touchez pas. Microsoft livre deux mises à jour majeures par an, et votre extension doit compiler et tourner sur chacune. Chaque objet possédé est une chose à revalider deux fois par an.
Sans tests automatisés, cette revalidation est manuelle, partielle, et repayée à chaque vague. Avec, c’est une exécution de pipeline. Une codeunit de test a une propriété qui compte ici : contrairement à une codeunit normale, elle continue d’exécuter ses autres méthodes de test après un échec. Une seule exécution vous dit donc tout ce qui a cassé, pas seulement la première chose.
C’est aussi pour cela que la taille de la surface spécifique est une question de tests. Plus vous possédez d’objets, plus une suite de tests absente vous coûte avec le temps. Nous défendons ailleurs que certaines personnalisations ne devraient jamais être écrites ; une personnalisation écrite et jamais testée, c’est la moitié coûteuse de ce problème.
Que doit en faire un acheteur ?
Arrêter de traiter les tests comme une option du devis :
- Demander le chiffre. Combien de méthodes
[Test]porte votre extension, pour combien d’objets ? N’importe quel partenaire peut répondre en cinq minutes avec la méthode ci-dessus. Si personne ne sait, vous avez votre réponse. - Chiffrer les tests dans la fonctionnalité, pas à côté. Une fonctionnalité livrée sans ses tests n’est pas moins chère : elle déplace le coût vers la prochaine fenêtre de mise à jour, où il se paie avec intérêts et sans devis.
- Vérifier le pipeline, pas le dossier. Une app de test qui existe mais n’est pas déclarée au build n’est pas un filet de sécurité. Demandez à voir une exécution d’intégration continue où les tests ont tourné.
L’approche Asio Services
La mesure ci-dessus sort de nos propres dépôts, donc les trois règles s’appliquent d’abord à nous : une app de test déclarée au pipeline, un chiffre que chacun peut recompter, et des tests chiffrés dans chaque changement plutôt qu’à côté.
Si vous préparez du développement Business Central qui devra traverser de nombreuses vagues de mise à jour, ou si vous avez hérité d’une extension sans savoir ce qui la protège, commencez par notre formulaire clarté. Nous vous dirons ce qui est testé, ce qui ne l’est pas, et ce que l’écart vous coûte.
FAQ
Comment écrire un test automatisé dans Business Central ?
On crée une codeunit avec Subtype = Test et on y ajoute des méthodes globales décorées de l’attribut [Test]. Par défaut, chaque méthode de test s’exécute dans sa propre transaction de base de données, et son résultat est soit un succès, soit un échec. Une codeunit d’exécution (Subtype = TestRunner) peut orchestrer plusieurs codeunits de test et récupérer les résultats.
Comment mesurer la couverture de tests d’une extension AL ?
La mesure honnête la plus simple : le nombre de méthodes [Test] divisé par le nombre d’objets de production, compté dans le dépôt. Elle ne dit pas quelles lignes sont exécutées, mais elle est rapide, reproductible et comparable d’un dépôt à l’autre. Vérifiez ensuite que les apps de test sont bien déclarées dans le pipeline de build.
Les tests automatisés valent-ils leur coût sur un projet ERP spécifique ?
Sur un système mis à jour deux fois par an, oui : le coût d’un test absent se repaie à chaque mise à jour. Sur notre app AppSource, la suite de tests a été multipliée par 29 en moins de cinq mois quand le code l’était par 4,7, parce que chaque test rend le changement suivant moins cher à vérifier.
Pourquoi une suite de tests cesse-t-elle de tourner ?
En général parce qu’elle a été financée une fois, et plus depuis. Les tests non maintenus se mettent à échouer pour des raisons sans rapport avec le code, et un test rouge qui ne bloque rien finit retiré du pipeline. Dans AL-Go for GitHub, une app de test absente du paramètre testFolders du projet ne fait pas partie du build d’intégration continue.