Java for Selenium Automation: Complete Tutorial (2026)
The 2026 complete Java tutorial for Selenium automation. Step-by-step from JDK setup to OOP, collections, exception handling, TestNG, Maven, Page Object Model, and design patterns.

In this article
- 1. Why Java for Selenium Automation in 2026
- 2. Install Java and Maven Step by Step
- 3. Set Up IntelliJ IDEA for Selenium
- 4. Java Fundamentals for Testers
- 5. OOP Concepts Every Selenium Engineer Must Know
- 6. Collections You Will Use Daily
- 7. Exception Handling the Right Way
- 8. File I/O and Properties for Test Data
- 9. TestNG Framework Deep Dive
- 10. Maven for Selenium Projects
- 11. Page Object Model in Java
- 12. Design Patterns for Test Automation
- 13. Java Best Practices for Selenium in 2026
- Frequently asked questions
What you'll master: By the end of this Java for Selenium tutorial you will have a working Java 17 + Maven + TestNG + Selenium 4 setup, the OOP concepts you need to write clean test code, the collections and exception handling patterns every Selenium framework uses, and a production-grade Page Object Model with TestNG data providers and listeners.
1. Why Java for Selenium Automation in 2026
Java has been the dominant language for Selenium since 2010. In 2026 it remains the most widely used, despite competition from Python and JavaScript. Here's why:
- Mature ecosystem — TestNG, JUnit, Maven, Gradle, Spring, Lombok
- Type safety — catches bugs at compile time that Python and JS miss
- Enterprise adoption — most enterprise QA jobs list Java
- Selenium Manager — Selenium 4 auto-resolves the right driver, no driver pain
- IDE support — IntelliJ IDEA is the gold standard for Java + Selenium
Choose Java if you're targeting enterprise roles, want maximum library support, prefer type safety, or your team already uses Java. Choose Python or TypeScript for greenfield projects or faster iteration.
For a deep comparison, see our Playwright vs Selenium guide and our Playwright complete guide.
🚀 Skip the boilerplate. Practise Java + Selenium rounds live with our AI mock interview, and review Selenium interview questions before your next round.
2. Install Java and Maven Step by Step
Install JDK 17 or 21
macOS:
brew install openjdk@17
echo 'export JAVA_HOME=$(/usr/libexec/java_home -v 17)' >> ~/.zshrc
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.zshrc
source ~/.zshrc
java -versionUbuntu/Debian:
sudo apt update
sudo apt install openjdk-17-jdk
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
java -versionWindows: download the JDK 17 MSI from Adoptium and set JAVA_HOME in System Environment Variables.
Install Maven
# macOS
brew install maven
# Ubuntu/Debian
sudo apt install maven
# Windows: download from maven.apache.org, extract, add bin/ to PATH
mvn -version3. Set Up IntelliJ IDEA for Selenium
Download the Community Edition (free) from jetbrains.com/idea. Create a new Maven project (JDK 17, name selenium-java-demo), then edit pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qa.selenium</groupId>
<artifactId>selenium-java-demo</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<selenium.version>4.25.0</selenium.version>
<testng.version>7.10.2</testng.version>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>Click Maven → Reload All Maven Projects. For more on Selenium 4 itself, see our Selenium WebDriver guide.
4. Java Fundamentals for Testers
Variables, methods, and control flow
int retryCount = 3;
double timeout = 10.5;
boolean isHeadless = true;
String url = "https://example.com";
String[] browsers = {"chrome", "firefox", "edge"};
public String buildUrl(String host, String path) {
return "https://" + host + path;
}
for (String browser : browsers) {
System.out.println("Testing in " + browser);
}
try {
driver.findElement(By.id("submit")).click();
} catch (NoSuchElementException e) {
takeScreenshot("submit-not-found");
throw e;
}Strings (the most common test-data type)
String email = "admin@example.com";
boolean isValid = email.contains("@");
String domain = email.substring(email.indexOf("@") + 1);
String[] parts = "a,b,c".split(",");The var keyword (Java 10+)
var driver = new ChromeDriver();
var wait = new WebDriverWait(driver, Duration.ofSeconds(10));Use var for local variables when the type is obvious; skip it when the type adds clarity.
5. OOP Concepts Every Selenium Engineer Must Know
Classes and objects
public class LoginPage {
private WebDriver driver;
public LoginPage(WebDriver driver) { this.driver = driver; }
public void login(String email, String password) {
driver.findElement(By.id("email")).sendKeys(email);
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.id("submit")).click();
}
}Inheritance
public abstract class BasePage {
protected WebDriver driver;
protected WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
public abstract boolean isLoaded();
}
public class LoginPage extends BasePage {
public LoginPage(WebDriver driver) { super(driver); }
@Override
public boolean isLoaded() {
return wait.until(ExpectedConditions.urlContains("/login")) != null;
}
}Polymorphism via interfaces
public interface Browser { WebDriver createDriver(); }
public class ChromeBrowser implements Browser {
public WebDriver createDriver() { return new ChromeDriver(); }
}
public class FirefoxBrowser implements Browser {
public WebDriver createDriver() { return new FirefoxDriver(); }
}Abstract class = partial implementation (use for BasePage); interface = pure contract (use for Browser, Listener, Config).
6. Collections You Will Use Daily
// List — ordered, allows duplicates
List<WebElement> rows = driver.findElements(By.cssSelector("tr"));
int rowCount = rows.size();
// Map — key-value pairs
Map<String, String> credentials = new HashMap<>();
credentials.put("admin", "Sup3rSecret!");
String adminPassword = credentials.get("admin");
// Set — unique values, no order
Set<String> uniqueEmails = new HashSet<>();
uniqueEmails.add("a@example.com");
// Stream API
List<WebElement> enabledButtons = driver.findElements(By.tagName("button"))
.stream()
.filter(WebElement::isEnabled)
.filter(WebElement::isDisplayed)
.collect(Collectors.toList());| Collection | Use case |
|---|---|
List | Ordered collection, duplicates OK (rows, browser list) |
Map | Lookup by key (config, credentials, test data) |
Set | Unique values (visited URLs, error codes) |
Queue | FIFO processing (to-be-processed items) |
7. Exception Handling the Right Way
public void clickSafely(By locator) {
try {
wait.until(ExpectedConditions.elementToBeClickable(locator)).click();
} catch (TimeoutException e) {
takeScreenshot("click-timeout");
throw new AssertionError("Element not clickable: " + locator, e);
} catch (StaleElementReferenceException e) {
// retry once
wait.until(ExpectedConditions.elementToBeClickable(locator)).click();
}
}Custom exceptions for test code
public class LoginFailedException extends RuntimeException {
public LoginFailedException(String message, Throwable cause) {
super(message, cause);
}
}TestNG assertions throw AssertionError; don't wrap them in try/catch — let TestNG report the failure.
Screenshot helper
public class ScreenshotHelper {
public static String takeScreenshot(WebDriver driver, String name) {
var src = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
var dest = new File("screenshots/" + name + "_" + Instant.now().toEpochMilli() + ".png");
dest.getParentFile().mkdirs();
try { Files.copy(src.toPath(), dest.toPath()); }
catch (IOException e) { throw new RuntimeException("Failed to save screenshot", e); }
return dest.getAbsolutePath();
}
}8. File I/O and Properties for Test Data
src/test/resources/config.properties:
base.url=https://staging.example.com
admin.email=admin@example.com
admin.password=Sup3rSecret!
browser=chrome
timeout.seconds=10public class Config {
private final Properties props = new Properties();
public Config() {
try (var input = new FileInputStream("src/test/resources/config.properties")) {
props.load(input);
} catch (IOException e) { throw new RuntimeException("Failed to load config", e); }
}
public String get(String key) { return props.getProperty(key); }
public int getInt(String key) { return Integer.parseInt(get(key)); }
}Reading a JSON test-data file with Jackson
public class TestDataLoader {
private final ObjectMapper mapper = new ObjectMapper();
public List<TestData> loadUsers(String file) throws IOException {
return mapper.readValue(
new File("src/test/resources/" + file),
mapper.getTypeFactory().constructCollectionType(List.class, TestData.class)
);
}
}Use environment variables for secrets and properties files for non-sensitive config. For API-side test data patterns, see our API testing tutorial.
9. TestNG Framework Deep Dive
TestNG is the dominant Java test framework. JUnit 5 is the alternative; pick TestNG if you need data providers and listeners out of the box.
Annotations
public class LoginTest {
@BeforeSuite public void beforeSuite() {}
@BeforeClass public void beforeClass() {}
@BeforeMethod public void beforeMethod() { driver = new ChromeDriver(); }
@Test(priority = 1, groups = "smoke")
public void loginValid() { /* ... */ }
@Test(priority = 2, groups = "regression", dependsOnMethods = "loginValid")
public void loginInvalid() { /* ... */ }
@AfterMethod public void afterMethod() { if (driver != null) driver.quit(); }
}Data providers (the killer feature)
@DataProvider(name = "userCredentials")
public Object[][] userCredentials() {
return new Object[][] {
{"admin@example.com", "Sup3rSecret!", "Welcome, admin"},
{"viewer@example.com", "ViewerPass1!", "Welcome, viewer"},
};
}
@Test(dataProvider = "userCredentials")
public void loginWithMultipleUsers(String email, String password, String expectedGreeting) {
driver.get("https://example.com/login");
driver.findElement(By.id("email")).sendKeys(email);
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.id("submit")).click();
assertEquals(driver.findElement(By.tagName("h1")).getText(), expectedGreeting);
}Listeners
public class ScreenshotListener implements ITestListener {
@Override
public void onTestFailure(ITestResult result) {
var driver = (WebDriver) result.getTestContext().getAttribute("driver");
if (driver != null) ScreenshotHelper.takeScreenshot(driver, result.getName());
}
}Parallel execution
<suite name="Suite" parallel="methods" thread-count="4">
<test name="Regression">
<classes>
<class name="com.qa.selenium.LoginTest"/>
<class name="com.qa.selenium.CheckoutTest"/>
</classes>
</test>
</suite>Run with mvn test -DsuiteXmlFile=testng.xml.
10. Maven for Selenium Projects
Standard project structure
selenium-java-demo/
├── pom.xml
├── src/
│ ├── main/java/com/qa/selenium/
│ │ ├── pages/ (BasePage, LoginPage, DashboardPage)
│ │ └── utils/ (Config, ScreenshotHelper)
│ └── test/
│ ├── java/com/qa/selenium/ (LoginTest, CheckoutTest, listeners/)
│ └── resources/ (config.properties, testdata/users.json)Profiles for different environments
<profiles>
<profile>
<id>staging</id>
<properties>
<env>staging</env>
<base.url>https://staging.example.com</base.url>
</properties>
</profile>
</profiles>Run with mvn test -Pstaging.
Surefire plugin (runs tests)
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<suiteXmlFiles><suiteXmlFile>testng.xml</suiteXmlFile></suiteXmlFiles>
<parallel>methods</parallel>
<threadCount>4</threadCount>
</configuration>
</plugin>11. Page Object Model in Java
Base page
public abstract class BasePage {
protected final WebDriver driver;
protected final WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(15));
PageFactory.initElements(driver, this);
}
public abstract boolean isLoaded();
public void waitForLoaded() { wait.until(d -> isLoaded()); }
}Login page
public class LoginPage extends BasePage {
@FindBy(id = "email") private WebElement emailField;
@FindBy(id = "password") private WebElement passwordField;
@FindBy(id = "submit") private WebElement submitButton;
@FindBy(css = ".error-message") private WebElement errorMessage;
public LoginPage(WebDriver driver) { super(driver); }
@Override
public boolean isLoaded() {
return wait.until(ExpectedConditions.visibilityOf(emailField)) != null;
}
public DashboardPage loginAs(String email, String password) {
emailField.clear(); emailField.sendKeys(email);
passwordField.clear(); passwordField.sendKeys(password);
submitButton.click();
return new DashboardPage(driver);
}
public String getErrorMessage() { return errorMessage.getText(); }
}Test that uses POM
public class LoginTest extends BaseTest {
@Test
public void loginFlow() {
driver.get("https://example.com/login");
var login = new LoginPage(driver);
login.waitForLoaded();
var dashboard = login.loginAs("admin@example.com", "Sup3rSecret!");
assertTrue(dashboard.getWelcomeMessage().contains("Welcome"));
}
}For framework architecture, see our framework setup guide — the patterns map closely to Java.
12. Design Patterns for Test Automation
Factory Pattern
public class PageFactory {
public static <T extends BasePage> T create(Class<T> pageClass, WebDriver driver) {
try {
return pageClass.getDeclaredConstructor(WebDriver.class).newInstance(driver);
} catch (Exception e) {
throw new RuntimeException("Failed to create page: " + pageClass.getName(), e);
}
}
}Builder Pattern (for complex test data)
public class UserBuilder {
private String email = "default@example.com";
private String password = "DefaultPass1!";
private String role = "viewer";
public UserBuilder withEmail(String email) { this.email = email; return this; }
public UserBuilder withRole(String role) { this.role = role; return this; }
public User build() { return new User(email, password, role); }
}
var admin = new UserBuilder().withEmail("admin@example.com").withRole("admin").build();Strategy Pattern (cross-browser)
public interface DriverStrategy { WebDriver createDriver(); }
public class ChromeStrategy implements DriverStrategy {
public WebDriver createDriver() {
var options = new ChromeOptions();
options.addArguments("--headless=new");
return new ChromeDriver(options);
}
}Singleton + Decorator
Use Singleton sparingly — it makes testing harder. Prefer dependency injection. Decorator is useful for adding logging or retries to a WebDriver without modifying it.
13. Java Best Practices for Selenium in 2026
Code style
- 4 spaces, not tabs; always use braces
- Use Lombok
@Data/@Builderto reduce boilerplate - Prefer composition over inheritance and immutability where possible
- Use
Optionalfor nullable returns - Use
varfor local variables when the type is obvious
Selenium-specific
- Use explicit waits — never
Thread.sleep - Use the Page Object Model for any non-trivial app
- Prefer accessibility IDs and relative locators (Selenium 4)
- Let Selenium Manager resolve drivers automatically
- Reset state between tests (cookies, localStorage, database)
Testing-specific
- Use TestNG data providers for data-driven testing
- Use TestNG listeners for cross-cutting concerns
- Use Maven profiles for environment-specific config
- Use Surefire's parallel mode for parallel execution
Pair this with the SDET career roadmap to turn Java + Selenium into a specialty employers pay for, and run mock rounds at AI mock interview.
Frequently asked questions
Do I need Java to learn Selenium?
No — Selenium supports Java, Python, JavaScript, C#, Ruby, and Kotlin. But Java is the most enterprise-friendly and the most widely deployed.
Java vs Python for Selenium — which is better?
Java for enterprise and type safety. Python for fast iteration and simplicity. Both work fine for Selenium in 2026; the job market favors Java for enterprise roles and Python for startups.
Which version of Java should I use in 2026?
Java 17 (LTS) or Java 21 (LTS). Both are widely supported. Selenium 4 requires Java 11+.
What is the best IDE for Java + Selenium?
IntelliJ IDEA Community Edition. It has the best Java support, Maven integration, and TestNG runner integration in 2026.
What is the difference between TestNG and JUnit?
TestNG has data providers, listeners, and dependency-based ordering built-in. JUnit 5 has similar features via extensions but with different syntax. Choose TestNG if you want the easier path; choose JUnit 5 if you're already in a Spring ecosystem.
Do I need Maven for Selenium projects?
Strictly speaking, no — Gradle works too. But Maven is the most common and has the lowest learning curve. Most enterprise teams use Maven.
How do I run Selenium tests in parallel?
TestNG's parallel="methods" attribute plus the Surefire plugin's parallel configuration. For cloud parallel, use a Selenium Grid or BrowserStack/Sauce Labs.
What is the Page Object Model?
A design pattern where each web page (or component) is represented by a class that encapsulates locators and actions. Tests interact with page objects, never raw locators.
How do I handle flaky Selenium tests?
Use explicit waits, isolate test data, design for parallelism from day one, and add retry only as a last resort with quarantining and dashboards.
Where can I practice Java + Selenium?
Build a small Java app with a login form and test every interaction with Selenium, or clone any open-source Java web app and test its main flows.
Practice these questions
Work through 300+ Selenium questions with Java code snippets, Selenium 4, Grid, framework patterns and CI/CD scenarios.
Was this article helpful?
Keep building your QA edge
Pillar guides- Playwright PillarPlaywright interview questions300 Playwright Q&A, framework design, and migration guides.
- AI Mock InterviewSoftwareTestPilot's AI interview coachLive AI-powered mock interviews with rubric feedback.
- ATS Resume ReviewSoftwareTestPilot's ATS resume checkerFree AI ATS scoring with rewrite suggestions.
Continue reading

Why Every QA Engineer Must Master CI/CD Pipelines in 2026 (Or Risk Obsolescence)
12 min read
Is Cypress Dead? Analyzing 2026 Playwright Market Share
12 min read
Why Tests Pass Locally But Fail in CI/CD (And the 6 Fixes That Actually Work in 2026)
13 min readJoin the QA Community
Connect with fellow testers, share job leads, and get career advice.
Stop Reinventing the Wheel. Upgrade Your QA Arsenal.
Take your testing skills from beginner to Lead Engineer. Supercharge your daily workflow with our premium digital resources.
- ⚡ Ready-to-use testing strategy templates
- 🔥 Advanced API & UI automation guides
- ⏱️ Save 10+ hours a week on test planning