On ne communique jamais autant qu’on le souhaiterait sur les fonctionnalités livrées à chaque mise à jour du Cockpit, mais cette fois, toute l’équipe a vraiment hâte de voir l’accueil qui sera réservé à notre dernière version.
Celle-ci introduit deux fonctionnalités qu’on considère vraiment majeures, et qui pourraient d’ailleurs éclipser d’autres fonctionnalités pourtant tout aussi utiles :
- une analyse des tests et de la couverture des risques, mais sous un angle qui nous parait assez novateur
- une vérification des contraintes d’architecture spécifique à l’application
Ces deux fonctionnalités ont été conçues et implémentées selon un processus qui est depuis le début notre marque de fabrique :
- une longue phase de recherche, parfois très théorique, toujours sur des bases statistiques, pour définir de nouveaux modèles d’analyse fiables. Phase qui ne nous récompense pas toujours de nos efforts, il nous est arrivé plusieurs fois de reporter certaines fonctionnalités parce que nous ne trouvions pas la martingale. Mais nous collaborons de plus en plus avec le CETIC (Centre d’Excellence en Technologies de l’Information et de la Communication) sur cette phase, et la croissance continue de notre base de statistiques nous permet de travailler sur des modèles de plus en plus fiables (plusieurs millions de lignes de code analysées chaque mois)
- un gros travail de “pragmatisation” pour s’assurer que les modèles théoriques seront facilement compréhensibles par les utilisateurs, vraiment applicables sur selon les différentes typologies de projets, et apportent de réels bénéfices à nos clients.
Voici un aperçu de ces deux fonctionnalités, que nous détaillerons chacune dans de futurs billets.
Analyse des tests et couverture des risques
On reprochait souvent au Cockpit de ne pas traiter la partie tests unitaires / couverture de code. Nous n’avions pas intégré cette analyse car nous n’avions pas encore trouvé le bon angle de traitement pour proposer quelque chose de vraiment pertinent.
Pourquoi la couverture de code est souvent mal utilisée
Nous considérons la couverture de code comme une mesure à manier avec précaution, et l’utilisation qui en est proposée dans la plupart des outils ne nous convenait pas.
Pour rappel, la couverture de code mesure le pourcentage du code d’une application réellement exécuté suite à des tests, généralement des tests unitaires. Cette mesure peut concerner les instructions, les conditions, les fonctions, etc.
Un outil de couverture de code vous indiquera par exemple que 58% de votre application est couverte par vos tests unitaires. Il vous fournira également les chiffres pour chaque package (Java) / namespace (C#), classe ou méthode.
Le principe de la couverture de code consiste à dire : tous vos tests unitaires sont passés, mais ils ne testent que 58% de votre application. Autrement dit : les 42% restants sont peut-être totalement défectueux et vous ne le constaterez qu’en validation fonctionnelle (recette) voire en production. Raisonnement qui conduit certains à (s’)imposer des taux de couverture de 80% voire 90% (et certains ayatollahs ciblent probablement 100%).
Nos clients nous ont clairement indiqué que cette option n’était pas réaliste pour eux : le coût de tests devenait trop important et incompatible avec leurs délais de réalisation.
En outre, cette utilisation de la couverture de code génère deux inconvénients majeurs :
- quand on annonce couvrir 80% de son application, on ne tient pas compte de la criticité du code couvert. Peut-être que les 20% non couverts concernent précisément le code le plus critique, et que les 80% sont artificiellement couverts par toute une ribambelle d’accesseurs (getter/setter en Java, propriétés en C#) ou autres codes non critiques (initialisations de constantes, méthodes de débug, classes bouchons, …).
- une équipe de développeurs à qui on demande un objectif de x% de couverture cherchera naturellement le chemin le plus court pour y arriver, c’est le risque d’une politique du chiffre. Et ce chemin consiste évidemment à tester les méthodes les plus longues (pour la couverture par instructions), même si elles n’ont aucune criticité
La couverture pondérée par la pertinence de test
Pour résoudre cette problématique, notre proposition consiste à pondérer la couverture de code selon la pertinence à tester chaque élément de code. Le Cockpit est désormais capable de caractériser que tel élément est critique et doit donc être couvert à 80% (par exemple), quand tel autre élément aura un objectif de 40%, voire aucun objectif parce qu’il ne présente aucun intérêt au niveau des tests.
Nous ramenons donc des taux de couverture bruts à des taux pondérés selon l’intérêt à tester le code, ce qui nous semble être une méthode plus pragmatique pour tester efficacement son code et éviter d’investir dans des tests moins rentables. Par exemple, dans cette représentation TreeMap, des classes très peu couvertes pourront être affichées en vert parce que l’objectif qui leur a été fixé est faible, alors que des classes bien couvertes pourront être affichées en rouge parce que leur objectif est fort :

Pour cela, nous nous appuyons sur une nouvelle mesure développée conjointement avec le CETIC : le TestRelevancyIndex (TRI). Le TRI mesure pour chaque méthode son intérêt à être testée unitairement. C’est un index qui se base sur différentes mesures recueillies par notre plateforme. Parmi ces mesures, on trouve par exemple la complexité cyclomatique, ou le nombre d’appels entrants, mais aussi des mesures plus originales telles que le nombre cumulé de violations détectées dans une méthode, car des études ont montré que plus un bloc de code a provoqué de bugs, plus il est susceptible d’en provoquer par la suite (e.g. : Static Analysis Tools as Early Indicators of Pre-Release Defect Density). Nous pouvons ainsi montrer dans le Cockpit les méthodes à tester en priorité, en les classant selon une échelle réduite à 5 niveaux Critique, Haute, Moyenne, Basse, Aucune :

Cette échelle de priorité est basée sur des intervalles de TRI auxquels sont associés des seuils de couverture de code. Cette échelle peut être ajustée selon les besoins du projet dans le module des exigences techniques (avec des fonctions d’aperçu et une cartographie du TRI pour aider à positionner les seuils) :

Cette mesure s’appuie aussi sur la notion de Business Risks (risques métier) pour orienter les tests sur les éléments fonctionnels les plus importants de l’application. Des modules fonctionnels sont définis, associés aux éléments du code qui les implémentent, puis pondérés par un facteur de criticité. Plus ce facteur est élevé, plus le TRI de chaque élément de code correspondant sera élevé, avec donc des exigences de tests renforcées.
Cette méthode s’adresse également à des projets qui n’ont pas encore adopté une démarche de tests unitaires totalement structurée, ou qui ne mesurent pas ce qui est réellement testé ; notre expérience montre que ces projets sont encore majoritaires. Ils peuvent ainsi tirer le maximum de l’effort de test qu’ils peuvent réaliser en l’orientant sur les points clés de l’application et ainsi s’inscrire dans une véritable politique de retour sur investissement.
D’ailleurs, pour faciliter encore plus ce processus, nous avons conçu, toujours avec le CETIC, un deuxième index : le TestEffortIndex (TEI). Cet index mesure l’effort à fournir pour tester une méthode. Comme le TRI, il est basé sur un ensemble de métriques unitaires, parfois communes avec le TRI (comme la complexité cyclomatique), parfois spécifiques (nombres de paramètres, de variables, …).
Voici une courte vidéo d’introduction sur cette fonctionnalité :
Les premiers retours sur cette fonctionnalité sont très positifs, nous attendons la suite avec impatience !
Contrôle du modèle d’architecture
Autre domaine d’analyse sur lequel nous travaillons depuis longtemps : l’architecture logicielle d’une application. Cette fonctionnalité est naturellement destinée aux profils architectes. L’objectif principal est de leur permettre de contrôler le respect du modèle d’architecture dont ils sont garants en analysant les appels et dépendances au sein du code. Pour mieux comprendre, voici un exemple de schéma qu’ils pourront définir en mode WYSIWYG directement sur le Cockpit :

Chaque “boîte” identifie une partie du code de l’application : une couche (IHM, couche métier, DAO, …), ou des composants (gestionnaire d’authentification, moteur de recherche, …). Pour cela, des classes ou des packages/namespaces sont associés à chaque boîte. Ensuite, l’architecte définit des contraintes (des flèches) pour autoriser ou interdire les appels entre les différentes boîtes. Tout ceci prend quelques minutes grâce à une interface simple. Il est également possible de définir plusieurs modèles pour une même application.
Ces modèles sont alors pris en compte par les analyses Kalistick. Tous les appels ou dépendances (héritages, associations) ne respectant pas les contraintes définies sont remontés comme des violations. Chaque violation est précisément identifiée au sein du code source pour faciliter les corrections.
Parmi ces violations, vous trouverez par exemple des dépendances cycliques, mais seulement les plus pertinentes : celles qui lient les boîtes et concernent donc des composants/couches avec un certain degré d’autonomie. Car la plupart du temps, identifier des dépendances cycliques entre packages ou classes proches ne sous semble pas pertinents quand ils appartiennent au même ensemble.
A quoi sert cette analyse au-delà de satisfaire la propension de tout architecte à sortir de beaux modèles d’architecture ? Voici quelques exemples :
- homogénéiser le comportement d’une application. Par exemple s’assurer que les écritures de logs utilisent telle API spécifique, que les accès aux données passent par telle couche, que telle librairie ne soit utilisée que par tel composant, …
- assurer l’étanchéité de certains composants pour faciliter leur évolution et limiter les effets imprévus mais aussi les rendre mutualisables avec d’autres applications
- éviter les failles de sécurité en s’assurant par exemple que des appels directs vers une couche d’accès aux données ne sont jamais réalisés sans passer par une couche métier qui serait responsable de contrôles de validation
Pour cette analyse de l’architecture, nous travaillons en collaboration avec le laboratoire LIESP de l’INSA de Lyon, dans le cadre du projet PACTE-Qualité, qui rassemble des éditeurs de la région Rhône-Alpes (axe R&D et Innovation du Cluster EDIT). Donc toujours dans une association recherche théorique et application pratique !
Cette fonctionnalité est publiée en version Labs car ce n’est que la première étape de ce que nous comptons offrir d’ici la fin du semestre. L’IHM ainsi que l’analyse du code vont être enrichies régulièrement d’ici les prochaines semaines, mais nous avons surtout lancé un nouveau chantier sur la création du modèle qu’on espère pouvoir qualifier de révolutionnaire…
Voici une courte vidéo d’introduction sur cette fonctionnalité :
Testez la démo !
Si vous souhaitez voir de plus près à quoi ressemblent ces deux fonctionnalités, notre site de démonstration https://demo.kalistick.fr est ouvert à tous. Et n’hésitez pas à nous soumettre vos questions ou demandes d’amélioration !
