Selenium, allons plus loin 1/2

Ayant récemment finalisé une prestation de coaching technico-agile chez un de nos clients, je souhaitais partager ce retour d’expérience sur la mise en place de Selenium et plus particulièrement celle de l’écosystème technique existant autour de cette solution.

Cet article présente et propose :

  • La mise en place de solutions techniques qui complètent l’usage de Selenium dans le cadre d’une industrialisation des tests fonctionnels
  • L’obtention d’un processus de test le plus simple qui soit et évolutif

Il y est également abordé quelques fonctionnalités et astuces utiles proposées par les outils Selenium.

Le contexte

Selenium est un outil qui fonctionne sur de nombreuses plateformes de développement Java, .Net …. Dans la suite de l’article, cette présentation sera en java.

Avant tout, définissons les contraintes du client :

  • La portabilité est essentielle dans la mise en place de son applicatif. Il souhaite pouvoir disposer d’un comportement équivalent de son application sur Mac, Windows et sous les différents navigateurs (et leurs versions) qui y sont associables (Firefox, Chrome, IE, Safari, Opera, ….).
  • Il souhaite disposer de ses tests dans le cadre d’une intégration continue.

Compte tenu de ces informations, vous trouverez ci-dessous le cheminement de mise en place des tests et de leur industrialisation :

  1. Création d’un test fonctionnel
  2. Exécution du test depuis le format html sur une seule plateforme (puis plusieurs)
  3. Mise en place d’une solution qui permette l’exécution des tests par un framework de build (ant, phing (pour PHP), maven)
  4. Exécution des tests sous plusieurs environnements (VM ou machines physiques dédiées)
  5. Exécution des tests dans un environnement déporté et infogéré.

Le test

En elle-même, la création d’un test Selenium est triviale. Cependant, la complication survient lors de la ré-exécution du test fraîchement généré. En effet, lors de l’enregistrement de chacune des actions sur le site, une nouvelle entrée est enregistrée dans un tableau. Lors de l’éxécution du test, donc du dépilement des actions à mener sur la page, en l’absence d’indication de durée avant d’exécuter l’action suivante, les actions se succèdent. Toutefois, Selenium IDE est capable d’identifier si ce process d’attente doit être intégré à la commande ou pas. Cependant, il peut arriver que certaines actions, suivant l’environnement, soit plus ou moins rapide dans leur exécution et qu’un délai d’attente ne soit pas implémentée lors de la phase d’enregistrement. Dans ce cas, je vous propose les solutions ci-dessous à implémenter manuellement dans le test :

  • solution  1 : mise en place de Selenese de type “waitFor”, qui va proposer d’attendre que l’exécution de la commande suivante soit réalisable (moyennant que le timeout par défaut ne soit pas dépassé)
  • solution 2 : mise en place de Selenese “setSpeed”, qui défini la vitesse d’exécution du test en millisecond.
  • solution 3 : modification du timeOut, “setTimeout” qui force la valeur par défaut (30ms). A utiliser dans le cas d’une transition de page complexe par exemple.

Je souligne une problématique que vous rencontrerez. En effet, lors de certains tests nous insérons ou modifions des données en base. Cela peut mettre en échec un test rejoué.

  • Solution : mise en place d’une procédure arrière présentée dans un article qui complètera celui-ci.

Dans la création du test et compte tenu du sujet de ce ticket je vais me baser sur un exemple simple, à savoir une séance de navigation sur le blog de So@t. Dans la génération de ce test, j’ai pris garde à contourner les points de contention de Selenium grâce à la commande “waitForTextPresent” sur les transitions de pages qui pourraient impliquer des erreurs sur des browsers plus lents.

Vous trouverez sur l’enregistrement ci dessous le mode opératoire de création du scénario :

Ci-dessous le test au format html (sortie standard de  Selenium IDE).

blogSoat
open http://blog-rec.soat.fr
verifyTextPresent Agilité, Java EE, .NET et plus si affinités
clickAndWait //ul[@id=’navigation’]/li[2]/a/span
waitForTextPresent //div[@id=’post-2′]/div/p[2] Créé en 2000, So@t s’impose progressivement dans le paysage de l’ingénierie et du conseil en informatique.
clickAndWait //ul[@id=’navigation’]/li[3]/ul/li/a/span
clickAndWait css=a.fadeThis.sf-with-ul > span.title
waitForText css=h1.title Formation
clickAndWait //img[@title=’Détails formation agile’]

Le contenu du test peut être exploité sous différents formats :

  • HTML
  • Test Unitaire (Java, C#, Ruby,…)

Nous allons aborder des possibilités d’utilisation des tests qui tournent autour de JAVA et HTML. Le format HTML est le résultat de Selenium IDE. Concernant Java, je présenterai la procédure d’export, l’idée encore une fois n’étant pas de présenter la construction d’un test grâce à l’API selenium-java.jar.

Essais du test au format HTML

Le format HTML est le plus utilisé pour l’enregistrement de test ou plus généralement dans l’utilisation de sélenium. Il l’est parce qu’il est naturellement généré par Selenium IDE. Il est simple de compréhension pour les utilisateurs n’ayant pas de connaissances techniques sur des plateformes de développement tel que java ou .Net. Il convient pour la mise en place de tests par des MOA ou membres d’une équipe de recette, dont l’objectif premier est de se focaliser sur des briques applicatives fonctionnelles.

Pour les raisons précédemment énoncées, j’ai eu l’occasion de proposer à mon client les deux solutions qui vont suivre.

Solution 1

La première et la plus basique des solutions est d’utiliser Selenium IDE en tant qu’outil de ré-exécution.

Inconvénients :

  • Pas de portabilité sur les différents browsers. Se limite à un test sur firefox.
  • Ce mode de fonctionnement est lourd lorsqu’il s’agira de tester plusieurs dizaines de scénarios. Il y a un risque que les utilisateurs se lassent et ne se focalisent que sur les nouveaux UseCases sans pourtant s’assurer de la non régression de l’application.

Solution 2

Utilisation Selenium AES (Auto Execution) : http://www.enjoyxstudy.com/selenium/autoexec/index.en.html

Avantage :

  • Elle permet de rester sur le format proposé par Selenium IDE et offre la possibilité d’exécuter les tests sous 4 navigateurs. De plus dans un objectif d’automatisation, le système peut interagir avec un outil de gestion des sources.

Inconvénient :

  • Pas de portabilité des tests sur différents OS. A moins, bien évidemment, de déployer ce système sous différents OS.

Exécution du test au format Java

Compte tenu des avantages et des inconvénients de l’utilisation d’une solution par exécution des scripts Html selenium, nous avons poussé le raisonnement afin de lever les problématiques de portabilité, d’infogérance et de répétabilité. Il en a résulté la conclusion suivante :

Les tests sont créés par les membres du métier alors que la gestion de la portabilité et répétabilité serait à la charge de l’IT. Pour cela nous utilisons le module d’export proposé par Selenium IDE.

A l’image des travaux de recherche effectués sur le format HTML nous avons validé plusieurs scénarios d’industrialisation des tests dans le cadre d’une Intégration Continue.

Le premier scénario est l’utilisation de Selenium Server (et son HUB) qui est une utilisation naturelle de Selenium. Nous avons enfin porté une attention particulière au environnement infogéré du cloud.

Export HTML -> JAVA

Selenium IDE propose un export du test au format HTML vers plusieurs plateformes de développement.

Sous Fichier/Exporter le Test sous … , je choisis le format TestNG.

Vous pourrez noter la richesse des plateformes d’exports. Il est cependant à noter que l’export au format PHP n’est plus supporté depuis la version 1.2 du plugin.

Le code ainsi généré est le suivant :

package com.soat.selenium;

import org.testng.annotations.Test;

import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.SeleneseTestNgHelper;

public class testSoat extends SeleneseTestNgHelper {
@Test public void testTestSoat() throws Exception {

selenium.open("http://blog-rec.soat.fr/");
verifyTrue(selenium.isTextPresent("Agilité, Java EE, .NET et plus si affinités"));
selenium.click("//ul[@id='navigation']/li[2]/a/span");
selenium.waitForPageToLoad("30000");
for (int second = 0;; second++) {
if (second >= 60) fail("timeout");
try { if ("Cr?? en 2000, So@t s?impose progressivement dans le paysage de l?ing?nierie et du conseil en informatique.".equals(selenium.getText("//div[@id='post-2']/div/p[2]"))) break; } catch (Exception e) {}
Thread.sleep(1000);
}

selenium.click("//ul[@id='navigation']/li[3]/ul/li/a/span");
selenium.waitForPageToLoad("30000");
selenium.click("css=a.fadeThis.sf-with-ul > span.title");
selenium.waitForPageToLoad("30000");
for (int second = 0;; second++) {
if (second >= 60) fail("timeout");
try { if ("Formation".equals(selenium.getText("css=h1.title"))) break; } catch (Exception e) {}
Thread.sleep(1000);
}

selenium.click("//img[@title='Détails formation agile']");
selenium.waitForPageToLoad("30000");
}

@Override
public void tearDown() throws Exception {
// TODO Auto-generated method stub
super.tearDown();
}
}

Utilisant Maven, je vous propose la configuration suivante. Comme vous le constaterez, j’ai récupéré la dépendance afin de travailler avec TestNG. Je le préfère à JUnit 4 et de plus il est supporté en export par Selenium IDE.

Configuration Maven du projet :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.soat</groupId>
<artifactId>seleniumSoat</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>seleniumSoat</name>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.3.1</version>
</dependency>
</dependencies>
</project>

Il manque encore un élément dans notre système. Un server capable de lancer les navigateurs et d’exécuter les requêtes HTTP provenant  des API Selenium. Pour cela nous utiliserons Selenium-Server.

Solution 1 : utilisation de selenium-server

Utilisation de  selenium-server-standalone-2.20.0.jar disponible ici .

java -jar selenium-server-standalone-2.20.0.jar

5 mars 2012 11:00:30 org.openqa.grid.selenium.GridLauncher main
INFO: Launching a standalone server
11:00:36.448 INFO - Java: Apple Inc. 20.4-b02-402
11:00:36.450 INFO - OS: Mac OS X 10.7.3 x86_64
11:00:36.466 INFO - v2.18.0, with Core v2.18.0. Built from revision 15704
11:00:36.670 INFO - RemoteWebDriver instances should connect to: http://127.0.0.1:4444/wd/hub
11:00:36.672 INFO - Version Jetty/5.1.x
11:00:36.673 INFO - Started HttpContext[/selenium-server/driver,/selenium-server/driver]
11:00:36.674 INFO - Started HttpContext[/selenium-server,/selenium-server]
11:00:36.675 INFO - Started HttpContext[/,/]
11:00:36.736 INFO - Started org.openqa.jetty.jetty.servlet.ServletHandler@5bbf3d87
11:00:36.737 INFO - Started HttpContext[/wd,/wd]
11:00:36.744 INFO - Started SocketListener on 0.0.0.0:4444
11:00:36.744 INFO - Started org.openqa.jetty.jetty.Server@5289cf1e

Comme l’indique les logs, nous avons lancé un Jetty sur le port 4444.

Il est possible de se passer du lancement du server à la main au travers du plugin selenium-maven-plugin . Ce dernier va gérer le démarrage et l’arrêt du serveur Selenium avant chaque phase de test.

Configuration maven à ajouter :

<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>selenium-maven-plugin</artifactId>
<version>1.0.1</version>
<executions>
<execution>
<id>start</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start-server</goal>
</goals>
<configuration>

<background>true</background>
</configuration>
</execution>
<execution>
<id>stop</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop-server</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Solution 2 : Utilisation de Selenium Grid

Avant tout, un article qui vous précisera le fonctionnement de Selenium Grid par ici.

Je ne souhaite en effet pas effectuer un travail qui l’a déjà été.

Cette solution s’appuie sur le HUB de Selenium Server. Elle autorise l’exécution de tests sur des plateformes systèmes du réseau. Additionnée à la capacité du code selenium server d’exécuter un test sous différents browsers, cette solution permet ainsi l’exploitation de plusieurs environnements de travail (window IE, linux FireFox, window Chrome, ….)

Inconvénient :

Compte tenu de la pluralité des environnements combinables (OS – BROWSER), cette solution est très intéressante mais nécessite la mise en place d’un ecosystème de machine dédiée ou de VM.

Solution 3 : Le cloud

Je me suis orienté sur cette solution au vu des services proposés

  • Des plateformes de tests variées (voir ici)
  • Un plugin firefox (Sauce Launcher) téléchargeable ici(cf. figure 1)
    • permettant la création de tests à l’image de ce que propose Selenium IDE
      • Il faut toutefois noter le fait que ce plugin est moins user friendly que celui de Selenium (cela n’engage que moi): en effet il n’est pas possible de sélectionner un élément de la page en cours au travers d’un click droit souris (proposé nativement par Selenium IDE et fortement pratique pour effectuer un assert ou verify de présence d’un élément).
    • proposant la possibilité d’exécuter un test en one shot sur un environnement précis (cf figure 3)
    • l’export des tests pour des environnements précis est intégré (cf figure 2)

Accès au plugin :

Exécution du test sur SauceOnDemand :

Export au format JAVA suivant l’environnement :

Figure 2 : Sélection de la plateforme d’export

Figure 3 : Choix des options d’exécution du test                

Résultat de la génération du test au format Java pour un environnement Window 2003 / Safari 3 :

NOTA : Au moment où je rédige cet article, l’export SauceOnDemand ne fonctionne pas correctement pour les tests Java au format TestNG. Il n’y inclut pas le setup paramétrant les informations de connexion et de paramétrage à SauceOnCommand.

package com.soat.selenium;

import com.thoughtworks.selenium.SeleneseTestCase;

public class testSauceLabSoat2 extends SeleneseTestCase {
public void setUp() throws Exception {
setUp("http://blog-rec.soat.fr/", "{\"username\": \"So@t\",\"access-key\":\"${myKey}\",\"browser\": \"safariproxy\",\"browser-version\":\"5\",\"job-name\":\"testSauceLabSoat2\",\"max-duration\":3000,\"record-video\":true,\"user-extensions-url\":\"\",\"os\":\"Windows 2003\"}");
}
public void testTestSauceLabSoat2() throws Exception {
selenium.open("/");
verifyTrue(selenium.isTextPresent("Agilité, Java EE, .NET et plus si affinités"));
selenium.click("//ul[@id='navigation']/li[2]/a/span");
selenium.waitForPageToLoad("30000");
for (int second = 0;; second++) {
if (second >= 60) fail("timeout");
try { if ("Créé en 2000, So@t s’impose progressivement dans le paysage de l’ingénierie et du conseil en informatique.".equals(selenium.getText("//div[@id='post-2']/div/p[2]"))) break; } catch (Exception e) {}
Thread.sleep(1000);
}

selenium.click("//ul[@id='navigation']/li[3]/ul/li/a/span");
selenium.waitForPageToLoad("30000");
selenium.click("css=a.fadeThis.sf-with-ul &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; span.title");
selenium.waitForPageToLoad("30000");
for (int second = 0;; second++) {
if (second >= 60) fail("timeout");
try { if ("Formation".equals(selenium.getText("css=h1.title"))) break; } catch (Exception e) {}
Thread.sleep(1000);
}

selenium.click("//img[@title='Détails formation agile']");
selenium.waitForPageToLoad("30000");
}
}

A l’image de Selenium-Server, SauceConnect est une api proposée par SauceLabs afin de faire communiquer les requêtes http de l’api de test avec le cloud. L’API est disponible ici.

Lancement du server.

java -jar Sauce-Connect.jar ${login} ${key} -P 4444

Dès lors, la connexion avec le cloud est effective sur le port 4444. Il ne reste plus qu’à lancer les tests avec le launcher de TestNG ou tout autre framework de test (suivant l’export).

Résultats :

Il est possible d’accéder aux résultats au travers du site (qui nécessite des identifiants de connexion)

Figure 4 : Liste des tests effectués

Sauce Labs conserve les tests ayant déjà été exécutés.

Il permet de visualiser la commande Selenium ayant échouée mais aussi d’accéder à un enregistrement vidéo du test :

Figure 5 : Ecran présentant le résultat d’un test

Conclusion

L’utilisation des tests Selenium au format HTML est une solution très simple à mettre en oeuvre. Elle impose cependant plus de configuration pour chacun des environnements sur lesquels ils doivent s’exécuter. Nous le verrons dans le prochain article, ce ne sont pas les plus propices à la mise en place de procédures de retour arrières (cf Selenium, allons plus loin 2/2 prochainement à venir).

Les tests au format Java autorisent la création d’un framework qui permettra d’exécuter lors du tearDown ou Setup une procédure de retour arrière, mais encore une fois nous verrons cela dans un futur article. Concernant le déploiement sur des plateformes diverses, ce mode de fonctionnement le permet plus facilement. En effet; il est tout a fait envisageable de créer un bash ou bien même des properties qui définiront la configuration d’environnements sur lesquels nous souhaitons exécuter le test. L’industrialisation s’en trouvera facilitée.

SauceLab propose des environnements de test sur lesquels se lance un SeleniumServer (mode de fonctionnement similaire à celui de Selenium Hub), il est donc envisageable de mettre en place ce mode de fonctionnement par soi-même et exécuter. Cependant, cela implique la mise en place d’environnements dédiées et hétérogènes. Les tests n’étant pas lancés toutes les minutes, je ne pense pas qu’il soit utile de mobiliser un serveur ou encore une VM (espace server) pour des tests Selenium.

Nombre de vue : 687

COMMENTAIRES 4 commentaires

  1. Grunenwald dit :

    Assez complet et bien détaillé !
    La procédure demande un peu de temps à mettre en place, mais ensuite on se trouve dans un bon cadre de développement 🙂

  2. Antoine Berthelin dit :

    Bon article Pierre-Yves, j’attends la suite man !

  3. Guy Talom dit :

    Très Bonne idée de faire un retour d’expérience sur un sujet pareil.
    Personnellement j’ai toujours entendu parler de Selenium, lorsqu’on cite des exemples d’outils de tests fonctionnels, mais c’est encore mieux de voir un cas pratique comme cela.

  4. […] une première partie (Selenium, allons plus loin 1/2), j’ai eu l’occasion de vous  présenter la mise en place de l’industrialisation […]

AJOUTER UN COMMENTAIRE