Dans cet article je vous propose donc de nous y attarder afin de regarder son intérêt, ses cas d’utilisations classiques, son principe de fonctionnement, et ce que l’on peut en faire (et vous verrez on peut en faire beaucoup de choses)
Robot Framework est un orchestrateur de tests automatiques [1]. Son principe de base est assez classique car il comporte, comme beaucoup d’outils de test, de SuiteCases composés de TestCases. La différence majeur se trouve par contre dans une approche orientée données proposant la possibilité de définir des DSL (Domain Specific Language) pouvant être à la fois technique comme fonctionnel (on verra qu’il est préférable justement de séparer ces deux aspects)
RobotFramework est un framework Open source (License apache 2) hebergé sur Github [6] développé en Python [2] (nous reviendrons sur ce langage mais cela a assez peu d’impact ici). Il est interopérable avec Java où .net au travers des frameworks Jython [3] ou IronPython [4]. Il se compose d’un ensemble de librairies standard facilitant la mise en oeuvre des SuiteCases et des TestCases et bénéficie d’un système de chargement de librairies dites Remote [5] au travers d’un serveur permettant l'exposition des fonctionnalités de ces librairies mais de façon distante. Cette fonction qui peut paraître un peu complexe fournit deux avantages majeur:
- pouvoir exécuter des tests sur des composants distants
- permettre l’adjonction de librairies développées dans des languages autres que python (nous ne traiterons pas ce point dans cet article mais nous y viendrons)
Toutes ces capacités font du coup de robotframework un outil à la fois simple, efficace de part sa modularité et capable de couvrir les différents types de tests que nous avions déjà évoqué dans l’article [18], mais rappelons le. RobotFramework permet de couvrir des tests
- Fonctionnel avec Selenium/Sikuli [19-20]
- Intégration en facilitant l’installation/désinstallation de composant et la constitution de bouchons
- Recette en fournissant un rapport d'exécution des tests livrable au client
- Non Régression reproductible de façon journalière à la mode intégration continue
- Couverture de code si l’outillage adéquat est réalisé (avec cobertura [15] par exemple)
- Performance avec Jmeter [16,17] et la librairie associée.
Pour pouvoir rentrer dans le vif du sujet, il importe de faire l’installation de robotframework. Les deux versions majeures de python sont utilisables pour utiliser robotframework cependant pour faire un choix, il importe d'étudier préalablement la compatibilité des librairies qui conviendra d’utiliser pour réaliser notre stratégie de test car elle ne le sont pas toujours entre elles.
Ainsi, dans le cadre de cet article et en coherence avec l’exemple que je présenterai plus loin dans l’article, nous utiliserons Python 2.7 [2] et vous invite donc à le télécharger et l’installer. De façon à travailler proprement (mais ce point n’est pas obligatoire), je vous invite à récupérer virtualenv [21] pour vous créer un environnement dédié à robotframework.
$ pip install virtualenv $ virtualenv rf-env
Ensuite, avec dans votre environnement vous installez les paquets robotframework (ici les plus communs mais la liste n’est pas exhaustive):
$ rf_pip install robotframework \ robotframework-databaselibrary \ robotframework-seleniumlibrary \ robotframework-sudslibrary \
Ensuite pour certaines librairies, il nous faut des addons. Ainsi il faut pour effectuer des tests sur des IHM Web avec Selenium[9] , des drivers spécifiques pour chaque type de navigateur. Nous utiliserons chrome, donc il faut télécharger le driver web chromedriver [11] et le mettre dans un répertoire que l’on ajoutera au PATH système:
export PATH=$PATH:D:\Tools\rf-drivers
Ensuite il est nécessaire de préparer les librairies Remote [5]. Dans ce cadre nous n'utilisons que la librairie JMS. Ainsi, il faut d’une part télécharger jmslibrary [7] mais aussi le serveur de librairies: jrobotremoteserver [12]. De la même manière que pour le driver web, on ajoute la librairie et le serveur au CLASSPATH :
export CLASSPATH=D:\Tools\rf-drivers\robotframework-jmslibrary-1.0.0.jar;\\ D:\Tools\rf-drivers\jrobotremoteserver-3.1-SNAPSHOT-jar-with-dependencies.jar;$CLASSPATH
Puis pour l’utiliser (nous verrons plus loin quand ):
java org.robotframework.remoteserver.RemoteServer -l JMSLibrary:/jmslibrary -p 8270
Maintenant que l’installation est faites, intéressons nous à la composition d’un projet RobotFramework afin de pouvoir traiter un exemple.
Un Projet RF c’est des jeux de données (souvent sous la forme de csv) et un ensemble de fichiers .robot servant de Suite Case pour ceux contenant des Tests Case où servant de Ressources sinon. Un .robot correspond au “code source” de notre projet et se décompose en différentes sections optionnelles:
*** Settings *** contiennent l'ensemble des importations qui \\ consiste en la déclaration des librairies utilisées où les ressources à utiliser. *** Variables *** définissent les données utilisables dans les mots clefs et les tests cases. *** Test Cases *** les tests à réaliser en s'appuyant les jeux de données et les variables\\ Peuvent se définir sous la forme de templates *** Keywords *** les mots clefs sont des fonctions de haut niveau constituant un ou plusieurs\\ DSL (technique ou fonctionnel) s'appuyant sur les mots clefs fournis par les librairies\\ et permettant la définition des scénarios des tests cases.
Afin de garantir une certaine cohérence dans la constitution du projet et faire correctement du DDT (Data Driven Test), il importe alors de composer les fichiers en séparant les différentes préoccupations possibles.
Data: on placera les données pour lesquels on construira des fichiers dédiées en séparant les données techniques propre à l’environnement de test, des données propres au métier du test, les données propres aux interfaces dans des répertoires séparés (par exemple tech_data.robot, fonctionnal_data.robot, etc)
- Technique/interface : On réalisera pour chaque interface du système testé des mots clefs techniques s’appuyant sur chaque librairie afin de simplifier la manipulation de celle-ci pour l’envoie où la réception de messages sans avoir de préoccupation technique particulière technical_keywords.robot
- Test/Fonctionnel: de la même manière, en s’appuyant sur les mots clefs techniques, on réalisera des mots clefs portant fonctionnellement des morceaux des uses case du composant à tester. La décomposition de ces mots clés doit faciliter la réutilisation au sein des Tests Case (comme par exemple un mot clé “Créer Utilisateur”) On placera ce mots clefs fonctionnels soit dans les suite case soit dans des fichiers dédiés selon le niveau d’abstraction (effectivement chaque couche doit constituer un DSL exploitable par la couche au dessus se rapprochant de plus en plus des utilisations métier pures)
A ce stade on notera une chose importante: Robotframework est un outil de test permettant avec la création de mots clefs, de faciliter la collaboration des développeurs et des fonctionnels en leur proposant de se constituer un langage commun. Bien sûr les développeurs auront la tâche de construire les couches les plus techniques des DSL mais ce seront aux fonctionnels de définir les mots clés facilitant la validation des uses cases (et la mise en oeuvre des tests cases)
Rentrons dans la syntaxe. Robotframework propose un syntaxe ultra simple afin de permettre à un non développeur de rapidement prendre en main le langage. Sur ce point, la documentation sera plus détaillé que cet article mais, ce qu’il faut retenir c’est que deux instructions robotframework se séparent toujours au minimum de deux espaces sur une même ligne.
Ainsi par exemple, prenons les type de données (*** Variables ***) :
${var} variable @{list} user1 user2 user3 user4 &{dico} user1=password1 user2=password2 user3=password3 user4=password4
Un autre exemple de configuration (*** Settings ***):
Library BuiltIn
Resource db_keywords.robot
Suite Setup Setup
Suite Teardown Teardown
Une partie contenant un mot clef (*** Keywords ***)
Modifier Role Modifier Profil &{ajout_user}[valid] &{user_data}
Enfin un cas de test (*** Test Cases ***):
Test Ajout Utilisateur Open Browser To Home Page ${BROWSER} Ajout Nouvel Utilisateur
Trop facile non?, c’est simple et immédiat à mettre en oeuvre non? Bien sur il y a quelques subtilités supplémentaires à prendre en compte comme le passage d’arguments aux mots clefs, où le retour de valeur d’un mot clef calculant quelques chose. Nous verrons ces quelques subtilité au fil de l’exemple que nous allons maintenant traiter.
Alors considérons un système dont le fonctionnement interne importe peu en l'état, considérons juste que celui-ci se compose d’une interface web par lequel nous pouvons enregistrer des utilisateurs et modifier leur profil. Ces utilisateurs sont renseignés en base de données et le système exposent deux interfaces supplémentaires, un webservice de type soap et une file JMS dont les messages sont générés lors des ajouts et modifications des utilisateurs.
Pour la mise en oeuvre de nos tests, nous allons utiliser Robotframework sur ces différentes interfaces via les librairies du schéma précédent. Pour cela, nous allons créer un fichier .robot pour chaque interfaces et librairies, pour les données d’environnements et pour les données métiers. Enfin les jeux de données seront répartis dans différents répertoires, un pour les webservices, un dernier pour les données métiers et un dernier pour la base de données.
Commençons par la gestion de l’environnement de test, avec un fichier variables_properties.robot nous allons initialiser des données pour pouvoir manipuler la base de données (entre autre l’initialiser DB_CMD_INIT et la vider DB_CMD_DROP, Liquibase [13]). Ensuite, on va initialiser les données pour manipuler le navigateurs ainsi que le conteneur applicatif (Weblogic [14]).
*** Variables *** &{DB_AUTH} user=usr pwd= &{DB_LIQUID} file=D:/Dev/robotframework/exemple/liquibase/changelog.xml \\ exe=D:/Tools/liquibase-3.2.3-bin/liquibase.bat &{DB_PROPERTIES} driver=org.h2.Driver file=~/h2base/mabase* lib=D:/Tools/h2/bin/h2-1.4.197.jar \\ url=jdbc:h2:tcp://localhost/~/h2base/mabase ${DB_CMD_INIT} &{DB_LIQUID}[exe] --driver=&{DB_PROPERTIES}[driver] --classpath=&{DB_PROPERTIES}[lib]\\ --changeLogFile=&{DB_LIQUID}[file] --url=&{DB_PROPERTIES}[url] --username=&{DB_AUTH}[user] migrate ${DB_CMD_DROP} &{DB_LIQUID}[exe] --driver=&{DB_PROPERTIES}[driver] --classpath=&{DB_PROPERTIES}[lib]\\ --changeLogFile=&{DB_LIQUID}[file] --url=&{DB_PROPERTIES}[url] --username=&{DB_AUTH}[user] dropAll ${BROWSER} Chrome ${VALID_USER} weblogic ${VALID_PASSWORD} weblogic ${LOGIN_URL} http://localhost:7001/console ${WL_CMD} D:/Tools/Oracle/[...]/domains/mondom/startWebLogic.cmd & ${WL_STP_URL} http://localhost:7001/console/console.portal=com.bea.console.handles.\\ JMXHandle%28%22com.bea%3AName%3DAdminServer%2CType%3DServer%22%29# ${WL_XPATH_STOP} xpath://html//[...]/div[1]/ul[1]/li[2]/a[1] ${BT_ARRET} xpath://html//[...]/tr[1]/td[1]/div[1]/button[4]
Ensuite il nous faut gérer des données de test. Par la création du fichier metier-data/metier.robot qui suit. A noter que ces données sont structurées de façon à simplifier la démarche de test et de validation. Ainsi les tests sont définis selon le même schéma (ici pour sélénium), on déclare, l’url, la page initiale, les schémas xpath de validation, et la page finale.
*** Variables *** ${ID} valeurId &{user_data} nom=Tomtom prenom=Tata tel id=${ID} email=thomas.tata@haha.com\\ page_ok=Administration - Fiche utilisateur ${ID} &{ajout_user} url=http://localhost:7001/monapp/utilisateur/creer page_name=Fiche utilisateur\\ valid=xpath://a[@id='btn-enregistrer'] menu=xpath://a[@class='small-btn btn-dd'] \\ path=xpath://a[@class='small-btn btn-dd'][contains(text(),'Créer un nouvel utilisateur')] &{recherche_user} url=http://localhost:7001/monapp/utilisateurs.html page_name=Administration\\ valid=xpath://html//div[@id='criteres-recherche']//a[2] menu=xpath://a[@class='small-btn’]\\ path=xpath://a[@class='small-btn‘][contains(text(),'Rechercher un utilisateur')] &{WService} url=http://localhost:7041/monapp/services/monWS?wsdl operation=monOpWS \\ requestFile=${CURDIR}/../ws-data/ws-req-template.robot expectFile=${CURDIR}/../ws-data/ws-rsp-template.robot &{JMS_Q} initial_context_factory=weblogic.jndi.WLInitialContextFactory jndi_provider_url=t3://localhost:7001\\ queue_topic=MonAppJmsSrv/MonAppJmsSrvModule!JMS_Q root=rootxml &{JMS_Q_CON} connection_factory_name=jms/MonAppJmsSrvCnxFactory connect=true start=true username=weblogic\\ password=weblogic transacted=true
La démarche est la même ensuite pour le webservcie et les flux jms, on dépose des templates variabilités dans des fichiers qui seront chargés dynamiquement.
ws-data/ws-req-template.robot
*** Variables *** ${REQUEST_WS_DATA} <soapenv:envelope soapenv:header=""><soapenv:body><com1:lireutilisateurrqt>\\ <com1:idutilisateur>${ID}</com1:idutilisateur></com1:lireutilisateurrqt></soapenv:body>\\ </soapenv:envelope>;
ws-data/ws-res-template.robot
*** Variables *** ${EXPECT_WS_DATA} <soap:envelope ns3:utilisateurs=""><listeutilisateur>\\ <idutilisateur>&{user_data}[id]</idutilisateur>\\ <nomutilisateur>&{user_data}[nom]</nomutilisateur></listeutilisateur></soap:envelope>
Pour chaque interfaces, on va alors définir un fichier de keywords modélisant les actions standards que l’on réalise sur celle ci. Sur la BD, une fonction appelant les commandes initialisation/suppression. On réalise de même sur l’interface des WS et sur la file JMS.
Le fichier Db_keywords.robot
*** Keywords *** Operate Liquibase Process On DataBase Server [Arguments] ${DB_CMD} Log To Console Restauration de la base Run Process ${DB_CMD} shell=True Ajout Commentaire Et attendre Reconstruction de la base ${DB_CMD} 59 seconds
Le fichier ws_keywords.robot
*** Keywords *** Requete WS [Arguments] &{WS_META_DATA} Import Resource &{WS_META_DATA}[requestFile] Import Resource &{WS_META_DATA}[expectFile] Create Soap Client &{WS_META_DATA}[url] ${resquet}= Create Raw Soap Message ${REQUEST_WS_DATA} ${resp}= Call Soap Method &{WS_META_DATA}[operation] ${resquet} ${last_receive}= Get Last Received Should Be Equal As Strings ${EXPECT_WS_DATA} ${last_receive}
Le fichier jms_keywords.robot qui comporte une subtilité en utilisant la librairie Remote pour charger de façon distante les mots clef de la librairie JMSLibrary
*** Settings *** Library Remote http://localhost:8270/jmslibrary WITH NAME JMSLibrary Library XML Resource variables_properties.robot *** Keywords *** Init JMS Provider [Arguments] ${CONNEXION_ARG} &{CONTEXT} Init Provider &{CONTEXT}[initial_context_factory] &{CONTEXT}[jndi_provider_url] ${CONNEXION_ARG} Verifier Message JMS Utilisateur [Arguments] ${codeType} ${id} &{CONTEXT} ${xml_message} Get JMS Message &{CONTEXT}[queue_topic] Should Be Equal ${xml_message.tag} &{CONTEXT}[root] Verify IdUtilisateur ${xml_message} ${id} Verify CodeTypeOperation ${xml_message} ${codeType} Close Connection Verify IdUtilisateur [Arguments] ${root_message} ${id} ${idUtilisateur} = Get Element ${root_message} utilisateur/idUtilisateur XML.Element Text Should Be ${idUtilisateur} ${id} [...] Get JMS Message [Arguments] ${queue_topic} Sleep 2 seconds Receive Once From Queue ${queue_topic} ${message}= JMSLibrary.Get Text Ajout Commentaire Reception : ${message} ${root_message} = Parse XML ${message} Return From Keyword ${root_message}
Enfin vient l’interface selenium qui est directement en interaction avec l’ihm du système. Pour ce cas, il sera difficile de ne pas produire des mots clés non fonctionnel. Cependant il est important de parvenir à s’abstraire au maximum des cas d’utilisations fonctionnels en fournissant juste des moyens de manipulation des différentes pages de l’application. Ainsi le fichier selenium_keywords.robot contiendra surtout des actions qu’il faudra contextualiser.
*** Keywords *** Pre Connect [Arguments] ${BROWSER} [...] Check Title Page Administration Open Browser To Home Page [Arguments] ${BROWSER} Set Selenium Timeout 2 seconds Pre Connect ${BROWSER} Modifier Profil [Arguments] ${valid_bt} &{USER} Click Element xpath://div[@class='row']//ul[@class='list unstyled mr1 right mb3']//li//a[@href='#']//span[contains(text(),'Ajouter un profil')] Click Element xpath://div[@class='inner mod border-default popin7 mt2 pa2 contenuPopin2']//tr[4] Click Element xpath://a[@id='btn-ajout-profil'] Sleep 1 seconds Click Element btn-enregistrer Check Title Page &{USER}[page_ok] Insert Et Valid User [...] Modify User [...] Ajout Entite [...] Go To Url Par Menu [...] Recherche Utilisateur [...] Check Title Page [...]
Ce fichier, le suite case main.robot, va, en plus de porter les mots clés fonctionnels, définir en premier lieu les procédures Setup/Teardown permettant de préparer le contexte de test et de le finaliser (dans notre cas, il s’agira surtout d’initialiser la base où la supprimer et de fermer proprement les navigateurs).
*** Settings *** Library BuiltIn Library OperatingSystem Library XML Library Collections Library SudsLibrary Resource db_keywords.robot Resource system_keywords.robot Resource selenium_keywords.robot Resource ws_keywords.robot Resource jms_keywords.robot Resource variables_properties.robot Resource metier-data/metier_1.robot Suite Setup Setup Suite Teardown Teardown [...] # Les tests case *** Keywords *** [...] # Les keywords fonctionnels Setup Log Environment Variables Remove Directory ${SCREENSHOT_DIR} recursive=true Set Screenshot Directory ${SCREENSHOT_DIR} Operate Liquibase Process On DataBase Server ${DB_CMD_INIT} Teardown Ajout Commentaire FIN Close All Browsers Operate Liquibase Process On DataBase Server ${DB_CMD_DROP} Terminate All Processes kill=True
main.robot (*** Keywords ***)
*** Keywords *** Modifier Role Zone Modifier Profil &{ajout_user}[valid] &{user_data} Modifier Telephone Utilisateur Modify User &{ajout_user}[valid] &{user_data} Ajout Nouvel Utilisateur Go To Url Par Menu &{ajout_user} Insert Et Valid User &{ajout_user}[valid] &{user_data} Recherche Nouvel Utilisateur Go To Url Par Menu &{recherche_user} Recherche Utilisateur &{user_data} Requete Utilisateur Sur WS Requete WS &{WService} Verifier Creation Utilisateur Sur JMS Init JMS Provider ${JMS_Q_CON} &{JMS_Q} Verifier Message JMS Utilisateur creation &{user_data}[gaia] &{JMS_Q} Verifier Modification Utilisateur Sur JMS Init JMS Provider ${JMS_Q_CON} &{JMS_Q} Verifier Message JMS Utilisateur modification &{user_data}[gaia] &{JMS_Q}
Enfin dans le fichier main.robot il va être possible de construire les scénarii et les tests cases consistant à ajouter un utilisateur puis vérifier via le webservice qu’il a ete realisé, qu’ensuite un message JMS a été produit.
*** Test Cases *** Test Ajout Utilisateur Open Browser To Home Page ${BROWSER} Ajout Nouvel Utilisateur Test Recuperation Information Sur WS Requete Utilisateur Sur WS Test Interrogation JMS CREATION Open Browser To Home Page ${BROWSER} Recherche Nouvel Utilisateur Modifier Role Zone Verifier Creation Utilisateur JMS
Enfin il reste à exécuter notre suite case. Avant cela, on va lance le serveur de la base de données (ici H2base), lancer également l’application (dans son conteneur applicatif). Il faut également exécuter le server remote pour la librairie JMS. Et enfin on lance les tests, en mode console.
$ java org.robotframework.remoteserver.RemoteServer -l JMSLibrary:/jmslibrary -p 8270 $ rf-env -m robot --loglevel DEBUG main.robot
La console nous indique alors l'exécution suivante de laquelle on déduit le bon déroulement des tests.
====================================================================== Main ====================================================================== Restauration de la base 2018-05-23 13:27:34:Reconstruction de la base D:/Tools/liquibase-3.2.3-bin/liquibase.bat […] migrate Test Ajout Utilisateur :: Test realisant l'ajout d'un nouvel utili... DevTools listening on ws://127.0.0.1:12794/devtools/browser/9dae94b9-5b37-48a3-82e5-98901a9b8383 Test Ajout Utilisateur :: Test realisant l'ajout d'un nouvel utili... | PASS | ------------------------------------------------------------------------------ Test Recuperation Information WebService:: Test validant l'ajout d'un... | PASS | ------------------------------------------------------------------------------ Test Interrogation JMS CREATION :: Test validant l'envoie sur l... DevTools listening on ws://127.0.0.1:12145/devtools/browser/c877364c-2582-4442-9ad0-0f987d4f0224 [...] Test Interrogation JMS CREATION :: Test validant l'envoie sur l... | PASS | ------------------------------------------------------------------------------ 2018-05-23 13:29:51:FIN Restauration de la base 2018-05-23 13:29:53:Reconstruction de la base D:/Tools/liquibase-3.2.3-bin/liquibase.bat […] dropAll Main | PASS | 5 critical tests, 5 passed, 0 failed 5 tests total, 5 passed, 0 failed ====================================================================== Output: D:\Dev\robotframework\main\output.xml Log: D:\Dev\robotframework\main\log.html Report: D:\Dev\robotframework\main\report.html
A noter que ce listing console nous donne également un rapport d'exécution nous permettant de justifier le résultat et le cas échéant de réaliser une analyse si il advenait que des tests se trouvaient en échec.
Conclusion
Voila, cela faisait un moment que je n’avais pas écrit d’article mais je me rattrape un peu a cet article plutôt long qui il me semble porté sur un sujet réellement fondamental aujourd’hui qu’est l’automatisation des tests. Nous avons vu avec robot framework qu’il était possible de tester fonctionnellement une application de façon rationnelle et maîtrisée dans une approche cohérente à la production logicielle. De plus robotframework étant un outil orienté fonctionnel, son accessibilité rend la définition des tests beaucoup plus simple et rapide.Références
[1] http://robotframework.org/[2] https://www.python.org/
[3] http://www.jython.org/
[4] http://ironpython.net/
[5] https://github.com/robotframework/RemoteInterface
[6] https://github.com/robotframework/robotframework
[7] http://ilkkatoje.github.io/robotframework-jmslibrary/1.0.0/robotframework-jmslibrary.html
[8] http://ombre42.github.io/robotframework-sudslibrary/doc/SudsLibrary.html
[9] http://robotframework.org/SeleniumLibrary/SeleniumLibrary.html
[10] https://franz-see.github.io/Robotframework-Database-Library/api/0.5/DatabaseLibrary.html
[11] http://chromedriver.chromium.org/
[12] https://github.com/ombre42/jrobotremoteserver
[13] https://www.liquibase.org/
[14] https://www.oracle.com/fr/middleware/weblogic/index.html
[15] http://cobertura.github.io/cobertura/
[16] https://github.com/kowalpy/Robot-Framework-JMeter-Library
[17] https://jmeter.apache.org/
[18] http://un-est-tout-et-tout-est-un.blogspot.fr/2018/04/les-tests-logiciels.html
[19] http://www.sikuli.org/ [20] https://github.com/rainmanwy/robotframework-SikuliLibrary
[21] https://virtualenv.pypa.io/en/stable/
[22] robotframework.slack.com
Aucun commentaire:
Enregistrer un commentaire