Java et qualité, la couverture du code par les tests.
Cet été, la version 2.2 de Sonar a été publiée. Sonar est réputé être un outil de suivi de la qualité des projets Java et se retrouve régulièrement en bout de chaîne d’une intégration continue. Il faut dire que c’est un excellent outil pour le suivi des développements, mais il met aussi en évidence cette limite de la notion de « qualité » au sein des développements informatique.
En effet, ces indicateurs sont souvent pris directement en tant que métriques traduisant la qualité du code. Le problème est qu’elles surfent souvent sur la vague des idées à la mode. Ainsi, un indicateur que j’ai eu l’occasion de voire comme le plus important est la couverture du code par les tests.
la couverture du code par les tests représente la proportion de lignes de codes qui ont été exécutées par les tests par rapport aux nombre de lignes de code total. Ainsi, la relation qui est faite est que plus cette valeur est haute, plus le code est fiable car testé. L’objectif est donc d’atteindre une valeur haute et de s’y maintenir. On en déduit également que plus cette valeur est élevée, plus le code est de qualité car testé.
Mais quelle différence entre cette valeur et ce qu’elle représente ? Prenons un cas inspiré de ce qui peut se trouver en entreprise :
public class MyTest { @Test public void doMyClassDoStuffTest() { // Initialisation MyClass myClass = new MyClass(); // Initialisation de l'objet myClass DataObject dObject = myClass.doStuff(); Asser.assertNotNull(dObject); } }
Ce test est il pertinent ? Lu comme ça, pourquoi pas. La méthode doStuff renvoie un objet de type DataObject, tester que l’objet ne soit pas nul peut être pertinent. Mais il faut voir aussi que ce test parcourt toute la méthode, ce qui fait que la métrique générée considère que la méthode est testée à 100%. Le responsable est content, c’est bien.
Oui, mais voyons le code testé…
public class MyClass { public DataObject doStuff() { DataObject dObject = new DataObject(); // traitement metier dObject.setSomeData(someData); // autre traitement metier dObject.setSomeOtherData(someOtherData); return dObject; } }
Aïe… Quand on réalise un test unitaire, qu’est ce qui est censé nous intéresser ? Evidemment le bon déroulement des traitements métier. Or que test notre test unitaire ? La bonne création d’un objet à la première ligne de la méthode et son retour à la fin. Tout ce qu’il y a entre les deux est du simple bruit pour le test. Dans la pratique, il s’agissait du seul test parcourant cette méthode. Supprimer toutes les lignes autre que l’initialisation de l’objet et son retour ne change pas le résultat du test ni sa couverture (de la méthode tout du moins). Sauf que la méthode ne fait plus rien…
Les évangélistes du test unitaire insistent bien pourtant qu’il faut passer autant de temps sur le code du test que sur le code testé. Ces mêmes évangélistes diront qu’avec une approche Test Driven Development, TDD (Développement dirigé par les tests), ce cas de figure n’aurai pas été possible. Oui mais voila, le TDD n’est pas une manière universelle de produire du code. Mais le problème n’est pas là.
Le problème est que les gestionnaires du projet ont choisi des métriques pour suivre le projet. Cette métrique est le taux de couverture du code par les tests, sans aucun contrôle de l’efficacité même des tests. Cette métrique représente pour eux la Qualité du logiciel. On peut imaginer que dans certains projets, ces métriques sont également des métriques permettant d’évaluer le gestionnaire du projet.
Mais dans ces cas, on va observer que l’objectif du développement n’est plus l’implémentation efficace d’une fonctionnalité, mais que cette implémentation atteigne ces autres objectifs, c’est à dire soit dans les bornes acceptables des métriques.
Dans l’exemple cité, chaque composant a été clairement pris à la légère, et interprété de la manière la plus simpliste qui soit : les tests unitaires doivent couvrir le maximum de code, et ce taux de couverture représente la qualité du produit. Ce n’est évidemment pas aussi simple que ça. Le taux de couverture du code est un indicateur, mais il doit être accompagné par une validation des tests, et un seul test ne suffit en général pas. Un test unitaire traduit une intention : le comportement dans le cas idéal, ou dans des conditions prévues. Le test unitaire valide le code écrit dans le périmètre de ce qui a été testé. De ce fait, cette métrique ne peut être extrapolée à une représentation de la Qualité du produit. Elle y contribue, mais il est très difficile de corréler les deux.
Ainsi il est très important d’interpréter chaque information de manière limitée sans extrapoler. Le taux de couverture du code est un indicateur des tests écrits, mais ne reflète en rien leur efficacité. Sonar est un excellent outil de suivi de métriques, mais il ne traduit que la Qualité du développement (respect des règles de développement), pas du produit.
À propos de... Darko Stankovski
iT guy, photographe et papa 3.0, je vous fais partager mon expérience et découvertes dans ces domaines. Vous pouvez me suivre sur les liens ci-dessous.