Améliorer la testabilité

Dominique Mereaux

Cet article donne quelques pistes de réflexion pour faciliter les tests dans le cadre d'une méthode de développement « cycle en V ».

Commentez

- Titre : Améliorer la testabilité

- Auteur : Dominique Mereaux

- Parution : 16 mai 2010

- Mise à jour : 4 juin 2020

- Licence : Copyright ® 2009 Dominique Mereaux. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- I-A. De quoi s'agit-il ?

- I-B. Quelques définitions

- II. Stratégie de test

- II-A. Quoi ?

- II-B. Comment ?

- III. Conception et codage

- III-A. Contrôlabilité

- III-B. Observabilité

- III-C. Dès la conception

- IV. Plan de test

- V. Exécution des tests

- V-A. Tests Unitaires

- V-B. Tests d'intégration

- V-C. Automatisation

- VI. Planification

- VII. Conclusion

- VIII. Références, liens

- IX. Remerciements

I - Introduction

I-A - De quoi s'agit-il ?

Un produit peut être plus ou moins facile à tester. Qui n'a pas été confronté un jour à ce type de problèmes :

1. Un logiciel sans spécifications ou des spécifications peu fiables : comment écrire le plan de test dans ces conditions ?

2. Une anomalie est détectée, mais il est impossible de localiser le problème ;

3. Une architecture peu adaptée aux tests unitaires.

Cet article a pour objectif d'étudier quels sont les points qui vont faciliter notre travail de testeur. Plusieurs éléments vont contribuer à améliorer la testabilité à différentes phases du processus de test d'un cycle en V.

I-B - Quelques définitions

1. Tests unitaires : tests de composants logiciels individuels faits par les développeurs.

2. Tests d'intégration : tests permettant de valider les interfaces entre composants. Ces tests sont généralement effectués par l'équipe de développement ou par une équipe dédiée dans le cas d'une intégration complexe.

3. Tests système : tests permettant de valider un système intégré par rapport à des exigences spécifiques. Ces tests sont généralement effectués par une équipe de validation dédiée ou par l'équipe de développement. Ces tests couvrent les aspects fonctionnels et non fonctionnels (performance, installation, robustesse, sauvegarde…).

4. Recette : processus qui permet au client de valider que la livraison des fonctionnalités développées par le fournisseur, correspond au cahier des charges.

II - Stratégie de test

Deux éléments de la stratégie de test concernent la testabilité :

1. Quels sont les objectifs qualité de nos tests ?

2. De quels moyens allons-nous avoir besoin ?

II-A - Quoi ?

Quand on parle de tester un produit le premier élément que nous exigeons est les spécifications fonctionnelles et pourtant il me semble encore plus important dans un premier temps de bien cerner quels sont les objectifs de nos futures campagnes de tests. Ces éléments sont essentiels pour calibrer nos efforts de test dans la bonne direction.
Imaginons que nous devons tester un nouveau service rendu par une interface Web. Nous ne sommes pas les premiers sur ce marché et la concurrence est rude. Il est d'autre part vital pour la société d'obtenir rapidement 5 % de part de marché. Les éléments stratégiques concernant le produit sont donc connus. Ils sont essentiels pour porter les efforts de test au bon endroit.
L'ergonomie doit permettre au client de trouver facilement les services qu'il désire, et les accès au site doivent être rapides sous peine de voir le client se tourner vers un site équivalent.
En plus des tests fonctionnels classiques, il va falloir valider la performance des interfaces et leur ergonomie. Ces tests demandent des compétences particulières qu'il faudra prévoir. À ce stade nous voyons également qu'il faudra un environnement de test et des moyens dédiés. Ces éléments qui seront traités dans le chapitre suivant, contribuent également à la testabilité du produit.

II-B - Comment ?

Nous voulons mettre notre logiciel en situation réelle et simuler tous les scénarios d'utilisation et d'erreur possibles. En règle générale, nous ne travaillons pas dans le monde réel, mais dans un environnement simulé qui se rapproche du réel. Nos outils devront :

1. Être capables de simuler un environnement réel en mode nominal, erreur et charge ;

2. Être fiables c'est-à-dire eux-mêmes testés.

Simuler un environnement externe peut être complexe, ces outils peuvent se trouver sur le marché par exemple un simulateur de réseau GSM pour tester un mobile. Dans ce cas, l'outil est connu et testé. Il suffit de faire une étude pour choisir celui qui conviendra le mieux (fonctionnalités offertes, type de test, profil utilisateur). Si nous devons développer ou faire développer notre propre simulateur, tout se complique, il faudra :

1. Identifier correctement nos besoins en terme de test ;

2. Évaluer la charge et bien planifier ;

3. Tester l'outil ;

4. Prévoir à la première utilisation la possibilité d'anomalie du côté de l'outil.

Quels sont les risques ?

1. Outil non livré en temps et en heure ce qui va retarder la phase de test ;

2. Impossibilité de tester certains scénarios (en particulier les cas d'erreur), car l'outil n'a pas été correctement spécifié ;

3. Outil non testé qui va rendre le debug très pénible et entraîner des confits entre développeurs et utilisateur de l'outil ;

4. Un bon outil va donc faciliter la phase de test, il faudra donc veiller à sa qualité.

Dans le chapitre automatisation j'aborderai la question des outils plus en détail.

III - Conception et codage

Mais le plus efficace est d'intégrer la testabilité lors de la conception. Deux aspects sont à prendre en compte :

1. La contrôlabilité : la possibilité de contrôler l'état d'un composant en vue de les tester ;

2. L'observabilité : la possibilité d'observer les résultats de test (intermédiaire et final).

III-A - Contrôlabilité

Roy Osherove, dans son article « Achieving And Recognizing Testable Software Designs », propose les règles suivantes pour atteindre cet objectif et améliorer la testabilité :

1. Des exécutions partielles : pour déboguer une fonctionnalité, nous pouvons exécuter de façon partielle une partie des tests unitaires ;

2. Des résultats identiques à chaque exécution : les résultats ne dépendent pas de l'état du système ;

3. Pas de configuration : aucune configuration du système n'est nécessaire pour tourner les tests ;

4. L'ordre des tests n'est pas important : nous pouvons exécuter les tests dans l'ordre que nous voulons ;

5. Temps d'exécution rapide : la masse de tests unitaires est importante, pour accélérer l'exécution, nous évitons par exemple de nous connecter aux bases de données.

Pour respecter ces règles, il faudra mettre en place des règles de codage qui visent à faciliter le test.
Si nous atteignons ces objectifs, nous gagnons en « contrôlabilité ». Le résultat est la possibilité de tester complètement et simplement le code par morceau. Un autre point qui améliore la testabilité est de limiter la complexité du code. Il est mesurable par la complexité cyclomatique (lié au nombre d'imbrications). Plus cette mesure est élevée plus le code est source d'erreur. De plus, il augmente le nombre de jeux d'essais qui permettent le test. D'autre part nous allons limiter le nombre de jeux d'essais et augmenter la contrôlabilité. Là encore, ce sont les règles de codage qui vont améliorer la testabilité.

III-B - Observabilité

Voilà nos tests unitaires et d'intégration ont été réalisés et ont permis de déboguer le code, reste à vérifier que notre logiciel a bien le niveau de qualité en termes de fonctionnalités attendues, mais aussi en termes de rendu de service (stabilité, sécurité …). Il nous faut passer aux tests système. Là encore, un certain nombre d'éléments doivent être prévus pour nous aider dans nos tests :

1. Politique de gestion d'erreur : date, type d'erreur sont des informations qui permettront de déboguer et trouver rapidement la cause en cas d'erreur détectée par les tests ;

2. Politique de log : différents niveaux que l'on peut activer ou désactiver à chaud. Si ces logs doivent être utilisés par le support, il faudra les rendre compréhensibles et accessibles à tous. Ils facilitent le debug et permettent d'analyser les causes d'erreurs ;

3. Connaissance des états internes du système ou des objets : donner les moyens de connaître les états du système, voire de les gérer, vont permettre de mieux contrôler et observer l'évolution du système lors des tests et ainsi de vérifier la consistance du système. Cela peut être fait par l'intermédiaire d'une base de données ou d'API.

Certains outils tels que les émulateurs ou les débogueurs peuvent être plus ou moins performants en termes de fonctionnalités et vont eux aussi permettre d'améliorer l'observabilité. Dans le développement d'un logiciel embarqué, l'absence d'émulateur peut ralentir considérablement la sortie d'un produit, car le debug se fera à tâtons par l'émission d'hypothèses.

III-C - Dès la conception

La testabilité doit être prise en compte dès la conception du logiciel, car l'architecture peut améliorer grandement ce critère. Il est d'ailleurs très difficile de l'améliorer a posteriori :

1. Tenter de rajouter des tests unitaires sur un logiciel où cet aspect a été négligé peut se révéler une tâche ardue. Car il est peu probable qu'il ait été conçu de façon modulaire ;

2. De même, mettre à niveau des logs ou de la gestion d'erreur peut s'avérer être une tâche coûteuse, mais éventuellement à mettre en balance avec le coût du debug, support et maintenance ainsi que l'image vis-à-vis du client.

IV - Plan de test

Pour écrire un plan de test complet, nous aurons besoin des points suivants :

1. Les spécifications sont connues et leurs évolutions maîtrisées ;

2. Elles sont complètes et abordent tous les aspects (fonctionnels, opérationnels, performance, sécurité, etc.).

Comment écrire un plan de test si nous ne savons pas quoi vérifier ? Sans exigences définies, comment faire une couverture et être sûr de la complétude de nos tests ?
Néanmoins, si elles sont absentes une technique de test peut pallier ce manque : les tests dits exploratoires : il s'agit d'écrire et d'exécuter les tests en découvrant le logiciel. Ces sessions seront conduites avec des objectifs qualité définis (comme l'usage d'une fonctionnalité, la sécurité…).
Cette technique peut être appliquée si :

1. Le produit est connu (existe par ailleurs, les testeurs connaissent le métier) ;

2. Les testeurs ne sont pas des débutants ;

3. Les conséquences des erreurs sont minimes (données sensibles, contrat, sécurité des personnes).

Néanmoins, il faut rester conscient que certains points peuvent échapper au testeur, car il n'a pas la maîtrise des spécifications de façon complète et sûre. De plus, tout est sérialisé, ce qui augmente les délais de livraison.

V - Exécution des tests

V-A - Tests Unitaires

Les tests unitaires sont essentiels pour améliorer la testabilité :

1. Ils vont faciliter le déroulement des tests système : les anomalies liées à des erreurs de codage sont plus facilement détectées et corrigées à ce niveau ;

2. Lors d'évolution ou de correction, ils vont permettre de localiser les erreurs de façon fine et permettre de corriger des anomalies ou d'améliorer le code sans crainte de non-régression.

V-B - Tests d'intégration

Il est absolument nécessaire de planifier ces tests avec tous les intervenants avant de commencer les tests système. Si cette phase n'est pas réalisée, au lieu de tester les aspects fonctionnels les testeurs feront face à un système qui ne fonctionne pas. Ils devront faire appel à tous les intervenants pour déboguer le système petit à petit. Ils prendront du retard et des conflits vont apparaître.
Une phase d'intégration planifiée et menée à terme facilite les tests système.

V-C - Automatisation

Tester prend du temps. Quand le nombre de tests devient trop important, les versions à tester se multiplient l'automatisation est la solution qui s'impose. Sinon faute de temps nous allons procéder à des coupes dans les tests pour livrer en temps et en heure et prendre des risques sur la qualité du livrable. L'automatisation est donc nécessaire, mais encore faut-il si prendre à l'avance. Le schéma suivant résume l'outillage nécessaire et nos choix vont augmenter ou non la testabilité de l'application de par les possibilités de nos outils : Injecteur : l'outillage qui va permettre d'appeler les interfaces externes de l'application par exemple des scripts de test écrit pour des API (java, C++, Soap) ou des tests automatiques d'interfaces (QTP, WINRUNNER, Selenium …).
Simulateur : outil qui simule le monde extérieur. Si l'application communique avec d'autres applications, il faut simuler la communication entre ces applications. Si le monde extérieur se réduit aux utilisateurs finals, il n'y a pas besoin de simulateur on utilise le même outil que l'injecteur soit des outils d'automatisation de test d'interfaces.
Vérification : toutes les vérifications qui vont déterminer si le test est réussi ou non.
Outil de gestion d'exécution des tests : le maître d'œuvre qui va séquencer les autres outils et centraliser les résultats de test.

L'injecteur est la partie la moins complexe, sauf pour les tests de performance. Pour ces tests, il faudra également prévoir le hardware nécessaire. L'autre difficulté est d'être capable de simuler une utilisation réaliste de l'application.
La partie vérification : pour automatiser, il faut pouvoir accéder à certaines données qui sont les sorties, mais parfois à des données internes en base ou à l'état d'un service en cours. Peut-être faudra-t-il faire développer des API internes ?
Les résultats devront être déterministes. Lors d'un de mes projets, nous avions réalisé un système d'automatisation de la façon suivante : le test était exécuté manuellement une première fois ; le résultat après vérification était enregistré comme référence ; le test automatisé vérifiait la sortie par rapport à la référence. Malheureusement il s'agissait d'une liste de contacts et à la version suivante l'ordre des contacts n'était plus déterministe. Impossible de repasser les tests automatiques même en changeant les résultats. Nous avons demandé une relivraison, car c'était plus facile côté développement.
Autre exemple : pour utiliser Sélénium (outil d'automatisation d'interfaces web) les objets graphiques doivent être identifiés par un identifiant unique, qui doit être positionné par le développeur d'application web.
Côté simulateur il faut qu'il réponde aux besoins de test c'est-à-dire être capable de simuler tous les cas de figure, cas normaux et cas d'erreur. La meilleure solution est de scripter le comportement du monde extérieur.

Ici la conception du simulateur est très simple, il consiste essentiellement à envoyer et recevoir des messages décrits dans les scripts, gérer des expirations de délai, vérifier que le message reçu est conforme à celui du script et fournir un compte rendu d'exécution du script. Aucune interprétation du message n'est demandée dans ce cas.
La plus mauvaise solution serait de simuler avec du code le comportement du monde extérieur. C'est-à-dire que le simulateur va interpréter les messages qu'il reçoit et répondre en fonction. Dans ce cas le développement du simulateur va être aussi complexe que celui de l'application et poser les problèmes décrits dans le chapitre II-B. Il y a également de fortes chances que l'on ne pourra pas tester de façon exhaustive les fonctionnements inattendus ou les expirations de délais.
Le choix des outils de manière générale est crucial et peut réduire considérablement le nombre de cas de tests ou les rendre difficilement réalisables, je pense en particulier aux cas d'erreur.
De même travailler en amont en intégrant lors de la conception des exigences relatives aux tests, est nécessaire (par exemple des API internes qui permettraient de vérifier des états de transaction) et facilite les activités de test. Cela est aussi vrai pour les activités de maintenance.

VI - Planification

La livraison des fonctionnalités dans le temps peut également jouer. Par exemple dans le cadre d'un développement itératif, les fonctionnalités de création d'abonnés ont été livrées, mais pas celles de suppression. Après un test de création pour détruire les données, il faut à chaque fois arrêter le serveur, détruire en base et redémarrer le serveur tout en se synchronisant avec les personnes utilisant ce même serveur. Compliqué, il eût été plus simple de livrer directement les fonctions de suppression.
De même, planifier la livraison des corrections d'anomalies peut faciliter l'exécution des tests surtout en cas d'anomalies bloquantes.

VII - Conclusion

La testabilité se joue dès les débuts de la réalisation de l'application à toutes les étapes (définition de la stratégie de test, spécifications, conception…). Le test et le développement doivent donc travailler ensemble avec comme objectif rendre l'application testable. Les méthodes agiles facilitent la gestion de cette problématique (écriture des tests avant le codage, participation des utilisateurs finals, équipe projet mixte, etc.), mais je ne saurais en dire plus n'étant pas du tout une spécialiste de ces méthodes.
Tous ces prérequis sont nécessaires pour tester dans les meilleures conditions :

1. Des objectifs, des risques identifiés orientent nos tests ;

2. Sans spécifications, pas de références pour nos oracles de test ;

3. Sans réel moyen de test l'efficacité des tests est diminuée ou bien les tests sont fastidieux à faire, etc. ;

4. Un logiciel testable va de pair avec une bonne conception basée sur les principes de modularité, etc. ;

5. Respecter les différentes phases de test (unitaires, intégration et système) ;

6. Les outils de tests livrés à temps et correctement testés ;

7. Des logs et une gestion d'erreur facilitent le debug ;

8. Planification des livraisons en prenant en compte les besoins des tests.

Évaluer la testabilité et mettre en amont les moyens pour l'améliorer sont donc nécessaires à la bonne exécution de l'activité de test. Cela a un coût, nécessite de la rigueur et du travail en amont, mais accélère les phases de test.

VIII - Références, liens

Achieving And Recognizing Testable Software Designs Part I
Roy Osherove
le forum Tests
les articles et tutoriels sur les tests

IX - Remerciements

Tous mes remerciements à Wachter pour sa relecture et ses corrections orthographiques. Tous mes remerciements à Ricky81 pour sa relecture et ses conseils et commentaires.