Thématiques principales

dimanche 27 mai 2018

Robotframework

Nous avons vu dans un article précédent [18] les differents types de tests que l’on pouvait rencontrer dans le cadre du développement de système logiciel. Nous avions alors évoqué l’utilisation qui pouvait être fait de l’outil RobotFramework [1] sans être pour autant rentrer dans le détail de son utilisation.

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>&amp;{user_data}[id]</idutilisateur>\\
          <nomutilisateur>&amp;{user_data}[nom]</nomutilisateur></listeutilisateur></soap:envelope>
Maintenant que les jeux de données sont définis, il va maintenant être possible de définir les premiers DSL techniques permettant l’utilisation des différentes interfaces en fournissant une sur-couche plus fonctionnel.

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
[...]
Enfin il reste maintenant à concevoir la partie fonctionnelle des mots clefs qui nous serviront à élaborer les tests cases. Ces mots clés, a ce stade, vont s’appuyer sur l’ensemble de ceux techniques précédemment réalisés. Ces mots clefs vont alors être défini dans le fichier principal, le suite case, dans la section Keywords.

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