Set up a Maven project for Selenium
On this page
A Selenium + TestNG project needs surprisingly little: two dependencies, a test runner plugin and a folder layout Maven understands. This chapter builds that skeleton and explains the one version choice that will trip you up in 2026.
Check your tools#
java -version # 17 or newer; this course uses 21
mvn -version # 3.9.x
Selenium Java 4.52.0 and TestNG 7.12.0 are both compiled for Java 11, but the course code uses a few newer language features (such as Stream.toList(), Java 16+), so the project compiles for Java 17. Any JDK from 17 upward works; use a current LTS (21 or 25) if you can choose. Gradle works just as well; the TestNG guide in our docs lists the Gradle coordinates if you prefer it.
Folder layout#
selenium-testng-course/
pom.xml
testng.xml
src/
test/
java/
com/example/
FirstTest.java
Everything lives under src/test/java because it is test code: Maven compiles it in the test-compile phase and runs it in the test phase. You do not need anything in src/main for a pure UI test suite.
The pom.xml#
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>selenium-testng-course</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<selenium.version>4.52.0</selenium.version>
<testng.version>7.12.0</testng.version>
<!-- default suite; override with -DsuiteXmlFile=other.xml -->
<suiteXmlFile>testng.xml</suiteXmlFile>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<!-- stay on 3.5.x: 3.6.0+ cannot run TestNG suite XML files -->
<version>3.5.6</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>${suiteXmlFile}</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>
A few choices are worth explaining.
selenium-java is the umbrella artifact. It pulls in the WebDriver API, the support classes (WebDriverWait, ExpectedConditions), the Chrome, Firefox, Edge and Safari drivers, RemoteWebDriver for grids, and Selenium Manager. You rarely need the individual modules.
<scope>test</scope> keeps both libraries off the main classpath. Since all of our code is in src/test, that is exactly right. If you later move page objects or helpers into src/main, change Selenium's scope to the default compile.
Versions in properties make upgrades a one-line change. Selenium releases often; check the selenium-java page on Maven Central and bump regularly, because new browser versions sometimes need a newer Selenium.
Why Surefire is pinned to 3.5.6#
Maven runs tests through the Surefire plugin. Starting with Surefire 3.6.0, every test framework runs through the JUnit Platform, including TestNG (via a TestNG engine). The JUnit Platform cannot read TestNG suite XML files, so the suiteXmlFiles setting is no longer supported. With 3.6.1 and the pom above, mvn test stops with:
Parameter "suiteXmlFiles" is not supported since 3.6.0: TestNG tests now run on the
JUnit Platform, which can't read TestNG suite XML files. ... or keep using
maven-surefire-plugin 3.5.x.
This course relies on testng.xml for parallelism, parameters and listeners (chapters 6-8), so we stay on 3.5.6, the last 3.5.x release. If you would rather move to 3.6, you can drop suiteXmlFiles and select tests with Surefire's groups and includes parameters instead, but you lose the suite file as the single source of truth. Check the Surefire release notes before you upgrade either way.
Drivers: there is nothing to download#
Older tutorials tell you to download chromedriver, put it on your PATH or set the webdriver.chrome.driver system property. With Selenium 4.6 and later, skip all of that. When you call new ChromeDriver(), Selenium Manager (bundled in selenium-java):
- Detects the installed Chrome version.
- Resolves the matching chromedriver version from the browser vendor's metadata.
- Downloads it once and caches it in
~/.cache/selenium. - Reuses the cached driver on later runs.
If Chrome itself is missing, Selenium Manager can also download a Chrome for Testing build into the same cache. A driver you place on the PATH yourself still takes precedence, which is a common source of "session not created: this version of ChromeDriver only supports Chrome version N" errors: an old driver on the PATH shadows the one Selenium Manager would have picked. Remove it.
On CI machines without internet access, Selenium Manager cannot download anything; either pre-install the driver or, more simply, run against a remote grid (chapter 9) where the drivers are already in place.
After Chrome auto-updated, every test fails with session not created: this version of ChromeDriver only supports Chrome version N. What is the most likely cause?
A driver you place on the PATH yourself takes precedence. Remove it and Selenium Manager resolves a matching driver for the installed Chrome.
A smoke check#
Create testng.xml in the project root. We will grow it in chapter 6:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Course suite">
<test name="First">
<classes>
<class name="com.example.FirstTest"/>
</classes>
</test>
</suite>
Then a throwaway test to prove the wiring:
package com.example;
import org.testng.Assert;
import org.testng.annotations.Test;
public class FirstTest {
@Test
public void wiringWorks() {
Assert.assertTrue(true);
}
}
mvn test
You should see Tests run: 1, Failures: 0 and a target/surefire-reports folder containing TestNG's index.html and emailable-report.html. In IntelliJ IDEA, the gutter icon next to the class or the testng.xml file runs the same thing without Maven; this IntelliJ setup guide walks through importing a Maven project if you haven't done that before.
Useful Maven commands#
mvn test # run the suite in testng.xml
mvn test -Dtest=FirstTest # one class (ignores testng.xml)
mvn test -Dtest=FirstTest#wiringWorks # one method
mvn test -DsuiteXmlFile=smoke.xml # a different suite file
mvn -q test # less noise
Note that -Dtest overrides the suite file completely: listeners and parameters declared only in testng.xml will not be applied. That is handy for a quick local run and a trap if you forget about it.
In the next chapter, FirstTest gets a real browser.
You run mvn test -Dtest=FirstTest and the screenshot listener declared in testng.xml never fires. Why?
-Dtest overrides the suite file completely. Handy for a quick run, and a trap when the suite file carries configuration.