- Blog Xebia France - http://blog.xebia.fr -

Automatiser les tests Selenium avec Maven

Posted By Romain Schlick On Vendredi 18 février 2011 @ 10:33 In Java / JEE,Tests | 4 Comments

selenium

Selenium regroupe une suite d’outils permettant de tester des applications web. Tout comme les tests unitaires, Selenium permet notamment de vérifier la non-régression d’une application et est un gage de qualité supplémentaire. Bien que la création des tests Selenium soit relativement simple, automatiser leur exécution sur un serveur d’intégration continue reste complexe à mettre en œuvre. Je vous propose une solution avec l’outil de build Maven. Disposant de nombreux plugins, comme SQL, Failsafe, Jetty et Selenium, Maven permet la mise en place d’une automatisation satisfaisante. Cette solution peut servir aussi aux tests d’intégration.

Selenium, un puissant outil pour créer des tests fonctionnels

Les tests Selenium reproduisent toutes sortes d’interactions qu’un utilisateur peut effectuer sur un navigateur web. Un test Selenium est constitué d’un script pouvant être écrit dans plusieurs langages (Java, Html, C#, Groovy, Php, etc.). Ce script est ensuite interprété par le serveur Selenium RC, qui s’occupe d’envoyer les bonnes commandes au navigateur. Les avantages de Selenium sont d’être compatible avec de nombreux navigateurs (Internet Explorer, Firefox, Chrome, etc.) et d’offrir un outil de création de test, appelé Selenium IDE. Ce dernier est disponible sous forme d’un plugin Firefox. Les scripts Selenium sont relativement simples à coder directement, puisqu’il s’agit à chaque fois d’indiquer le type d’interaction (clic, saisie, etc.), puis de sélectionner l’élément en jeu (lien, tableau, div, input, etc.), et enfin d’indiquer une éventuelle valeur. Cependant, on aurait souhaité la disponibilité de Selenium IDE sur d’autres navigateurs.

Vers une automatisation des tests Selenium avec Maven

L’exécution automatique des tests unitaires sur un serveur d’intégration continue est une pratique très répandue. En utilisant une base de données en mémoire, comme HSQLDB, les tests unitaires peuvent être exécutés facilement quel que soit l’environnement. De la même manière, il est intéressant de pouvoir lancer automatiquement ces tests fonctionnels sur un serveur d’intégration. Cependant, la mise en place est plus complexe: à chaque exécution, il faut au préalable s’assurer que la base de données soit dans un état cohérent (en terme de jeu de données), mais aussi que les serveurs web et Selenium RC soient démarrés.

Voici les étapes que nous allons automatiser à l’aide de l’outil Maven :

  1. Initialiser la base de données avec un jeu de données : Plugin maven SQL.
  2. Construire l’application web et la déployer sur un serveur web Jetty : Plugin maven Jetty.
  3. Démarrer le serveur Selenium RC : Plugin maven Selenium
  4. Exécuter les tests Selenium : Plugin maven Failsafe.

Comme vous pouvez le constater, cette solution repose sur l’outil de build Maven et le serveur web Jetty. Dans un précédent article, j’avais proposé une autre solution basée sur l’outil Ant. Avant d’aller plus loin, il est important de connaître le cycle de vie du build de Maven. Voici l’ordre d’exécution des principales phases du build :

  • validate
  • compile
  • package
  • test
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify
  • install
  • deploy

Lorsqu’on lance une phase, toutes les phases qui la précèdent sont exécutées au préalable. La phase test exécute les tests unitaires. Il existe aussi une phase integration-test qui va nous servir à exécuter les tests Selenium. Cette phase est précédée de la phase pre-integration-test et suivie de la phase post-integration-test qui vont respectivement nous aider à préparer puis nettoyer l’environnement d’exécution : démarrage et arrêt des serveurs Web et Selenium RC typiquement. Pour lancer les tests Selenium, il est nécessaire de lancer la commande mvn verify et non mvn integration-test, sinon la phase post-integration-test ne sera pas exécutée.

Je vais maintenant aborder l’intégration de chacune des briques (Selenium, base de données, et serveur web) dans Maven.

Intégration de Selenium dans Maven

L’intégration de Selenium dans Maven se décompose en deux étapes :

  • Déclarer l’exécution des tests Selenium via le plugin Failsafe.
  • Démarrer le serveur Selenium RC en pre-integration-test et l’arrêter en post-integration-test.

Solution 1: Exécuter les tests avec le plugin Surefire

Pour intégrer l’exécution des tests Selenium, il existe une première solution basée sur le plugin Surefire. La fonction première de ce plugin est d’exécuter les tests unitaires lors de la phase test et de générer un rapport. L’idée ici est de considérer les tests Selenium, comme des tests unitaires JUnit. Pour inclure les tests Selenium, il faut définir un plan d’exécution surefire-integration-test associé à la phase integration-test en incluant dedans les classes de test Selenium. Par ailleurs, il faut exclure les tests Selenium de la phase test (dans <configuration>). On obtient alors la déclaration du plugin suivant dans le pom :

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-surefire-plugin</artifactId>
   <configuration>
      <includes>
         <!-- Inclure les tests unitaires ici ... -->
      </includes>
      <excludes>
         <exclude>com/xebia/selenium/**/*Test.java</exclude>  <!-- Exclure les tests Selenium ici ... -->
      </excludes>
   </configuration>
   <executions>
      <execution>
         <id>surefire-integration-test</id>
         <phase>integration-test</phase>
         <goals>
            <goal>test</goal> <!-- La phase integration-test va lancer les tests... -->
         </goals>
         <configuration>
            <skip>false</skip>
            <includes>
               <include>com/xebia/selenium/**/*Test.java</include> <!-- ... Inclure les tests Selenium ici -->
            </includes>
         </configuration>
      </execution>
   </executions>
</plugin>

La solution Surefire n’est cependant pas optimale. En effet, Surefire a été conçu à l’origine uniquement pour les tests unitaires. Ce qui explique que si un test échoue, le build maven s’arrêtera à la phase integration-test, sans aller jusqu’au post-integration-test. Du coup, l’environnement d’exécution ne sera pas correctement nettoyé.

Solution 2 recommandée: Exécuter les tests avec le plugin FailSafe

Pour corriger les limitations de Surefire, un nouveau plugin FailSafe dédié aux tests d’intégration a été créé. Ce plugin est un fork de Surefire, qui ne bloque pas le cycle de vie du build en cas d’échec. En lançant les tests Selenium avec mvn verify et non mvn integration-test, on s’assure que la phase post-integration-test sera bien exécutée en cas d’erreurs. Le rôle de la phase verify est donc clairement de vérifier l’état d’exécution des tests d’intégration. Voici comment déclarer le plugin FailSafe dans le pom :

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-failsafe-plugin</artifactId>
   <version>2.7.2</version>
   <configuration>
      <reportsDirectory>${basedir}/target/surefire-reports</reportsDirectory>
      <includes>
         <include>com/xebia/selenium/**/*.java</include> <!-- ... inclure les tests Selenium -->
      </includes>
      <systemPropertyVariables>
         <jetty.port>${jetty.port}</jetty.port>
         <jetty.context>${artifactId}</jetty.context>
      </systemPropertyVariables>
   </configuration>
   <executions>
      <execution>
         <id>integration-test</id>
         <goals>
            <goal>integration-test</goal>
         </goals>
      </execution>
      <execution>
         <id>verify</id>
         <goals>
            <goal>verify</goal>
         </goals>
      </execution>
   </executions>
</plugin>

Afin de rendre paramétrable certaines valeurs de contexte dans vos classes de tests Selenium, comme le port et le contexte de l’application, il est possible d’indiquer des propriétés systèmes dans le plugin FailSafe. Pour cela, il suffit d’ajouter l’élément <systemPropertyVariables> dans <configuration> avec les différentes propriétés.
On remarque aussi la propriété <reportsDirectory> qui permet de générer les rapports de tests dans le même dossier que Surefire. Le serveur d’intégration inclura ainsi les résultats des tests Selenium avec ceux des tests unitaires. Une autre solution est de définir l’emplacement des rapports directement sur le serveur d’intégration continue. Par défaut, FailSafe place les rapports dans basedir/target/failsafe-reports.

Ajout du plugin Selenium (Pour démarrer et arrêter le serveur Selenium RC)

Afin de lancer automatiquement le serveur Selenium RC pendant l’exécution de la phase integration-test, il faut ajouter le plugin suivant dans le pom :

<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>

L’option <background>true</background> permet de ne pas bloquer le processus Maven pendant que le serveur Selenium RC est démarré.

Configuration du serveur web avec le plugin Jetty

Préalablement à l’exécution des tests Selenium, il est nécessaire de démarrer l’application sur un serveur web durant la phase pre-integration-test. Le serveur sera arrêté durant la phase post-integration-test. Voici la manière de déclarer le plugin Jetty dans le pom :

<plugin>
   <groupId>org.mortbay.jetty</groupId>
   <artifactId>maven-jetty-plugin</artifactId>
   <version>6.1.10</version>
   <configuration>
      <scanIntervalSeconds>10</scanIntervalSeconds>
      <stopKey>foo</stopKey>
      <stopPort>9999</stopPort>
      <webAppSourceDirectory>${webapp.dir}</webAppSourceDirectory>
      <connectors>
         <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
            <port>${jetty.port}</port>
            <maxIdleTime>60000</maxIdleTime>
         </connector>
      </connectors>
   </configuration>
   <executions>
      <execution>
         <id>start-jetty</id>
         <phase>pre-integration-test</phase>
         <goals>
            <goal>run</goal>
         </goals>
         <configuration>
            <scanIntervalSeconds>0</scanIntervalSeconds>
            <daemon>true</daemon>
         </configuration>
      </execution>
      <execution>
         <id>stop-jetty</id>
         <phase>post-integration-test</phase>
         <goals>
            <goal>stop</goal>
         </goals>
      </execution>
   </executions>
</plugin>

L’attribut <webAppSourceDirectory> permet d’indiquer le chemin de l’application web. On peut aussi remarquer l’utilisation d’une propriété jetty.port pour mettre en paramètre le port d’écoute de Jetty. Celle-ci se déclare de la manière suivante dans le pom.xml :

<properties>
   <jetty.port>9090</jetty.port>
   ......
</properties>

Manipulation de la base de données

Il existe de nombreuses solutions pour préparer la base de données aux tests d’intégration. Même s’il est possible d’utiliser une base de données en mémoire, comme HSQLDB, je recommanderais plutôt d’utiliser le même type de base qu’en production, parce que nous exécutons des tests end-to-end. Pour insérer les jeux de données, il est possible d’utiliser deux plugins Maven : SQL ou DbUnit. J’ai privilégié l’utilisation du plugin SQL, car mes scripts étaient déjà écrits en SQL. En effet, DbUnit utilise son propre format en XML.
Le plugin SQL permet aussi d’exécuter des procédures stockées et des fonctions en changeant le type de délimiteur, qui est par défaut ‘;’. Dans <configuration>, on définit le driver, ainsi que les paramètres de connexion. J’ai ajouté une dépendance au driver d’Oracle dans mon exemple. Le paramètre <autocommit> permet d’indiquer si l’on veut que les scripts soient exécutés de manière transactionnelle ou non.
Ensuite, il suffit de déclarer un plan d’exécution ‘insert_data’ pour la phase pre-integration-test et un autre ‘drop_data’ pour la phase post-integration-test en indiquant les scripts SQL.

<plugin>
   <groupId>org.codehaus.mojo</groupId>
   <artifactId>sql-maven-plugin</artifactId>
   <version>1.4</version>
   <dependencies>
      <dependency>
         <groupId>com.oracle</groupId>
         <artifactId>ojdbc14</artifactId>
         <version>10.2.0.4</version>
      </dependency>
   </dependencies>
   <configuration>
      <driver>oracle.jdbc.driver.OracleDriver</driver>
      <url>jdbc:oracle:thin:@localhost:1521:capitaineHadoc</url>
      <username>tintin</username>
      <password>milou</password>
      <delimiter>;</delimiter>
      <delimiterType>normal</delimiterType>
      <keepFormat>true</keepFormat>
      <skip>${maven.test.skip}</skip>
      <autocommit>true</autocommit>
   </configuration>
   <executions>
      <execution>
         <id>insert-data</id>
         <phase>pre-integration-test</phase>
         <goals>
            <goal>execute</goal>
         </goals>
         <configuration>
            <orderFile>ascending</orderFile>
            <fileset>
               <basedir>${basedir}/path/to/sql/files</basedir>
               <includes>
                  <include>insert-data.sql</include>
               </includes>
            </fileset>
         </configuration>
      </execution>
      <execution>
         <id>drop-data</id>
         <phase>post-integration-test</phase>
         <goals>
            <goal>execute</goal>
         </goals>
         <configuration>
            <orderFile>ascending</orderFile>
            <fileset>
               <basedir>${basedir}/path/to/sql/files</basedir>
               <includes>
                  <include>drop-data.sql</include>
               </includes>
            </fileset>
         </configuration>
      </execution>
   </executions>
</plugin>

Execution des tests Selenium

Depuis Maven

Après avoir déclaré tous les plugins dans le pom.xml, il suffit de lancer la commande suivante :

mvn verify

Voici alors une synthèse des logs que l’ont obtient :

[INFO] [selenium:start-server {execution: start}]
[INFO] 1
[INFO] [jetty:run {execution: start-jetty}]
[INFO] [failsafe:integration-test {execution: integration-test}]
[INFO] [selenium:stop-server {execution: stop}]
[INFO] 1
[INFO] [jetty:stop {execution: stop-jetty}]
[INFO] [failsafe:verify {execution: verify}]
[INFO] Failsafe report directory: D:Javasoftworkspacepid-trunktargetsurefire-reports

Depuis votre IDE

Lorsqu’on développe de nouveaux tests Selenium ou tout simplement pour débugger, il est nécessaire de les exécuter sur son IDE. Pour parvenir à cela, il suffit de lancer les commandes suivantes (dans des terminaux différents) :

  1. Préparer la base de données si nécessaire :
    mvn sql:execute
  2. Démarrer le serveur Selenium RC :
    mvn selenium:start-server
  3. Démarrer le serveur web Jetty :
    mvn jetty:run
  4. Exécuter le test depuis votre IDE.

Conclusion

Avec ses nombreux plugins, Maven est un puissant outil pour automatiser l’exécution des tests Selenium. Les solutions proposées dans cet article peuvent servir de manière générale aux tests d’intégration. Les prochaines versions de Maven ne prévoient pas une séparation des sources entre les tests unitaires et les tests d’integration (src/main/test vs src/main/it). En effet, le plugin FailSafe permet de gérer les tests d’intégration de manière dédiée.


Article printed from Blog Xebia France: http://blog.xebia.fr

URL to article: http://blog.xebia.fr/2011/02/18/automatiser-les-tests-selenium-avec-maven/