Rôle | Nom |
---|---|
Créateur | Olivier JAN |
Contributeurs | Olivier JAN, Olivier LI-KIANG-CHEONG, David GUENAULT |
Le but de ce tutoriel est d’avoir, de construire un automate de tests de bout en bout, également appelé End User Experience
Tout le reste est expliqué dans ce qui suit ;)
Nous avons donc comme point de départ une distribution Linux de type desktop avec interface graphique et un navigateur Firefox. Il est bien entendu que cette machine à accès aux sites à tester via probablement une connexion Internet active. Vu que tous les éléments sont programmés en Ruby, on installe fort logiquement Ruby et surtout ruby gems pour pouvoir installer la suite et quelques dépendances. Nous installons une version 1.8 de Ruby et de ces composants. Nous aurons également besoin des librairies de développement Ruby.
sudo apt-get install ruby rubygems ruby-dev libxml2-dev libxslt-dev libssl-dev build-essential libruby-extras
Il est recommandé de mettre à jour les gems déjà installés par la commande
sudo gem update
Et enfin, tout au moins sur notre Ubuntu qui ne possèdes pas une version de Ruby Gems suffisament à jour (1.3.6 minimum), il faut les mettre à jour comme ceci.
sudo gem install rubygems-update cd /var/lib/gems/1.8/bin/ sudo ./update_rubygems
Cela nous installe une version 1.8.4 à l’heure de la rédaction de Ruby Gems. Ensuite, les gems pour Cucumber-nagios, Cucumber, Watir et Webdriver
sudo gem install cucumber cucumber-nagios watir watir-webdriver
sudo apt-get install xvfb sudo gem install headless
A ce moment là, il devient inutile d’installer une distribution desktop, une version serveur suffit.
Nous allons voir un peu plus loin comment utiliser XVFB pour exécuter nos scénarios en mode “headless” (c.a.d. sans serveur X).
Notre navigateur Firefox est désormais prêt à être scripté. Attaquons un premier scénario basique sur wiki.monitoring-fr.org
Nous allons rédiger un scénario d’exemple simple mais pas simpliste nous permettant de vérifier que le wiki monitoring-fr.org est disponible, qu’il est possible de se connecter avec un identifiant et un mot de passe afin de rédiger de nouveaux tutoriaux comme celui-ci et enfin d’effectuer une recherche parmi le contenu de celui-ci.
Avant de commencer la rédaction des scénarii à proprement parler, il convient de créer un dossier que vous nommerez comme bon vous semble pour y stocker nos scénarii Cucumber et nos étapes (steps). Dans notre cas, nous créons un dossier wiki comprenant deux sous-dossiers : support et steps (ces deux noms sont obligatoires).
mkdir -p wiki/steps mkdir -p wiki/support
Nous aurions pu utiliser la méthode d’auto-génération de ces dossiers comme expliqué dans le tutoriel Cucumber mais avons préféré montrer que tout ceci pouvait se gérer manuellement. Le dossier support contient un fichier env.rb qui permet de placer quelques variables d’exécution bien senties dont je vous livrerais le contenu le moment voulu.
La force de cucumber, est-il besoin de le rappeler, est de pouvoir rédiger des tests en langage naturel… ou presque ;) et de décrire ceux-ci par le comportement attendu de notre application. Nous allons éclater notre test en deux scénarii complémentaires afin de pouvoir les tester séparément ou enchaînés. Le premier est la séquence de connexion (nom de fichier logging.feature)
vi logging.feature
Feature: wiki Logging In order to write more content into the wiki I need to be able to log into the wiki Scenario: wiki Logging Given that I am on the wiki Connexionpage When I identify as "cucumber" with password "lepassequivabien" Then I should see "Déconnexion"
et le deuxième fort logiquement la séquence de recherche (fichier search.feature).
vi search.feature
Feature: wiki Search In order to find more about Shinken I need to be able to search wiki Scenario: wiki Search for Shinken Given that I am on the wiki Homepage When I search for "Shinken" Then I should see "Installation de Shinken"
Comme vous pouvez le constater, nous ne somme pas très éloignés de la formulation que j’ai faite au départ de cette rédaction ci-dessus. Il reste à faire correspondre ces “features” et “scénario” à des action Watir qui piloteront le navigateur Firefox au travers de Webdriver.
Je vous en ai parlé un peu avant, préparons notre environnement d’exécution Watir en plaçant un fichier env.rb dans le dossier support de notre dossier wiki qui contient ceci :
begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end if ENV['FIREWATIR'] require 'firewatir' Browser = FireWatir::Firefox.new else case RUBY_PLATFORM when /darwin/ require 'safariwatir' Browser = Watir::Safari.new when /x86_64-linux/ require 'watir-webdriver' Browser = Watir::Browser.new(:firefox) when /win32|mingw/ require 'watir' Browser = Watir::IE.new when /java/ require 'celerity' Browser = Celerity::Browser.new else raise "This platform is not supported (#{PLATFORM})" end end # "before all" browser = Browser Before do @browser = browser end # "after all" at_exit do browser.close end
Nous allons maintenant rédiger nos “steps” en nous appuyant sur Cucumber pour nous indiquer la marche à suivre. Si nous tentons d’exécuter nos scénarii à ce moment, Cucumber va nous indiquer fort logiquement que nous n’avons pas de “steps” correspondant à ceux-ci. Pratique :)
cd wiki cucumber logging.feature
retourne alors
Feature: wiki Logging In order to write more content into the wiki I need to be able to log into the wiki Scenario: wiki Logging # logging.feature:5 Given that I am on the wiki Connexionpage # logging.feature:6 When I identify as "lidentifiant" with password "lepasse" # logging.feature:7 Then I should see "Déconnexion" # logging.feature:8 1 scenario (1 undefined) 3 steps (3 undefined) 0m0.004s You can implement step definitions for undefined steps with these snippets: Given /^that I am on the wiki Connexionpage$/ do pending # express the regexp above with the code you wish you had end When /^I identify as "([^"]*)" with password "([^"]*)"$/ do |arg1, arg2| pending # express the regexp above with the code you wish you had end Then /^I should see "([^"]*)"$/ do |arg1| pending # express the regexp above with the code you wish you had end
Il nous faut créer le fichier logging_steps.rb dans le dossier steps avec le contenu suivant :
vi steps/logging_steps.rb
Given /^that I am on the wiki Connexionpage$/ do @browser.goto('http://wiki.monitoring-fr.org/start?do=login') end When /^I identify as "([^"]*)" with password "([^"]*)"$/ do |id, password| @browser.text_field(:name, 'u').set(id) @browser.text_field(:name, 'p').set(password) @browser.button(:value, 'Connexion').click end Then /^I should see "([^"]*)"$/ do |text| @browser.text.include?(text).should == true end
Notez comment on retrouve à quelques modifications près les suggestions de Cucumber quand nous avons testé le scénario sans “steps” écrits. Tout ce qu’il y a entre le when et le end est du “code” Watir à proprement parlé. Une référence des commandes possibles est disponible sur le site de Watir. Le deuxième fichier search_steps.rb contient ceci :
vi steps/search_steps.rb
Given /^that I am on the wiki Homepage$/ do @browser.goto('http://wiki.monitoring-fr.org') end When /^I search for "([^"]*)"$/ do |query| @browser.text_field(:name, 'id').set(query) @browser.button(:value, 'Rechercher').click end Then /^I should see "([^"]*)"$/ do |text| @browser.text.include?(text).should == true end
C’est en place. Exécutons de nouveau
cucumber logging.feature
Nous avons alors une sortie beaucoup plus conforme à nos attentes :) Notre scénario logging fonctionne parfaitement comme nous l’indique la sortie ci-dessous.
Feature: wiki Logging In order to write more content into the wiki I need to be able to log into the wiki Scenario: wiki Logging # logging.feature:5 Given that I am on the wiki Connexionpage # steps/logging_steps.rb:1 When I identify as "cucumber" with password "lepassequivabien" # steps/logging_steps.rb:9 Then I should see "Déconnexion" # steps/search_steps.rb:10 1 scenario (1 passed) 3 steps (3 passed) 0m8.078s
Il reste maintenant la partie traditionnelle de branchement de ces deux scénarii à notre ordonnanceur de supervision.
Oui m’sieur c’est possible et en plus c’est très simple à mettre en place :
tout d’abord il va falloir connaitre les mots clés disponible :
cucumber --i18n fr | feature | "Fonctionnalité" | | background | "Contexte" | | scenario | "Scénario" | | scenario_outline | "Plan du scénario", "Plan du Scénario" | | examples | "Exemples" | | given | "* ", "Soit ", "Etant donné " | | when | "* ", "Quand ", "Lorsque ", "Lorsqu'" | | then | "* ", "Alors " | | and | "* ", "Et " | | but | "* ", "Mais " | | given (code) | "Soit", "Etantdonné" | | when (code) | "Quand", "Lorsque", "Lorsqu" | | then (code) | "Alors" | | and (code) | "Et" | | but (code) | "Mais" |
La colonne de gauche donne les mots clés de référence, et la colonne de droite donne les mots clés “traduits”. Les mots clés suffixé par (code) correspondent à la traduction des mots utilisés dans la rédaction des “steps”.
Reprenons notre scénario d’oirgine :
Feature: wiki Logging In order to write more content into the wiki I need to be able to log into the wiki Scenario: wiki Logging Given that I am on the wiki Connexionpage When I identify as "cucumber" with password "lepassequivabien" Then I should see "Déconnexion"
la “traduction” en français donnera :
# language: fr Fonctionnalité: authentification wiki Afin d'écrire plus de contenu dans le wiki J'ai besoin d'être capable de m'authentifier dans le wiki Scénario: authentification wiki Etant donné que je suis sur la page de connexion du wiki Quand je m'identifie en tant que "cucumber" avec le mot de passe "lepassquivabien" Alors je devrais voir "Déconnexion"
notez la première ligne, # language: fr, qui permet de spécifier le langage dans lequel est rédigé le scénario. Il y en à d’autre (nombreux) que vous pouvez lister de la manière suivante :
cucumber --i18n help
Ensuite nous allons traduire les “steps”
A l’origine nous avions ceci
Given /^that I am on the wiki Connexionpage$/ do @browser.goto('http://wiki.monitoring-fr.org/start?do=login') end When /^I identify as "([^"]*)" with password "([^"]*)"$/ do |id, password| @browser.text_field(:name, 'u').set(id) @browser.text_field(:name, 'p').set(password) @browser.button(:value, 'Connexion').click end Then /^I should see "([^"]*)"$/ do |text| @browser.text.include?(text).should == true end
Et le résultat dans la “langue de molière” (ou presque) et le suivant :
Etantdonné /^que je suis sur la page de connexion du wiki$/ do @browser.goto('http://wiki.monitoring-fr.org/start?do=login') end Quand /^je m'identifie en tant que "([^"]*)" avec le mot de passe "([^"]*)"$/ do |id, password| @browser.text_field(:name, 'u').set(id) @browser.text_field(:name, 'p').set(password) @browser.button(:value, 'Connexion').click end Alors /^je devrais voir "([^"]*)"$/ do |text| @browser.text.include?(text).should == true end
L’exécution du scénario se fait exactement de la même manière que pour l’original.
Simple non ?
Il faut installer par la méthode qui vous convient le démon NRPE sur la machine automate. C’est à travers ce protocole que nous ferons nos demandes de contrôles depuis le serveur de type Nagios. Vous pouvez néanmoins faire ces demandes par n’importe quel autre protocole à votre disposition (SSH, NSClient++ pour un automate Windows avec Explorer ;))
Nous allons donc faire correspondre l’appel de nos scénarii à des appels de type commandes NRPE sur notre automate de supervision en ajoutant dans le fichier de configuration NRPE les éléments suivants :
command[check_wiki]=/usr/bin/cucumber-nagios /home/admin/EUE/production/wiki/ command[check_wiki_search]=/usr/bin/cucumber-nagios /home/admin/EUE/production/wiki/search.feature command[check_wiki_logging]=/usr/bin/cucumber-nagios /home/admin/EUE/production/wiki/logging.feature
Notez le premier appel qui pointe vers notre dossier wiki et qui permet d’enchaîner toutes les “features” présentes dans celui-ci. Les deux autres appels précisent chacun quant à eux une “feature” particulière. Nous utilisons dans ces appels cucumber-nagios et plus cucumber de façon à avoir une sortie moins verbeuse et compatible avec le format de sortie de Nagios. C’est à ça qu’il sert et n’est donc qu’une surcouche au final de cucumber. Le reste ne devrait pas vous surprendre.
Comme nous allons le voir, c’est un check_nrpe que nous utilisons pour déclencher nos scénarii sur notre automate… Logique :) Dans un ordonnanceur type Nagios, nous avons besoin à minima d’une commande au sens Nagios du terme, d’un hôte et d’un service pour pouvoir faire un contrôle. Je vous passe le fichier de configuration de l’hôte qui est habituel et je m’attarde sur la commande et le service que j’ai essayé de rendre pratique en utilisant des macros personnalisées.
# 'check_watir' command definition define command{ command_name check_watir command_line $USER1$/check_nrpe -H $_SERVICEROBOT_IP$ -t $_SERVICETIMEOUT$ -c $ARG1$ }
Je définis dans le fichier de commande check_watir ci-dessus deux macros $_SERVICEROBOT_IP$ et $_SERVICETIMEOUT$ qui vont contenir respectivement l’adresse IP de notre automate et la valeur de timeout du script, celle-ci pouvant être personnalisé par type de scénario à exécuter. Pratique car un scénario peut durer 10 secondes comme une minute ou plus dans le cadre de l’EUE. En $ARG1$, le nom de la commande NRPE telle qu’a été définie sur notre automate ci-dessus.
define service{ use eue-service host_name wiki service_description Search Feature check_command check_watir!check_wiki_search _TIMEOUT 60 _ROBOT_IP 192.168.3.4 }
La définition de service ci-dessus contient les deux macros personnalisées qui seront passées à la commande vu plus haut. J’hérite d’un template eue-service toutes les propriétés du service non précisé ci-dessous.
Au final, Nagios ordonnancera le check suivant à l’intervalle que vous voulez en substituant les macros par leurs valeurs définies dans le service.
/usr/local/nagios/libexec/check_nrpe -H 192.168.3.4 -t 60 -c check_wiki_search
Pour une sortie Nagios compatible donc, y compris données de performance ;)
CUCUMBER OK - Critical: 0, Warning: 0, 3 okay | passed=3; failed=0; nosteps=0; total=3; time=5
Il vous restera à industrialiser les fichiers via templates pour avoir une supervision de type EUE parfaitement opérationnelle.