Selenium Java Framework Setup: Step-by-Step (2026)
Selenium with Java remains the most widely used test automation stack in enterprises. While Playwright is newer and faster, Selenium's maturity, extensive documentation, and legacy system support make it indispensable.
I've set up Selenium Java frameworks on 4 projects. This guide distills lessons learned into a copy-paste-ready setup.
Prerequisites
- Java 11+ installed
- Maven 3.6+
- IDE: IntelliJ or Eclipse
Table of Contents
- Maven Project Setup
- Dependency Configuration
- WebDriver Management
- Page Object Model
- Test Configuration
- Parallel Execution
- Real Project Example
- Troubleshooting
Maven Project Setup
Create Project
mvn archetype:generate \n -DgroupId=com.automation \n -DartifactId=selenium-automation \n -DarchetypeArtifactId=maven-archetype-quickstart \n -DinteractiveMode=false
cd selenium-automation
Project Structure
selenium-automation/
├── src/
│ ├── main/
│ │ └── java/
│ │ ├── utils/
│ │ │ ├── DriverManager.java
│ │ │ └── WaitUtils.java
│ │ └── config/
│ │ └── ConfigReader.java
│ └── test/
│ ├── java/
│ │ ├── pages/
│ │ │ ├── BasePage.java
│ │ │ └── LoginPage.java
│ │ ├── tests/
│ │ │ └── LoginTest.java
│ │ └── TestBase.java
│ └── resources/
│ ├── config.properties
│ └── testng.xml
├── pom.xml
└── README.md
Dependency Configuration
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.automation</groupId>
<artifactId>selenium-automation</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<selenium.version>4.15.0</selenium.version>
<testng.version>7.8.1</testng.version>
</properties>
<dependencies>
<!-- Selenium WebDriver -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- WebDriverManager (handles driver downloads) -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.6.2</version>
</dependency>
<!-- TestNG (test runner) -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<!-- Log4j (logging) -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.21.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.21.0</version>
</dependency>
<!-- Assertions (AssertJ for fluent assertions) -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Compiler Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<!-- Surefire (runs tests in Maven) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
<parallel>methods</parallel>
<threadCount>4</threadCount>
</configuration>
</plugin>
</plugins>
</build>
</project>
WebDriver Management
DriverManager.java
public class DriverManager {
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static void initializeDriver(String browserName) {
WebDriver webDriver;
switch (browserName.toLowerCase()) {
case "chrome":
WebDriverManager.chromedriver().setup();
webDriver = new ChromeDriver(getChromeOptions());
break;
case "firefox":
WebDriverManager.firefoxdriver().setup();
webDriver = new FirefoxDriver();
break;
case "edge":
WebDriverManager.edgedriver().setup();
webDriver = new EdgeDriver();
break;
default:
throw new IllegalArgumentException("Invalid browser: " + browserName);
}
driver.set(webDriver);
}
public static WebDriver getDriver() {
return driver.get();
}
public static void quitDriver() {
WebDriver webDriver = driver.get();
if (webDriver != null) {
webDriver.quit();
driver.remove();
}
}
private static ChromeOptions getChromeOptions() {
ChromeOptions options = new ChromeOptions();
options.addArguments("--start-maximized");
options.addArguments("--disable-blink-features=AutomationControlled");
options.setExperimentalOption("excludeSwitches", new String[]{"enable-automation"});
return options;
}
}
Page Object Model
BasePage.java
public class BasePage {
protected WebDriver driver;
protected WebDriverWait wait;
protected static final Logger logger = LogManager.getLogger();
public BasePage() {
this.driver = DriverManager.getDriver();
this.wait = new WebDriverWait(driver, Duration.ofSeconds(5));
}
protected void click(By locator) {
wait.until(ExpectedConditions.elementToBeClickable(locator)).click();
}
protected void type(By locator, String text) {
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(locator));
element.clear();
element.sendKeys(text);
}
protected String getText(By locator) {
return wait.until(ExpectedConditions.presenceOfElementLocated(locator)).getText();
}
protected boolean isDisplayed(By locator) {
try {
return driver.findElement(locator).isDisplayed();
} catch (NoSuchElementException e) {
return false;
}
}
}
LoginPage.java
public class LoginPage extends BasePage {
private static final Logger logger = LogManager.getLogger();
// Locators
private final By emailField = By.id("email");
private final By passwordField = By.id("password");
private final By submitButton = By.xpath("//button[@type='submit']");
private final By errorMessage = By.className("error-message");
public void login(String email, String password) {
logger.info("Logging in with email: " + email);
type(emailField, email);
type(passwordField, password);
click(submitButton);
wait.until(ExpectedConditions.urlContains("/dashboard"));
}
public String getErrorMessage() {
return getText(errorMessage);
}
public boolean isErrorMessageDisplayed() {
return isDisplayed(errorMessage);
}
}
Test Configuration
testng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Automation Suite" parallel="methods" thread-count="4">
<listeners>
<listener class-name="listeners.TestListener"/>
</listeners>
<test name="Login Tests">
<classes>
<class name="tests.LoginTest"/>
</classes>
</test>
<test name="Dashboard Tests">
<classes>
<class name="tests.DashboardTest"/>
</classes>
</test>
</suite>
TestBase.java
public class TestBase {
@BeforeMethod
public void setUp() {
String browser = System.getProperty("browser", "chrome");
DriverManager.initializeDriver(browser);
DriverManager.getDriver().get("https://example.com");
}
@AfterMethod
public void tearDown() {
DriverManager.quitDriver();
}
}
Parallel Execution
Run Tests in Parallel
# Run with 4 threads
mvn test -Dthread-count=4
# Run specific test class
mvn test -Dtest=LoginTest
# Run with specific browser
mvn test -Dbrowser=firefox
Real Project Example
On a fintech automation project:
Setup:
- 120 E2E tests with Selenium Java
- Page Objects: 15 pages
- Parallel execution: 4 workers
Results:
- Sequential: 45 minutes → Parallel: 12 minutes (3.7x faster)
- Maintenance: 20 hours/month → 4 hours/month (Page Objects)
Troubleshooting
Issue: "ChromeDriver path not found"
- Fix: WebDriverManager automatically downloads drivers. Ensure internet access.
Issue: "StaleElementReferenceException"
- Fix: Re-find element after page change. Use explicit waits.
Issue: "ElementNotInteractableException"
- Fix: Wait for element to be clickable:
wait.until(ExpectedConditions.elementToBeClickable(locator)).click();
FAQ
Q: Selenium or Playwright?
A: Selenium for legacy systems, large enterprises. Playwright for new projects.
Q: TestNG or JUnit?
A: TestNG. Better parallel execution, more flexible.
Q: How many tests per file?
A: 5-10 per file. Organize by feature.
Q: How to handle dynamic waits?
A: Use WebDriverWait with ExpectedConditions. Never use Thread.sleep().
Tayyab Akmal
AI & QA Automation Engineer
I've caught critical bugs in fintech, e-commerce, and SaaS platforms — then built the automation that prevents them from shipping again. 6+ years scaling test automation and AI-driven QA.