Selenium

Selenium est une suite d’outils permettant d’automatiser des interactions avec les navigateurs Web. Selenium n’est donc pas un outil complet pour automatiser des tests. Par contre, il peut très facilement s’intégrer avec des frameworks de test dans différents langages de programmation et permettre ainsi d’automatiser des tests pour des interfaces graphiques Web.

Les outils fournis par Selenium sont :

Selenium IDE

L’IDE de Selenium se présente sous la forme d’une addon pour navigateur Web. Il permet d’enregistrer des scénarios de test à partir des actions de l’utilisateur. Cette solution s’adresse avant tout à des personnes ne maîtrisant par la programmation.

Le WebDriver

Le WebDriver est un module qui permet d’interagir par programmation avec un navigateur Web. Selenium fournit une API disponible pour plusieurs langages de programmation (Java, C#, Python, JavaScript, Ruby) afin de communiquer avec un WebDriver.

Le Web Driver offre une alternative à Selenium IDE pour l’automatisation des tests d’intégration et d’acceptation impliquant un navigateur Web.

Grid

Grid permet d’exécuter les tests développer à partir de l’API du WebDriver sur plusieurs types de navigateurs Web et même sur plusieurs systèmes d’exploitation.

Selenium IDE

Selenium IDE permet d’enregistrer les actions d’un utilisateur dans un navigateur Web. Il peut être utilisé comme un outil d’automatisation de tests même pour des personnes qui ne maîtrisent pas de langage de programmation. Il peut également servir de base de travail en générant des fichiers sources de tests pour différents langages (Java, Python, C#…).

Selenium IDE a l’avantage d’une prise en main rapide (y compris pour des personnes qui ne réalisent pas de développement logiciel). Par contre, cette approche reste limitée pour la mise en place d’une vraie solution de test automatisée à l’échelle d’une application, notamment à cause d’une difficulté dans la maintenance et la lisibilité des tests.

Installation

Selenium IDE se présente comme un add-on à installer dans Chrome ou Firefox.

Chrome

Téléchargez l’addon sur https://chrome.google.com/webstore/detail/selenium-ide/mooikfkahbdckldjjndioackbalphokd

Firefox

Téléchargez l’addon sur https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/

Enregistrer des commandes

Dans un premier temps, Selenium IDE permet d’enregistrer les interactions de l’utilisateur avec un navigateur sous la forme d’une suite de commandes. Un scénario de test est donc une liste de commandes que l’on peut ensuite répéter automatiquement.

Cliquez sur l’icône de Selenium IDE dans la barre d’outils de votre navigateur pour ouvrir l’IDE. Dans l’écran d’accueil de l’IDE, choisissez Record a new test in a new project. Vous devez ensuite donner le nom de votre projet et l’URI de base du site que vous voulez tester. L’IDE ouvre une nouvelle fenêtre pour afficher le site.

../_images/selenium_ide.png

L’interface de Selenium IDE

La nouvelle fenêtre affiche un message indiquant qu’un enregistrement est en cours. Toutes les interactions avec le site depuis cette fenêtre sont immédiatement reportées dans la liste des commandes.

À tout moment, vous pouvez interrompre l’enregistrement (et le reprendre plus tard) en cliquant sur l’icône rouge dans l’IDE.

À gauche de l’IDE, vous avez la liste des tests. Vous pouvez ainsi faire plusieurs enregistrements différents correspondant à plusieurs scénarios de test différents.

Vous pouvez rejouer un test grâce aux boutons play.

../_images/selenium_ide_enregistrement.png

Le résultat d’un enregistrement

Notion de commande

L’interaction avec le navigateur est représentée par des commandes. Une commande à un nom, une cible (target) et une valeur associée (value). La valeur est un champ optionnel utilisé par certaines commandes.

Par exemple, la commande type permet de simuler la saisie au clavier. La cible indique l’élément dans la page qui reçoit la saisie. Pour référencer un élément dans une page vous pouvez :

  • fournir son ID HTML sous la forme id=mon_id

  • fournir le nom d’un champ de formulaire sous la forme name=nom_de_l_input

  • fournir un sélecteur CSS sous la forme css=div>p

  • fournir un chemin XPath sous la forme xpath=//div/p

Pour la commande type, la valeur correspond à la valeur saisie par l’utilisateur. Ainsi pour simuler la saisie d’un champ de formulaire dont le nom est adresse, il est possible d’utiliser :

commande

cible

valeur

type

name=adresse

12 rue de là-bas

Note

La documentation des commandes est fournie par l’outil dans l’onglet Reference en bas.

Vous pouvez également vous reporter à la documentation officielle :

Finaliser un test

Une fois l’enregistrement terminé, le test n’est pas finalisé. Il est nécessaire de faire une passe sur les commandes enregistrées pour :

  • supprimer les commandes inutiles

  • ajouter des assertions

L’enregistreur va créer des commandes qui ne sont pas strictement utiles à votre test. Il est conseillé de repasser sur toutes les commandes créées et de supprimer celles qui ne sont pas strictement indispensables. En effet, ces commandes pourraient faire échouer plus tard un test suite à des évolutions d’IHM. Elles concourent donc à fragiliser les tests.

Les tests créés avec Selenium IDE doivent, comme tous les tests automatisés, être auto-validant. Cela signifie qu’ils doivent comporter des assertions pour vérifier l’état du système. Les assertions ne peuvent pas être ajoutées automatiquement par l’enregistreur. Il nous faut donc ajouter nous-mêmes des commandes d’assertion avec l’IDE. Avec un clic droit dans la liste des commandes, vous pouvez insérer une nouvelle commande et choisir parmi la liste des assertions.

../_images/selenium_ide_assertions.png

Le test après suppression des commandes inutiles et ajout des assertions

Sauver les tests

Les tests peuvent être sauvés dans un fichier avec l’extension .side. Vous pouvez charger ces fichiers dans Selenium IDE pour les exécuter à nouveau mais vous pouvez aussi exécuter vos tests à partir de la ligne de commandes (ce qui les rend exécutables dans le cadre d’un processus d’intégration continue). Pour cela, vous aurez besoin de l’application selenium-side-runner et de un ou plusieurs WebDrider (Cf. ci-dessous). Pour en savoir plus, vous pouvez vous reporter à la documentation officielle :

Création de suites de tests

Vous pouvez regrouper les tests en suites. Par exemple, vous pouvez faire une suite par chaque macro fonctionnalité de votre application. Dans la fenêtre gauche de l’IDE, vous pouvez passer de la vue des tests à la vue d’exécution mais également à la vue des suites.

../_images/selenium_ide_suite.png

La gestion des suites dans Selenium IDE

La création de suites permet d’organiser les scénarios de test et, éventuellement, d’exporter plusieurs tests en même temps.

Bonnes pratiques

Selenium IDE permet de créer des tests d’acceptation pour des interfaces Web. Mais pour que ces tests soient exploitables, ils doivent rester maintenables et évolutifs. En effet, si telle ou telle fonctionnalité évolue, certains tests devront évoluer en conséquence. Il est courant pour les développeurs de devoir reprendre et modifier des tests existants.

Pour qu’un test créé avec Selenium IDE ne soit pas trop complexe à maintenir, il faut donc qu’il reste compréhensible au lecteur. Pour cela, chaque commande peut être documentée grâce au champ description.

Il faut aussi garder à l’esprit que la complexité d’un test est parfois liée à la complexité du système à tester. Pour qu’une page Web soit facilement exploitable par Selenium IDE, il faut que les développeurs respectent au mieux certaines pratiques au moment de création de la page elle-même :

  • identification des pages. Chaque page HTML devrait avoir un marqueur permettant de l’identifier (par un exemple un ID sur la balise <html>). Cela permet de créer des assertions efficaces pour savoir si l’utilisateur se trouve bien sur une page donnée.

  • identification de chaque élément important indépendamment du rendu. Les champs de formulaire, les messages d’erreur ou d’information devraient également être identifiables facilement avec un attribut ID. Même s’il est possible d’employer des sélecteurs CSS et des chemins XPath avec Selenium IDE, ils peuvent rendre le test dépendant de la structure de la page. Dans ce cas, le test peut être fragilisé par une simple modification cosmétique de la page.

  • ajout de méta-informations. Il ne faut pas hésiter à utiliser des méta-informations (comme les attributs data-* HTML) pour fournir les informations de contexte qui peuvent être nécessaires à l’extraction de l’information par les tests.

Web Driver

Le Web Driver est un module permettant de communiquer avec un navigateur Web par programmation afin de lui passer des commandes et d’avoir accès au contenu de la page affichée. Avec le Web Driver, nous pouvons implémenter directement des tests en utilisant un navigateur Web.

L’avantage du Web Driver est qu’il est indépendant du langage de programmation utilisé. On trouve ainsi des API pour différents langages : Java, Python, C#, JavaScript. Le Web Driver a fait l’objet d’une recommandation W3C pour l’intégration dans les navigateurs Web. Ainsi, le Web Driver n’est plus fourni par Selenium mais par chaque développeur de navigateur Web. L’avantage est la garantie d’une mise à jour permanente du Web Driver. L’inconvénient est qu’il est nécessaire d’installer le Web Driver à part.

Installation du Web Driver pour Chrome

Un Web Driver se présente sous la forme d’un exécutable. Il faut donc qu’il soit installé sur la machine de test et qu’il soit présent dans le chemin des exécutables du système.

Par exemple, pour le Web Driver de Google Chrome, vous devez commencer par télécharger l’archive depuis :

Dézippez l’archive téléchargée sur votre disque. Vous devez ensuite modifier vos variables d’environnement pour vous assurer que l’exécutable est accessible directement.

Note

Pour télécharger un Web Driver pour un autre navigateur, consultez la documentation de Selenium :

Windows

Pour Windows, si vous avez placé l’exécutable chromedriver.exe dans le répertoire C:\webdriver, vous devez modifier la variable d’environnement PATH afin d’ajouter le chemin C:\webdriver.

MacOs

Pour MacOs, si vous avez placé l’exécutable chromedriver dans le répertoire /Applications/webdriver , vous devez modifier la variable d’environnement PATH. Pour cela, il vous faut éditer le fichier .bash_profile dans votre répertoire utilisateur :

touch .bash_profile
open -e .bash_profile

Et ajouter à la fin du fichier :

export PATH=/Applications/webdriver:$PATH

Note

Si vous utilisez zsh plutôt que bash, vous devez modifier le fichier .zprofile et non pas le fichier .bash_profile.

Linux

Pour Linux, si vous avez placé l’exécutable chromedriver dans le répertoire /opt/webdriver, vous devez modifier la variable d’environnement PATH. Pour cela, si vous utilisez Bash, il vous faut éditer le fichier .bashrc :

touch ~/.bashrc
xdg-open ~/.bashrc

Et ajouter à la fin du fichier :

export PATH=/opt/webdriver:$PATH

Note

Vous pouvez également vérifier s’il n’existe pas un paquet fourni directement par votre distribution pour le navigateur Chromium. Pour Ubuntu, le paquet s’appelle chromium-chromedriver.

Utilisation d’un binding

Pour interagir avec le Web Driver dans un de vos programmes, vous devez disposer d’un binding. Un binding est une bibliothèque de programmation qui vous permet d’envoyer des commandes. Actuellement, il existe des bindings pour les langages de programmation Java, Python, C#, Ruby, Javascript et Kotlin.

Exemples d’implémentation en Java

Les exemples présentés ci-dessous sont implémentés en Java. Vous pouvez les tester vous-même à partir du projet Maven que vous pouvez télécharger directement : selenium-demo.zip.

Les fichiers sources sont dans src/test/java. Le fichier pom.xml inclus les dépendances pour tous les Web drivers. Vous pouvez ainsi facilement remplacer le driver Chrome (utilisé dans les exemples) par celui de votre choix.

En Java, un binding est simplement un ensemble de bibliothèques Java qui peuvent être déclarées comme dépendances dans un projet Maven. Il existe un binding par navigateur (afin de prendre en compte certaines spécificités). Dans un projet Maven, vous devez déclarer autant de dépendances que de navigateur que vous souhaitez tester :

navigateur

artifactId

groupId

Chrome

org.seleniumhq.selenium

selenium-chrome-driver

Firefox

org.seleniumhq.selenium

selenium-firefox-driver

Edge

org.seleniumhq.selenium

selenium-edge-driver

Opera

org.seleniumhq.selenium

selenium-opera-driver

Safari

org.seleniumhq.selenium

selenium-safari-driver

Internet Explorer

org.seleniumhq.selenium

selenium-ie-driver

Si vous souhaitez intégrer l’utilisation du Web Driver, vous pouvez déclarer dans votre projet Java une dépendance avec JUnit, une dépendance avec les classes communes de Selenium et une dépendance au binding du navigateur qui vous intéresse :

Exemple de dépendances pour des tests JUnit avec Chrome
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-support</artifactId>
        <version>3.141.59</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-chrome-driver</artifactId>
        <version>3.141.59</version>
        <scope>test</scope>
    </dependency>
</dependencies>

En Java, le binding fournit une classe pour représenter le Web Driver d’un navigateur spécifique :

navigateur

class du WebDriver

Chrome

org.openqa.selenium.chrome.ChromeDriver

Firefox

org.openqa.selenium.firefox.FirefoxDriver

Edge

org.openqa.selenium.edge.EdgeDriver

Opera

org.openqa.selenium.opera.OperaDriver

Safari

org.openqa.selenium.safari.SafariDriver

Internet Explorer

org.openqa.selenium.ie.InternetExplorerDriver

Toutes ces classes héritent de RemoteWebDriver et implémentent plusieurs interfaces dont l’interface WebDriver. À part au moment de l’initialisation, il est conseillé d’utiliser des références sur les interfaces pour permettre de rendre le code facilement adaptable pour tous les navigateurs.

Ci-dessous, un exemple de manipulation du navigateur Chrome dans un test JUnit :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package dev.gayerie.selenium;

import static org.junit.Assert.*;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

public class DuckDuckGoReferencementAcceptanceTest {

  private WebDriver webDriver;

  @Before
  public void createWebDriver() {
    webDriver = new ChromeDriver();
  }

  @After
  public void closeWebDriver() {
    webDriver.quit();
  }

  @Test
  public void checkSeleniumDevSiteFoundInFirstPageOfDuckduckgo() throws Exception {
    // On précise l'URI sur la barre d'adresse du navigateur
    webDriver.navigate().to("https://duckduckgo.com/");
    // On extrait des références sur les éléments de la page :
    // le champ de saisie pour la recherche et le bouton pour lancer la recherche
    WebElement searchInput = webDriver.findElement(By.name("q"));
    WebElement searchButton = webDriver.findElement(By.id("search_button_homepage"));

    // On remplit le champ de saisie et on clique sur le bouton de recherche
    searchInput.sendKeys("selenium");
    searchButton.click();

    // On vérifie que dans la page dans laquelle on se situe
    // (normalement la page des résultats de la recherche),
    // on trouve bien des liens vers le site voulu.
    List<WebElement> resultLinks = webDriver.findElements(By.partialLinkText("https://www.selenium.dev"));
    assertFalse("Aucun lien trouvé pour selenium.dev", resultLinks.isEmpty());
  }

}

Aux lignes 17 à 20, nous déclarons une méthode qui doit créer une instance du driver. Nous plaçons l’objet créé dans un attribut de type WebDriver. Ainsi nous ne sommes pas dépendants du type du driver et nous pourrons facilement adapter le code pour un autre type de Driver (le FirefoxDriver par exemple).

Aux lignes 22 à 25, nous déclarons une méthode qui doit fermer le driver après chaque test. Il est obligatoire de quitter un Web Driver lorsqu’il n’est plus utile. Pour un test unitaire, il est conseillé d’utiliser une instance de driver par test afin de garantir l’indépendance des tests entre eux.

Aux lignes 27 à 45, on trouve le test proprement dit qui utilise le Web driver pour manipuler le navigateur Web. Les développeurs du binding Java ont essayé de produire une API la plus expressive possible. Pour d’autres exemples d’utilisation de l’API, vous pouvez vous reporter à la documentation officielle :

Gestion de l’attente

Une difficulté technique importante dans l’utilisation d’un Web Driver est que notre programme tourne parallèlement au navigateur Web qui est piloté. Dans ce contexte, il faut garder à l’esprit qu’un programme s’exécute généralement trop rapidement pour le navigateur Web. Par exemple, l’appel à la méthode findElement pour trouver un élément dans une page peut se faire alors que la page n’est même pas complètement chargée par le navigateur.

Par défaut, l’API Java adopte une stratégie de retentative. Si une page n’est pas complètement chargée, l’appel à la plupart des méthodes du driver finiront en erreur. Cette erreur est récupérée en interne par l’API qui émettra à nouveau la commande un certain nombre de fois avant de considérer que l’appel doit finir en erreur.

Cette gestion implicite de l’attente peut être remplacée par une gestion explicite en utilisant notamment la classe WebDriverWait. Pour certains tests, il est parfois nécessaire d’utiliser ce mécanisme d’attente explicite. Vous pouvez trouver des exemples de code dans la documentation officielle :

Exporter les tests Selenium IDE

Avec Selenium IDE, vous pouvez exporter les tests ou les suites vers un langage de programmation. Vous pouvez ainsi faire un export Java/JUnit pour que l’outil génère le code.

Malheureusement, cet export est difficilement exploitable dans le cadre d’un développement d’une vraie application. En effet, le code généré est très complexe et parfois même, les tests ne s’exécutent pas correctement et leur implémentation doit être systématiquement modifiée.

L’export de tests créés avec Selenium IDE peut, tout au plus, servir de transition pour l’apprentissage des Web drivers.

Page object models

Si vous vous reportez à la documentation officielle de Selenium, vous trouverez un chapitre consacré aux guidelines afin de sensibiliser les développeurs sur la qualité du code produit pour l’automatisation des tests.

En effet, si vous souhaitez utiliser Selenium pour automatiser des tests pour un produit, il faut garder à l’esprit que les tests devront évolués en même temps que les fonctionnalités de votre système. Le code des tests évolue autant que le code de l’application proprement dit au cours du cycle de vie. Conserver un code de test maintenable avec une complexité minimale fait également partie des objectifs d’une équipe qui s’engage dans l’automatisation de tests. Ainsi les critères de qualité à appliquer au code vaut également pour le code des tests. Ce n’est donc pas un hasard si on trouve dans la documentation de Selenium, plusieurs chapitres consacrés aux bonnes pratiques à respecter (et aussi aux pires pratiques à éviter) pour l’automatisation.

Parmi ces bonnes pratiques, une des plus importantes est certainement le principe du Page Object Model. Ce principe a pour objectif de rendre le code des tests plus lisible et donc plus compréhensible pour la maintenance. L’idée simple est de créer une classe par page de l’application Web. Cette classe doit proposer des méthodes qui traduisent les opérations de haut niveau que l’utilisateur peut réaliser. Par exemple, une classe représentant une page de recherche devrait avoir une méthode pour indiquer les mots-clés saisis par l’utilisateur et une méthode pour déclencher la recherche. L’objectif principal de ce modèle est d’encapsuler l’utilisation de l’API Selenium et de ne présenter dans les tests que des méthodes qui traduisent une action utilisateur. Les tests sont ainsi susceptibles d’être moins impactés par une évolution cosmétique d’une page (on se contentera d’impacter la classe représentant la page).

Reprenons notre exemple précédent et adaptons le en utilisant le principe du Page Object Model. Nous aurons besoin d’une classe représentant la page de recherche du moteur et une classe représentant la page de résultat :

La classe représentant la page de recherche
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package dev.gayerie.duckduckgo;

import static org.junit.Assert.assertTrue;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public class HomePage {
  private WebDriver webDriver;

  public HomePage(WebDriver webDriver) {
    this.webDriver = webDriver;
  }

  public HomePage open() {
    webDriver.navigate().to("https://duckduckgo.com");
    assertTrue("Titre de page inattendu " + webDriver.getTitle(),
               webDriver.getTitle().startsWith("DuckDuckGo"));
    return this;
  }

  public static HomePage openWith(WebDriver webDriver) {
    HomePage homePage = new HomePage(webDriver);
    homePage.open();
    return homePage;
  }

  public HomePage enterKeywords(String... words) {
    WebElement searchInput = webDriver.findElement(By.name("q"));
    searchInput.sendKeys(String.join(" ", words));
    return this;
  }

  public ResultPage clickOnSearch() {
    WebElement searchButton = webDriver.findElement(By.id("search_button_homepage"));
    searchButton.click();
    return new ResultPage(webDriver);
  }
}

Notez que la classe HomePage fournit une méthode statique de construction (factory method) et toutes les méthodes retournent un objet représentant une page. Cela permet le chaînage d’appel (fluent pattern) pour améliorer la lisibilité des tests. Avec les méthodes enterKeyworks et clickOnSearch, cette classe présente dans une API de haut-niveau toutes les opérations utilisateur nécessaires à la rédaction des tests. Un objet de cette classe est construit en passant un WebDriver en paramètre.

La classe représentant la page de résultats
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package dev.gayerie.duckduckgo;

import static org.junit.Assert.assertTrue;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;

public class ResultPage {
  private WebDriver webDriver;

  public ResultPage(WebDriver webDriver) {
    this.webDriver = webDriver;
    assertTrue("Titre de page inattendu " + webDriver.getTitle(),
               webDriver.getTitle().endsWith("at DuckDuckGo"));
  }

  public boolean isLinkPresent(String link) {
    return ! webDriver.findElements(By.partialLinkText(link)).isEmpty();
  }
}

La classe ResultPage se crée en passant une instance de WebDriver en paramètre. Elle vérifie à la construction que la page courante est bien la page de résultat (en vérifiant le titre de la page). Cela permet au test de détecter au plus tôt des problèmes de navigation lorsque la page courante n’est pas la page attendue.

Note

Lorsqu’on développe un site destiné à être testé automatiquement, il est important de faire apparaître un identifiant unique pour chaque page afin de permettre au programme de test de vérifier la page courante de manière non ambiguë.

La classe de test
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package dev.gayerie.duckduckgo;

import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class DuckDuckGoReferencementAcceptanceTest {
  private WebDriver webDriver;

  @Before
  public void createWebDriver() {
    webDriver = new ChromeDriver();
  }

  @After
  public void closeWebDriver() {
    webDriver.quit();
  }

  @Test
  public void checkSeleniumDevSiteFoundInFirstPageOfDuckduckgo() throws Exception {
    ResultPage resultPage = HomePage.openWith(webDriver)
                                    .enterKeywords("selenium")
                                    .clickOnSearch();

    assertTrue(resultPage.isLinkPresent("https://www.selenium.dev"));
  }
}

Le test écrit en utilisant le Page Object Model est beaucoup plus compact et beaucoup plus simple à lire (et donc à maintenir). À part l’initialisation et la fermeture du WebDriver, cette classe ne contient aucune référence à l’API Selenium et se concentre sur les interactions utilisateur.

Exécuter les tests Selenium à la phase integration-test de Maven

Les tests automatisés avec Selenium ne sont pas des tests unitaires. Ils supposent qu’une application Web soit déployée quelque part. Il s’agit donc soit de tests d’intégration destinés à valider le bon déploiement, soit de tests d’acceptation destinés à valider les fonctionnalités de l’application. Pour les différencier des tests unitaires, il est possible de les exécuter à la phase integration-test du cycle de vie de Maven. Cette phase est située après la phase package et, comme son nom l’indique, est réservée au lancement de tests d’intégration. Par défaut, aucun plugin ne s’exécute à cette phase, il faut donc configurer le lancement d’un plugin dans le fichier pom.xml du projet.

La façon la plus simple consiste à utiliser le plugin maven-surefire-plugin qui permet d’exécuter les tests JUnit. Pour cela, on définit une règle permettant de différencier les classes de tests unitaires et les classes de tests avec Selenium. Par exemple, ces dernières peuvent avoir un nom se terminant par IntegrationTest ou AcceptanceTest. On configure alors le plugin maven-surefire-plugin pour ignorer les classes dont le nom se termine par IntegrationTest ou AcceptanceTest lors de la phase de test et à lancer uniquement ces classes lors de la phase integration-test :

Configuration du maven-surefire-plugin pour le lancement à la phase integration-test
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
    <configuration>
        <excludes>
            <exclude>**/*IntegrationTest.java</exclude>
            <exclude>**/*AcceptanceTest.java</exclude>
        </excludes>
    </configuration>
    <executions>
        <execution>
            <id>integration-test</id>
            <goals>
                <goal>test</goal>
            </goals>
            <phase>integration-test</phase>
            <configuration>
                <excludes>
                    <exclude>none</exclude>
                </excludes>
                <includes>
                    <include>**/*IntegrationTest.java</include>
                    <include>**/*AcceptanceTest.java</include>
                </includes>
            </configuration>
        </execution>
    </executions>
</plugin>

Dans le cycle de vie de Maven, la phase integration-test est encadrée par les phases pre-integration-test et post-integration-test. Il est donc facile de réaliser le déploiement de l’application sur un serveur lors de la phase pre-integration-test et, éventuellement, d’effectuer des tâches de nettoyage lors de la phase post-integration-test.

Pour exécuter les tests Selenium, il suffit de lancer la commande Maven :

mvn integration-test

Note

Cette approche permet de conserver les fichiers sources des tests d’intégration et d’acceptation dans le même projet que le code source de l’application et des tests unitaires. Ce scénario fonctionne pour une solution logicielle assez simple qui se résume à une seule application Web. Pour des solutions plus complexes impliquant plusieurs modules Web (et donc plusieurs projets Maven), il est plus simple de concevoir un projet Maven indépendant dédié uniquement à l’implémentation des tests Selenium.