Après avoir défini les niveaux de test et en particulier les tests système, il me parait important de mentionner quelques pièges très courants, dans lesquels les testeurs débutants peuvent facilement tomber. Ces pièges peuvent avoir différents effets, de la perte de qualité dans le produit à la baisse de rentabilité des investissements en test. Dans tous les cas, ces pratiques peuvent rapidement se montrer contre-productives pour les entreprises. 

Traps to avoid with system tests

1. Entrer dans la « boîte noire » 

Il peut parfois être tentant de vouloir aller vérifier dans le système qu’un élément a bien été inséré par exemple, mais attention ! Cette « intrusion » dans la boîte noire vient créer une dépendance avec l’implémentation du système et expose à une maintenance plus importante des tests au fil des évolutions de ce dernier. A n’utiliser donc qu’en cas de nécessité absolue et en pesant le pour et le contre ! 

2. Dépendre du bon fonctionnement du système lui-même pour exécuter les tests 

On rencontre ce travers dans les cas où l’on veut s’affranchir de la complexité parfois importante de la mise en place du contexte de test (préconditions d’exécution, données de test…). On peut alors choisir d’utiliser le système lui-même pour se mettre en condition avant de lancer notre test. On prend le risque de faire l’impasse sur un des objectifs du test qui est de fournir de l’information sur le bon fonctionnement de tout le système. 

Prenons un exemple :  

On souhaite valider une fonctionnalité de suppression d’un élément dans un système. Pour assurer la rejouabilité du test, nous devons disposer à chaque lancement d’une donnée à supprimer. Plusieurs options s’offrent alors à nous, parmi lesquelles : 

  • Jouer un script avant de commencer le test pour insérer la donnée (la meilleure option dans bien des cas) 
  • Réinsérer toutes les données de test à chaque déploiement du système et donc redéployer le système avant chaque lancement des tests 
  • Disposer de « suffisamment » de données pour jouer un certain nombre de fois le test (cette option n’est clairement pas la meilleure car elle ne fait que repousser le problème !) 
  • Insérer la donnée en utilisant la fonctionnalité d’ajout de données du système : c’est cette option qui cache un piège important ! 

Le risque est de perdre la visibilité sur l’état de toutes les fonctionnalités du système si l’insertion de données ne fonctionne pas.  

On ne peut en effet plus tester les récupérations, modifications et suppressions de données si tous les scénarios de test commencent par l’utilisation du système pour insérer la donnée de test et que ceci est impossible. 

On retrouve ce même phénomène si, pour « gagner du temps » sur la mise en place des contextes de test, on construit un « super » scenario dans lequel on enchaine astucieusement toutes les fonctionnalités à tester pour couvrir l’ensemble : on ne pourra constater que le premier souci rencontré et on sera aveugle sur d’autres soucis potentiels détectables plus tard dans le scénario de test. Nous nous exposons alors à ce que j’appellerais les bugs « à tiroirs » : on fixe un bug, on découvre le suivant, on le corrige, on en trouve un autre etc…. et on ne sait absolument pas dire combien de fonctionnalités du système sont altérées à un instant T. 

Ceci rend les prises de décisions et les planifications très compliquées car l’effort de correction ne peut être estimé. Cette dépendance sur le bon fonctionnement de certaines fonctionnalités du système pour en valider d’autres est inévitable mais elle est à manier avec précaution et il est important de ne jamais perdre l’objectif des tests, qui est de fournir en continu de l’information sur chaque règle fonctionnelle du système. 

3. Reproduire le comportement du système dans les tests 

On peut être tenté de reproduire un algorithme de calcul par exemple pour valider la pertinence du résultat renvoyé par le système. Mais cette pratique est très risquée et rien ne permet de dire si le résultat correct est celui calculé par le système en test ou celui calculé par l’outil de test. Un exemple courant est celui de la génération de token d’authentification : il n’est pas bon de tenter de calculer un token valide dans l’outil de test pour valider celui renvoyé par le système. Le meilleur moyen reste encore d’utiliser le token reçu pour vérifier sa validité (tout en veillant à ne pas tomber dans le travers précédent !). 

Attention à ne pas réimplémenter la complexité du système qu’on cherche à tester dans l’outil de test lui-même : l’outil de test doit rester « simple » observateur du système qu’il valide et c’est là toute la difficulté ! 

4. Attendre « un certain temps » 

Quand certains traitements du système prennent « du temps » ou sont asynchrones, on peut avoir envie d’attendre un « certain temps » avant de vérifier certaines informations. Mais combien de temps ? 

  • Si on attend trop peu, le test tombe en erreur alors que la fonctionnalité fonctionne correctement. 
  • Si on attend trop longtemps, le temps d’exécution des tests est dégradé alors que le système a terminé son traitement et cela peut avoir un impact très lourd quand nous avons des centaines voire des milliers de tests.  

On se retrouve souvent à devoir petit à petit augmenter ce temps d’attente pour éviter les faux positifs et on perd ainsi énormément de temps pour jouer les tests. C’est dans cette situation qu’on peut s’intéresser aux sorties secondaires du système : on peut vérifier l’apparition d’une entrée de log ou l’émission d’une notification particulière pour déclencher la suite du test. C’est alors le système lui-même qui nous indique quand continuer. 

Il faut tout de même décider d’un ‘timeout’ raisonnable pour conclure que quelque chose s’est mal passé mais en cas de succès du test, nous aurons limité au maximum le temps passé à attendre. 

5. Utiliser des valeurs de référence dynamiques 

Pour mener les tests, nous définissons des « données attendues » qui serviront à valider les retours du système. Nous devons toujours faire attention à ne pas trop dynamiser ces données : on peut toujours dynamiser une donnée liée à la date du jour ou pour pallier des contraintes d’unicité de certaines informations (guid générés par ex.) dans les sorties qu’on souhaite vérifier mais il faut faire attention à disposer tout de même de données fixes et maîtrisées, prises comme valeurs de référence. Sans cela, le risque est de bien valider le format ou la structure des données mais pas leur véracité, c’est-à-dire leur pertinence, compte tenu du jeu de données de test.  

6. Mettre à jour en masse les données de référence 

C’est souvent ce qu’on peut faire quand il y a des changements importants dans les sorties du système ou quand on souhaite établir un premier jeu de données de références sur un système existant. Il faut se méfier de cela car un ou plusieurs bugs peuvent se cacher derrière les retours actuels. En effet, si tout semble lié à la modification en cours dans le système, une ou plusieurs régressions peuvent tout de même se cacher dans les retours nouvellement obtenus. Les données de référence vont servir longtemps à sécuriser les livraisons du produit et y embarquer un bug peut avoir un effet très large chez les clients sans qu’on ne s’en rende compte.  

Les données de référence doivent faire l’objet d’une validation manuelle et au cas par cas pour garantir le meilleur bénéfice des tests système.  

7. Implémenter un comportement spécifique pour les tests dans le système  

C’est le fameux « If test » dans le code de production ! On déclenche un comportement particulier si on reçoit un certain header ou toute autre information permettant d’identifier qu’il s’agit d’un test en cours. Ceci permet de valider le comportement du système, quand on est dans le cadre des tests mais pas son comportement réel en production. J’avoue qu’il m’est arrivé de devoir en faire usage dans des cas très précis où cela s’est avéré être indispensable mais cette pratique est à manier avec grande précaution. 

8. Établir un rapport de test global 

Suivant l’outil de test utilisé, les différents cas de test peuvent être exécutés et les rapports générés de différentes manières. Il est indispensable de disposer en sortie des tests d’un rapport sur l’état de chaque fonctionnalité du système. On doit éviter d’avoir un seul indicateur donnant le statut global (qui serait du coup très souvent ‘rouge’) avec nécessité de se rendre dans la console ou dans un fichier pour pouvoir identifier le ou les soucis rencontrés. 

9. Ne jouer les tests qu’après une modification du système 

Même s’il est important de jouer les tests système après chaque déploiement faisant suite à une modification du système, cela n’est pas suffisant. Il est en effet important de s’assurer régulièrement que le système reste fonctionnel dans son environnement. L’environnement peut en effet évoluer et un système qui n’est pas souvent modifié peut être altéré par des causes extérieures. Il est donc conseillé de déclencher les tests existants sur une base régulière, même en l’absence de modifications du système. Sinon, le risque est de trouver un certain nombre de surprises lors de la prochaine modification à apporter au système. 

10. Tester uniquement les cas passants 

S’il est essentiel de valider le bon fonctionnement des cas passants d’un système, les cas d’erreur sont tout aussi importants, voire même parfois plus importants si on s’intéresse aux caractéristiques non fonctionnelles de la qualité du système, comme l’utilisabilité ou la sécurité. Informer correctement l’utilisateur en cas de mauvaise utilisation du système permettra d’améliorer la qualité de son expérience, par exemple. 

Simplement quelques mots pour conclure : vous avez sûrement déjà rencontré un ou plusieurs de ces pièges et il s’agit toujours de prendre une décision adaptée à chaque situation. L’important dans l’évaluation du risque ou la prise de décision est de connaître aussi bien que possible l’impact que cela peut avoir à long terme. 

Dans le développement logiciel, la gestion de la qualité réside en grande partie dans la pertinence des investissements à long terme. Si vous devez parfois prendre des décisions qui vont augmenter la dette technique dans votre produit, ce n’est pas un souci du moment que vous n’oubliez pas de gérer cela correctement le plus tôt possible. Mais ceci pourra faire l’objet d’un nouvel article… 

Crédit photo: Michael Podger

Author

Fort de plus de 10 ans d’expérience en stratégie et implémentation de tests logiciels en environnement Agile, Alexandre est en charge de l’industrialisation des développements chez AT Internet. Son challenge au quotidien : accompagner nos équipes de développement dans la mise en place d’outils et méthodes afin de garantir à nos clients des livraisons régulières et de qualité.

Comments are closed.