Chapitre en accès libre
Premier script : ouvrir, lire, fermer
15 min de travail — lecture et pratique
Dans les chapitres précédents, vous avez écrit du Java qui s’exécutait dans une console. Ici, le programme sort de la console : il prend le contrôle d’un vrai navigateur, ouvre une vraie page, en lit le titre, et se ferme. C’est le moment où l’automatisation cesse d’être une idée.
Comptez quinze minutes. Les dix premières suffisent à voir Chrome se lancer tout seul ; les cinq dernières servent à comprendre pourquoi, et à ne pas laisser de processus zombie derrière vous.
Le résultat que vous devez obtenir
À la fin de ce chapitre, la commande mvn test doit afficher ces lignes, dans cet ordre, dans votre terminal :
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.stratest.tests.TitreTest
Titre lu : The Internet
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.246 s -- in com.stratest.tests.TitreTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] BUILD SUCCESS
D’autres lignes s’intercaleront chez vous : les téléchargements Maven au premier lancement, les barres [INFO] ------ autour de BUILD SUCCESS, et souvent un avertissement horodaté du type AVERTISSEMENT: Unable to find CDP implementation matching 143 — il est sans conséquence, la section « Quand ça rate » y revient.
Deux choses comptent : la ligne Titre lu : The Internet, qui prouve que votre code a réellement lu la page, et BUILD SUCCESS. Le temps écoulé sera différent chez vous, c’est normal.
Et pendant l’exécution, une fenêtre Chrome s’ouvre, charge une page, puis disparaît. C’est bref — une à trois secondes. Ne clignez pas des yeux.
Vérifier le terrain avant de commencer
Deux commandes, dans un terminal. Si l’une échoue, arrêtez-vous : le reste ne marchera pas.
java -version
mvn -v
java -version doit afficher la version 11 ou supérieure. Selenium 4.49 est compilé pour Java 11 : il ne s’exécute pas sur Java 8. mvn -v doit afficher Maven 3.x et confirmer qu’il utilise bien ce JDK.
Il faut aussi Google Chrome installé sur la machine, et une session graphique : ce chapitre ouvre une vraie fenêtre. Sur un serveur Linux sans interface graphique, ou dans une connexion SSH sans affichage, il faut le mode headless, qui n’est pas traité ici.
Pour connaître la version de Chrome :
Windows — ouvrez Chrome, saisissez chrome://version dans la barre d’adresse.
macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version
Linux
google-chrome --version
Notez ce numéro quelque part. Il servira si quelque chose casse.
Créer le projet
Si vous avez déjà un projet Maven issu des chapitres précédents, réutilisez-le et sautez directement au pom.xml. Sinon, créez l’arborescence à la main — c’est plus rapide et plus prévisible qu’un archétype Maven, qui pose des questions et génère du code dont vous n’avez pas besoin.
macOS / Linux
mkdir -p ~/road-to-qa/src/test/java/com/stratest/tests
cd ~/road-to-qa
Windows (PowerShell)
mkdir "$HOME\road-to-qa\src\test\java\com\stratest\tests"
cd "$HOME\road-to-qa"
Vous devez obtenir cette structure — Maven ne cherche les tests nulle part ailleurs :
road-to-qa/
├── pom.xml
└── src/
└── test/
└── java/
└── com/
└── stratest/
└── tests/
└── TitreTest.java
Ouvrez le dossier road-to-qa dans VS Code (Fichier → Ouvrir le dossier…).
Le pom.xml
Créez le fichier pom.xml à la racine du projet, à côté du dossier src.
<?xml version="1.0" encoding="UTF-8"?>
<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.stratest</groupId>
<artifactId>road-to-qa</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.release>11</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.49.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.14.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.16.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.6.0</version>
</plugin>
</plugins>
</build>
</project>
Quatre points méritent une explication.
| Élément | Pourquoi il est là |
|---|---|
selenium-java 4.49.0 |
L’artefact « tout-en-un » : l’API WebDriver, le pilote Chrome, Selenium Manager. Un seul ajout suffit. |
junit-jupiter 5.14.4 |
JUnit 5 en un seul artefact (API + moteur). La branche 5.x reste compatible Java 11 ; JUnit 6 exige Java 17. |
maven.compiler.release 11 |
Impose la cible Java 11, quel que soit le JDK installé sur votre poste. |
maven-surefire-plugin 3.6.0 |
À déclarer explicitement. Maven 3.6 et Maven 3.8 lient par défaut Surefire 2.12.4, qui ne connaît pas JUnit 5 : sans cette déclaration, vos tests ne sont pas exécutés et Maven affiche Tests run: 0 puis BUILD SUCCESS, sans la moindre erreur. Maven 3.9 lie une version 3.x, mais déclarer le plugin rend le projet indépendant de la version de Maven installée sur la machine. |
Vous avez peut-être vu ailleurs une dépendance webdrivermanager, ou une ligne System.setProperty("webdriver.chrome.driver", ...). Les deux étaient nécessaires avant Selenium 4.6. Elles ne le sont plus. N’en mettez pas.
Le code
Créez src/test/java/com/stratest/tests/TitreTest.java :
package com.stratest.tests;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TitreTest {
@Test
public void leTitreDeLaPageDAccueilEstCorrect() {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://the-internet.herokuapp.com/");
String titre = driver.getTitle();
System.out.println("Titre lu : " + titre);
assertEquals("The Internet", titre);
} finally {
driver.quit();
}
}
}
Onze lignes utiles. Lisez-les dans l’ordre :
WebDriver driver = new ChromeDriver();— la variable est déclarée du typeWebDriver, l’interface, et nonChromeDriver, l’implémentation. Le jour où vous passerez à Firefox, seule la partie droite change. C’est gratuit maintenant, et coûteux à rattraper plus tard.driver.get(...)— ouvre l’URL et attend que le document soit chargé avant de rendre la main. Il n’y a rien à ajouter ici, pas de pause, pas deThread.sleep.driver.getTitle()— renvoie le contenu de la balise<title>de la page, sous forme deString.assertEquals— c’est ce qui fait de ce programme un test et non un script. Sans assertion, le code ne peut pas échouer, donc il ne vérifie rien.driver.quit()dans unfinally— voir plus bas. C’est le point le plus important du chapitre.
Le nom de la classe se termine par Test. Ce n’est pas une convention esthétique : Surefire ne ramasse que les classes nommées Test*, *Test, *Tests ou *TestCase. Une classe MonPremierScript ne sera jamais exécutée.
Exécuter
Depuis la racine du projet :
mvn test
Le premier lancement télécharge Selenium, JUnit et leurs dépendances : comptez une à deux minutes selon votre connexion, et beaucoup de lignes Downloading from central. Les fois suivantes, l’exécution prend quelques secondes.
Puis Chrome s’ouvre. Puis il se ferme. Si tout s’est bien passé, le terminal se termine par BUILD SUCCESS ; sinon, la section « Quand ça rate » plus bas couvre les erreurs les plus courantes.
Ce qui vient réellement de se passer
Votre code Java ne pilote pas Chrome directement. Entre les deux, il y a un exécutable intermédiaire, chromedriver, qui parle le protocole WebDriver d’un côté et le langage interne de Chrome de l’autre. La chaîne est donc :
java -version
mvn -v0
Jusqu’à Selenium 4.5, il fallait télécharger chromedriver à la main, le déposer quelque part, et indiquer son chemin avec System.setProperty. À chaque mise à jour de Chrome, il fallait recommencer. C’est ce que décrivent les tutoriels et les supports de formation écrits avant Selenium 4.6, et que l’on trouve encore en ligne aujourd’hui.
Depuis Selenium 4.6, un composant appelé Selenium Manager est embarqué dans la bibliothèque. Au premier new ChromeDriver(), il détecte la version de Chrome installée, télécharge le chromedriver correspondant s’il ne l’a pas déjà, et le lance. Vous n’avez rien à configurer.
Vous pouvez le constater. Après votre premier mvn test réussi, regardez le cache :
macOS / Linux
ls ~/.cache/selenium/chromedriver
Windows (PowerShell)
dir "$HOME\.cache\selenium\chromedriver"
Vous y trouverez un dossier par plateforme (linux64, mac-arm64, win64…), puis un dossier par version de driver téléchargée. Ce dossier est jetable : le supprimer force simplement un nouveau téléchargement au prochain lancement. C’est d’ailleurs le premier geste de dépannage quand le driver et le navigateur se désynchronisent.
quit() et close() : la différence qui coûte cher
Les deux méthodes existent. Elles ne font pas la même chose, et la confusion entre les deux laisse des processus derrière elle.
driver.close() |
driver.quit() |
|
|---|---|---|
| Effet | Ferme la fenêtre courante du navigateur | Termine la session et arrête le processus chromedriver |
| Après l’appel | Le driver reste utilisable s’il reste d’autres fenêtres | Le driver est inutilisable ; tout appel ultérieur lève une exception |
| Processus résiduel | Oui, chromedriver reste en vie |
Non |
Ce n’est pas une nuance théorique. Un test qui se termine par close() au lieu de quit() laisse un processus chromedriver actif sur la machine, et ce processus survit à la fin du build. Un test, un processus. Cent exécutions dans la journée, cent processus. Sur un serveur d’intégration continue, où les exécutions s’enchaînent, ils s’accumulent et consomment mémoire et ports ; l’erreur qui finit par remonter peut n’avoir plus aucun rapport visible avec la cause.
Vérifiez-le vous-même après une exécution :
macOS / Linux
pgrep -l chromedriver
Windows (PowerShell)
tasklist | findstr chromedriver
Avec le code ci-dessus, cette commande ne doit rien renvoyer. Si elle renvoie quelque chose, nettoyez :
macOS / Linux
pkill -f chromedriver
Windows (PowerShell)
taskkill /F /IM chromedriver.exe /T
Retenez la règle : quit() toujours, close() seulement quand vous voulez fermer un onglet précis et continuer à travailler.
Et retenez le finally. Si assertEquals échoue, une exception est levée : sans finally, la ligne driver.quit() n’est jamais atteinte, et c’est précisément le jour où votre test échoue que vous laissez un navigateur ouvert. Le finally garantit la fermeture, succès ou échec. Vous verrez plus loin dans le parcours comment déléguer cela à @AfterEach, qui est la forme propre — mais le principe est le même.
Quand ça rate
Tests run: 0 et BUILD SUCCESS
Maven compile, ne trouve aucun test, et se déclare satisfait. Deux causes, dans cet ordre :
- Le
maven-surefire-pluginn’est pas déclaré dans lepom.xml, et votre Maven lie par défaut la version 2.12.4, qui ne connaît pas JUnit 5. C’est le cas de Maven 3.6 et de Maven 3.8. Le repère est dans la sortie du build : cherchez la ligne--- maven-surefire-plugin:2.12.4:test (default-test) ---. Si vous y lisez2.12.4, c’est la cause. - Le nom de la classe ne se termine pas par
Test, ou le fichier n’est pas soussrc/test/java.
Unable to obtain: chromedriver, error Command failed with code: 65
Le message complet ressemble à ceci :
NoSuchDriverException: Unable to obtain: chromedriver, error Command failed with code: 65,
executed "/home/vous/.cache/selenium/manager/0.4.49/selenium-manager"
with arguments [--browser, chrome, --language-binding, java, --output, json]
Selenium Manager n’a pas trouvé Chrome. Le plus souvent, Chrome n’est tout simplement pas installé sur cette machine, ou l’installation est portable et hors des emplacements standards. Installez Google Chrome depuis le site officiel, fermez puis rouvrez votre terminal, et relancez.
Si votre machine est derrière un proxy d’entreprise, l’erreur peut aussi venir d’un téléchargement bloqué. Vérifiez avec votre équipe informatique que https://googlechromelabs.github.io et https://storage.googleapis.com sont joignables.
Chrome failed to start / DevToolsActivePort file doesn’t exist
Le message a cette forme :
session not created: Chrome failed to start: exited abnormally.
(unknown error: DevToolsActivePort file doesn't exist)
(The process started from chrome location /usr/bin/google-chrome is no longer running,
so ChromeDriver is assuming that Chrome has crashed.)
Chrome n’a pas d’écran où s’afficher : machine Linux sans serveur graphique, connexion SSH sans affichage, conteneur. Ce chapitre suppose un poste avec interface graphique. Exécuter sans écran demande le mode headless, qui est traité plus loin dans le parcours.
SessionNotCreatedException avec un écart de versions
Le message a cette forme :
org.openqa.selenium.SessionNotCreatedException: Could not start a new session.
Message: session not created: This version of ChromeDriver only supports Chrome version 140
Current browser version is 143.0.7499.40 with binary path /usr/bin/google-chrome
Le driver et le navigateur ne sont plus de la même génération. Chrome se met à jour tout seul, en silence ; le driver, lui, ne suit que si on le laisse faire. Dans l’ordre :
- Cherchez un
System.setProperty("webdriver.chrome.driver", ...)dans votre code et supprimez-le. Cette ligne désactive Selenium Manager et impose un driver figé ; c’est ce qu’enseignent les supports écrits avant Selenium 4.6. - Supprimez le cache : le dossier
~/.cache/selenium(%USERPROFILE%\.cache\seleniumsous Windows). Le bon driver sera retéléchargé au prochain lancement. - Vérifiez qu’aucun vieux
chromedriverne traîne dans votrePATH:which chromedriversur macOS et Linux,where.exe chromedriverdans PowerShell sous Windows (whereseul y est un alias deWhere-Objectet ne fait pas ce que vous croyez). S’il y en a un, retirez-le. - En dernier recours, montez la version de
selenium-javadans lepom.xml.
AVERTISSEMENT: Unable to find CDP implementation matching 143
Ce message apparaît dans la console et ne bloque rien. Il signale que la version du protocole de débogage de votre Chrome ne correspond pas exactement à celles embarquées dans votre version de Selenium. Tant que votre test passe, ignorez-le : ce chapitre n’utilise aucune fonctionnalité CDP.
getTitle() renvoie une chaîne vide ou null
La page n’a pas de balise <title>, ou vous interrogez une page d’erreur affichée par Chrome (site injoignable, DNS en échec). Ajoutez temporairement System.out.println(driver.getCurrentUrl()); juste après le get pour voir où vous avez réellement atterri.
Tests run: 1, Failures: 1 avec expected: <The Internet> but was: <...>
Votre code s’est exécuté : le navigateur s’est ouvert et la page a été lue. C’est la valeur attendue qui ne correspond plus à ce que renvoie la page. Vérifiez d’abord, avec la ligne Titre lu :, que vous avez bien atterri sur la page visée ; si c’est le cas, le titre du site a changé, et il suffit de reprendre l’assertion avec la valeur affichée. Un test qui échoue pour cette raison-là est un bon test : il vous a signalé un changement.
Le site cible est indisponible
https://the-internet.herokuapp.com/ est un site public de démonstration. Il peut être lent ou momentanément hors ligne. Si c’est le cas, cela ne remet pas en cause votre installation : changez d’URL pour une page dont vous connaissez le titre, et adaptez l’assertion.
La fenêtre Chrome se ferme trop vite pour être vue
C’est le comportement normal, et c’est frustrant la première fois. Pour la regarder, insérez temporairement une pause avant le quit() — puis retirez-la. Thread.sleep n’a aucune place dans un test destiné à durer, et le chapitre sur les attentes explique pourquoi.
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version0
La méthode de test doit alors déclarer throws InterruptedException.
Ce que ce chapitre ne couvre pas
Pour être clair sur la frontière :
- Localiser et manipuler des éléments (
findElement,By, clics, saisies). Aucun élément n’a été touché ici : on s’est arrêté au titre du document. - Les attentes.
driver.get()attend le chargement de la page, mais rien n’attend qu’un élément apparaisse. Dès que vous cliquerez sur quoi que ce soit, cette question deviendra centrale. - Le cycle de vie propre.
@BeforeEach/@AfterEach, la mutualisation du driver entre plusieurs tests : ici, tout tient dans une seule méthode. - Le mode headless, l’exécution sans interface graphique, nécessaire en intégration continue et sur une machine sans écran.
- Les autres navigateurs.
FirefoxDriver,EdgeDrivers’utilisent de la même façon et Selenium Manager les gère aussi, mais rien n’a été testé ici. - Les rapports de test. Surefire écrit du XML dans
target/surefire-reports. Le rendre lisible viendra plus tard.
Avant de passer à la suite
Trois minutes, pour ancrer ce que vous venez de lire.
- Remplacez
driver.quit()pardriver.close(), relancezmvn test, puis exécutezpgrep -l chromedriver(outasklist | findstr chromedriver). Constatez le processus resté en vie alors que le build est terminé. Tuez-le, remettezquit(), relancez, vérifiez qu’il ne reste rien. - Changez la valeur attendue dans
assertEqualspour une valeur fausse. Lisez attentivement le message d’échec : c’est un message que vous relirez souvent. Remettez la bonne valeur. - Ajoutez
driver.manage().window().maximize();juste après legetet relancez, pour voir la fenêtre se redimensionner.
Si ces trois manipulations vous ont donné le résultat attendu, vous avez un environnement Selenium qui fonctionne et vous savez ce qui se passe derrière. Le chapitre suivant ouvre la vraie question du métier : comment désigner un élément précis dans une page, et pourquoi c’est beaucoup plus difficile qu’il n’y paraît.