Automatiser les tests Selenium avec Maven

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] [sql:execute {execution: insert-data}]
[INFO] [jetty:run {execution: start-jetty}]

[INFO] [failsafe:integration-test {execution: integration-test}]

[INFO] [selenium:stop-server {execution: stop}]
[INFO] [sql:execute {execution: drop-data}]
[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.

Publié par

Commentaire

4 réponses pour " Automatiser les tests Selenium avec Maven "

  1. Publié par , Il y a 9 ans

    Un bon outil pour réaliser facilement des tests d’intégration d’application web conjointement à selenium est le mycila jetty testing plugin http://code.google.com/p/mycila/wiki/PluginJetty

    Il permet à l’aide d’une simple annotation de déployer dans Jetty le WAR packager par maven.

  2. Publié par , Il y a 9 ans

    Bonjour,

    J’ai récemment travaillé sur l’exécution automatique de batteries de tests d’intégrations continues avec Maven.

    La configuration et le démarrage de l’application à tester (la fameuse AUT) ont bien été délégués à Maven, mais je trouvais le plugin Selenium très limité.

    En effet, celui-ci ne fait que démarrer une instance de Selenium RC.
    Or ce démarrage ne correspond qu’à une seule ligne de code Java à mettre dans le @BeforeClass d’un test case.

    En utilisant la solution Java, les tests peuvent de plus être exécutés avec le plugin JUnit d’Eclipse sans recourir à Maven, sur une version de développement tournant en local.

    Je trouvais donc la solution programmatique plus flexible, notamment par l’utilisation conjointe de Spring IOC pour la configuration de Selenium, qui permet entre autres :
    – Le choix entre le démarrage d’un serveur RC ou de l’utilisation de web drivers (masqué par une couche d’abstraction),
    – Le choix du port d’écoute de Selenium RC (avec mécanisme de fallback si port par défaut occupé),
    – Le lancement de chaque test sur différents navigateurs (« Write once, run everywhere ») par l’écriture d’un TestRunner personnalisé et l’instanciation dynamique de serveur RC.

  3. Publié par , Il y a 9 ans

    @amertum: Merci pour cette information.

    Je ne connaissais pas Mycila. De manière plus générale, Mycila est une boîte à outils, qui permet d’intégrer facilement plusieurs frameworks pour les tests comme: JMock, EasyMock, Mockito, Guice, Spring, DB, etc.
    Il existe dans le même genre l’outil: Unitils.

    @Adrien Nortain :

    En effet, démarrer Selenium RC de manière programmatique évite de lancer une commande maven supplémentaire et avec l’appui de Spring IOC, on peut rendre paramétrable certaines variables. En mixant cette solution avec un serveur jetty démarré de manière programmatique, comme le suggère @amertum, et l’utilisation de DBUnit, on peut imaginer une solution « full java » qui ne ferai appel à Maven que pour lancer les tests avec FailSafe.

  4. Publié par , Il y a 9 ans

    Si l’application ne se contente pas d’un simple jetty, alors utilisez le plugin Maven pour Deployit.
    En effet, il est capable de prendre en charge plusieurs types de serveurs d’applications (Weblogic, Websphere, Tomcat, JBoss…) et de bases de données (MySQL, Oracle, DB2..) répartis sur des machines locales et distantes.
    La nouvelle version (3.0) du plugin Maven pour Deployit permet de se connecter un serveur Deployit centralisé qui permet de tracer l’ensemble des applications déployées.

    Pour mémoire, ce cas d’utilisation a été montré lors de la présentation de Deployit au Paris Jug en mai dernier lors de la soirée « Share, Build & Deploy (11/05/2010) »

    Exemple :
    Deploy and run functional tests : http://tech.xebialabs.com/deployit-maven-plugin/1.3-beta-4-SNAPSHOT/examples/functional-tests.html
    Deploy and run performance tests http://tech.xebialabs.com/deployit-maven-plugin/1.3-beta-4-SNAPSHOT/examples/performance-tests.html

    Lien Officiel : http://www.xebialabs.com/apache-maven-add-on

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.