L'automatisation des tests est le processus d'utilisation d'outils et de logiciels pour exécuter automatiquement des tests sur des applications, des systèmes ou des logiciels.
Cela permet de réduire la charge de travail manuel et de gagner du temps lors de l'exécution de tests répétitifs ou fréquents.
Afin d’être efficiente, l’automatisation des tests doit suivre un processus clairement défini et être pilotée par des indicateurs précis pour savoir si la démarche apporte bien le gain escompté.
Dans cet article un peu particulier, nous allons réaliser une démarche complète d’automatisation sur une petite application très simple.
Qu'est ce qu'une automatisation des tests ?
Les tests automatisés sont une forme de test logiciel.
Ils consistent à utiliser des outils et des logiciels pour exécuter des cas de test et vérifier automatiquement les résultats par rapport aux attentes prédéfinies.
Ceci dans le but de mettre en place une stratégie de test efficace.
L'automatisation des tests suit un processus comprenant plusieurs étapes d'évaluation constante, détaillées comme suit :
- Evaluation risques et des gains inhérents au processus d’automatisation des tests
- Evaluation de l’effort pour réaliser ce gain ou supprimer ce risque en déterminant les ressources, le temps, les coûts …
- Décision de réaliser ou non l’automatisation
- Evaluation des gains effectivement réalisés ou les risques qui demeurent malgré l'automatisation (risques résiduels)
- Evaluation du ROI du cycle d’automatisation en comparant les gains obtenus à l'investissement initial
Dans le cadre de la gestion en mode projet, les cycles d'automatisation peuvent être plus ou moins longs en fonction du type de projet, du mode de fonctionnement de l’équipe, du testeur, et du niveau de maturité de l’organisation.
Etude de cas : la célèbre calculatrice Windows
Si vous souhaitez découvrir comment le processus de l'automatisation des tests s'applique en pratique, vous êtes alors au bon endroit.
1) Présentation de l'application à tester
Pour ce cas pratique, nous allons utiliser la très célèbre calculatrice intégrée à Windows.
Nous allons dans cet article chercher à valider le fonctionnement de la fonctionnalité «addition», identifiée par les histoires utilisateur ou user stories.
2) Rédaction des user stories
Ci-après les user stories identifiés :
2.1) User story 1
- Etant donné que l'écran affiche 0, quand je tape 2 l'écran affiche 2
- Etant donné que l'écran affiche 0, quand je tape + l'écran affiche 0+et 0
- Etant donné que l'écran affiche 0, quand je tape + puis * l'écran affiche 0* et 0
2.2) User story 2
- Etant donné que l’écran affiche 0, quand je tape 2+2= alors l’écran affiche 4
- Etant donné que l’écran affiche 4, quand je tape +2= alors l’écran affiche 6
- Etant donné que l’écran affiche 0, quand je tape +2= alors l’écran affiche 2
2.3) User story 3
- Etant donné que l'écran affiche 0 sans historique, quand je tape 50 fois "10+" alors l'écran affiche "500"
- Etant donné que l'écran affiche 0 sans historique, quand je tape 50 fois "10x" alors l'écran affiche "9,765625e+16"
3) Les types de test
Dans ces scénarios nous avons 3 types de tests possibles :
3.1) Tests unitaires
Les scénarios "unitaires" (user story 1) seront les briques de base des scénarios complets, seront exécutées au plus tôt.
3.2) Tests de bout en bout
Les scénarios de "bout en bout" (user story 2) utiliseront les briques de base sous forme d’enchaînements pour réaliser des scénarios complets mais toujours simples.
3.3) Tests de performance
Les scénarios "long" (user story 3) constitueront nos tests de performance.
Ils vont mélanger et chaîner des tests de "bout en bout", pour réaliser des scénarios complexes très proches de la réalité.
Ceci, afin de vérifier que dans une configuration matérielle donnée, l’application atteint le niveau de performance nécessaire.
Ces 3 étages de la pyramide de tests devraient, dans l’idéal, être construits dans cet ordre :
- Tests unitaires
- Tests de bout en bout
- Tests de performance
Ceci, afin de pouvoir réutiliser chaque étage inférieur pour construire celui du dessus, et ce, dans le but de ne pas avoir à répéter ou modifier de code.
C’est ce que nous présenterons dans cet article. Toutefois, cela n’est pas toujours le cas dans des projets très complexes où les tests de "bout en bout" et unitaires peuvent être créés en parallèle par différents acteurs (développeurs, QA, …).
4) Etapes de mise en place des tests automatisés
Voici les 3 étapes à suivre dans notre cas pratique :
Etape 1 - Création des tests « unitaires »
Les tests unitaires nous serviront de brique de base pour la mise en place des autres types de tests. Ce sont donc nos « fonctions » qui seront appelées plus tard.
1.1 Le fichier de scénarios
Ce fichier de plus haut niveau reprend la syntaxe des tests définis auparavant :
Resource Calc_keywords.resource
*** Test Cases ***
Test Calculator With BDD Syntax
Given The Calculator Is Running
When The User Enters The Term "2"
Then The Result Should Be "2"
Ce fichier contient 2 parties :
- Il appelle une « ressource », c’est-à-dire un autre fichier contenant les actions (ou mots clés) que nous allons utiliser
- Il définit un scénario au format Gerkin dont chaque ligne correspond à une fonction du fichier de la ressource.
Et le fichier ressource ?
Ce fichier contient les actions (ou mots clés) que nous allons utiliser :
Library Calculator.py
*** Keywords ***
The Calculator Is Running
Log Opening Calculator
Start Calculator
The User Enters The Term "${term}"
Log Entering ${term}
Set Test Variable ${term}
The User Triggers The Calculation
Log Triggering Calculation
${result} Calculate Term ${term}
Set Test Variable ${result}
The Result Should Be "${expected}"
Log Checking Result
Should Be Equal As Numbers ${result} ${expected}
On remarque que lui aussi appelle une ressource. Il implémente chacune des phrases de notre scénario en utilisant des mots clés (log, set test variable, should be equal as, …). Ceci, afin de réaliser des actions et des assertions.
Ces mots clés correspondent à du code de plus bas niveau livré avec le robot (mais nous ne nous y attarderons pas dans cet article).
La ressource appelée correspond, elle, à une action non standard :
print( "Calculator Started")
def calculate_term (term):
"""
Interpret term as a mathematical expression
and return the result
"""
print( "Calculating Term:" , term)
return eval(term)
Ces 2 actions nous permettent de « tricher » car elles renvoient des résultats même en l’absence de calculatrice lancée.
Dans la terminologie des tests, nous appelons cela un « fake » (faux), notion que nous évoquerons dans un futur article.
Cette astuce nous permet de vérifier que le robot fonctionne avant de vérifier que l’application ET le robot fonctionnent bien ensemble.
1.2 Mise en place des scénarios
A vous de jouer !
A notre tour de mettre en place nos scénarios. Vous pouvez pour cela utiliser la console en ligne ici.
Nous allons commencer par coder tous nos scénarios unitaires (user story1), soit :
Resource Calc_keywords.resource
*** Test Cases ***
As a user, when I enter 2, the display shows 2
Given The Calculator Is Running
When The User Enters The Term "2"
And The User Triggers The Calculation
Then The Result Should Be "2"
Il est possible de lancer les tests à tout moment en utilisant le bouton « Run ». Nous obtenons le résultat escompté :
==============================================================================
Robot Files.Calculator Test Suite
==============================================================================
As a user, when I enter 2, the display shows 2 |
PASS |
------------------------------------------------------------------------------
Robot Files.Calculator Test Suite |
PASS |
1 test, 1 passed, 0 failed
==============================================================================
Robot Files |
PASS |
1 test, 1 passed, 0 failed
Nous pouvons ensuite ajouter le test suivant :
Given The Calculator Is Running
When The User Enters The Term "+0"
And The User Triggers The Calculation
Then The Result Should Be "0"
Le test passe, nous avons donc 2 tests « passants ». Le dernier test va nous poser un petit problème :
Given The Calculator Is Running
When The User Enters The Term "+*"
And The User Triggers The Calculation
Then The Result Should Be "0"
Si je tape « +* » lorsque la calculatrice affichait « 0 », alors le résultat devrait être 0 car le programme « sait » qu’il faut remplacer + par *, puis ajouter un 0 si rien n’est précisé en second terme.
Cela fonctionne sur la calculatrice réelle, mais pas dans notre « mock » que nous allons devoir modifier un peu. Le code modifié est un peu complexe, mais respecte les étapes suivantes :
- Lire l’entrée
- Vérifier qu’il n’y a qu’un seul signe +, -, * ou / entre deux chiffres
- S’il y en a deux, ne conserver que le dernier
- Si l’entrée ne se termine pas par un chiffre, ajouter un 0
En utilisant cette modification, nous obtenons des tests passants :
PASS |
3 test, 3 passed, 0 failed
==============================================================================
Robot Files |
PASS |
3 test, 3 passed, 0 failed
Nos tests « unitaires » sont maintenant implémentés, ils couvrent les comportements les plus simples de notre calculatrice.
Nous allons donc pouvoir implémenter des tests de "bout en bout" pour vérifier des saisies réelles.
Etape 2 – Implémentation des tests de bout en bout
Ces tests reprennent des étapes unitaires afin de construire des scénarios proches de ceux réalisés par un utilisateur réel :
Given The Calculator Is Running
When The User Enters The Term "2 + 2"
And The User Triggers The Calculation
Then The Result Should Be "4"
On peut continuer sur un cas dérivé :
Given The Calculator Is Running
When The User Enters The Term "1 + 1"
And The User Triggers The Calculation
Then The Result Should Be "2"
Et prendre un cas aux limites :
Given The Calculator Is Running
And The display shows « 0 »
When The User Enters The Term "+2"
And The User Triggers The Calculation
Then The Result Should Be "2"
En lançant nos tests, nous obtenons le résultat escompté :
Robot Files.Calculator Test Suite
==============================================================================
Test 2+2 gives 4 |
PASS |
------------------------------------------------------------------------------
Test 2+2+2 gives 6 |
PASS |
------------------------------------------------------------------------------
Test +2 gives 2 |
PASS |
------------------------------------------------------------------------------
Robot Files.Calculator Test Suite |
PASS |
3 tests, 3 passed, 0 failed
==============================================================================
Robot Files |
PASS |
3 tests, 3 passed, 0 failed
Nous avons donc implémenté les tests unitaires, ainsi que des tests de "bout en bout". Il est temps de tester la capacité de notre application à répondre à des usages d’intensité normale.
Etape 3 - Implémentation des tests de performance
Les tests de performance, parfois associés aux tests de charge, ont pour but de vérifier que dans ces cas d’utilisation normale ou intensive de l’application, celle-ci donne bien les résultats attendus dans des délais acceptables.
Dans notre mini-plan de tests, ce sont les 2 cartes du user story 3.
Essayons avec une boucle :
Resource Calc_keywords.resource
*** Test Cases ***
Test adding +1 +2 +3 +4 +5 +6 +7 +8 +9 to +500
Given The Calculator Is Running
FOR ${num} IN RANGE 1 500
When The User Enters The Term "+ ${num}"
And The User Triggers The Calculation
Then The Result Should Be ${num}
END
Ce test répète 500 fois la même action très rapidement, et vérifie donc que le programme est capable de l’effectuer dans un temps raisonnable, et ceci sans "manquer " une des itérations.
A partir de ce scénario, il va nous être possible d'exécuter des tests en parallèle en lançant plusieurs exécutions simultanées, mais aussi de demander des opérations très complexes comme des divisions, des calculs de logarithmes ou autres. En plus, de vérifier que l’application respecte toujours les critères de performance et de fiabilité.
Quel est l'avantage de ce procédé ?
Procéder de cette manière a plusieurs avantages, parmi lesquels :
- Identifier les scénarios et les configurations précis : Permet au processus de développement d’établir un contrat sans ambiguïté avec son client, lui permettant de délivrer des itérations indépendantes rapidement et de converger vers une solution en détectant un écart très tôt
- Disposer de tests dès le début du développement : Permet de mettre en place des approches "test driven" dans lesquelles le développeur va utiliser des tests pour progresser par itérations très rapides vers une solution validant uniquement les scénarios demandés. Ceci à pour conséquence, une meilleure organisation de travail, une gestion de temps efficace, une optimisation de code et une limitation des pertes avec moins de réunions
Conclusion
A travers cet article, vous aviez eu un aperçu rapide de l’implémentation pratique de différents types de tests sur une application relativement connue et « simple », tout en mettant en avant le lien avec la stratégie et le plan de tests. Il vous a montré aussi l’importance de garder en tête la lisibilité du code et le respect des exigences du client.
L’automatisation des tests consiste à faire exécuter des opérations simples ou complexes des tests par un robot. Ceci, pour obtenir un retour plus rapide et gagner du temps tout en évitant les erreurs de répétition.
Ce processus doit découler d’une stratégie de tests, afin que les tests automatisés permettent d’atteindre les objectifs fixés par la politique qualité (maintenabilité, fiabilité, performance, …).