# Selenium WebDriver with Java — Setup and Your First Automated Test

Source: https://skills101.in/learn/test-automation/selenium-webdriver-java-first-test — Skills101 free tutorial (Software Testing & Automation). Updated 2026-11-09.

**Selenium WebDriver** is the most used tool for browser automation. Your Java code tells a real browser — Chrome, Edge, Firefox — to open pages, type, click and read text, exactly like a user.

## 1. How it works

![Your Java test sends W3C WebDriver commands over HTTP to ChromeDriver, which controls the real Chrome browser; Selenium Manager downloads the matching driver automatically](https://skills101.in/learn-img/test-automation/webdriver.svg)

- Your test calls the **Selenium Java library**.
- Each call becomes an HTTP request in the **W3C WebDriver** standard, sent to a **driver** program (ChromeDriver, EdgeDriver, GeckoDriver for Firefox).
- The driver controls the browser and sends back the result.

Since Selenium 4.6, **Selenium Manager** finds or downloads the right driver for your browser version automatically — old tutorials that make you download `chromedriver.exe` by hand (or use WebDriverManager) are out of date.

## 2. Project setup

A Maven project with three test libraries:

```xml
<!-- File: 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.skills101</groupId>
    <artifactId>test-automation</artifactId>
    <version>1.0.0</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>
        <restassured.version>6.3.0</restassured.version>
    </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>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>${restassured.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.5.4</version>
                <configuration>
                    <!-- run the suite described in testng.xml -->
                    <suiteXmlFiles>
                        <suiteXmlFile>testng.xml</suiteXmlFile>
                    </suiteXmlFiles>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
```

- `<scope>test</scope>` — these libraries are only for tests, not for the shipped app.
- Surefire (Maven's test runner) runs the suite described in `testng.xml` (lesson 6).
- Java 17+, like the rest of our tracks.

## 3. Creating the browser

```java
// File: src/test/java/com/skills101/qa/base/DriverFactory.java
package com.skills101.qa.base;

import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;

/**
 * Creates the browser chosen with -Dbrowser=chrome|edge|firefox (default chrome).
 * -Dheadless=false shows the browser window while you write tests.
 * Selenium Manager (built into Selenium 4) downloads the matching driver automatically.
 */
public final class DriverFactory {

    private DriverFactory() {
    }

    public static WebDriver create() {
        String browser = System.getProperty("browser", "chrome");
        boolean headless = Boolean.parseBoolean(System.getProperty("headless", "true"));
        WebDriver driver = switch (browser) {
            case "edge" -> {
                EdgeOptions options = new EdgeOptions();
                if (headless) options.addArguments("--headless=new");
                yield new EdgeDriver(options);
            }
            case "firefox" -> {
                FirefoxOptions options = new FirefoxOptions();
                if (headless) options.addArguments("-headless");
                yield new FirefoxDriver(options);
            }
            default -> {
                ChromeOptions options = new ChromeOptions();
                if (headless) options.addArguments("--headless=new");
                options.addArguments("--window-size=1280,900");
                yield new ChromeDriver(options);
            }
        };
        driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(30));
        return driver;
    }
}
```

- Choose the browser with `-Dbrowser=edge` (default Chrome) — the same tests run on every browser (**cross-browser testing**).
- **Headless** mode runs the browser without a window — faster, and required on CI servers. Use `-Dheadless=false` to watch your test while writing it.

Every test needs a fresh browser and must close it afterwards, so we put that in a base class:

```java
// File: src/test/java/com/skills101/qa/base/BaseUiTest.java
package com.skills101.qa.base;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

/** Every UI test class extends this: a fresh browser before each test, a screenshot if it fails, then close. */
public abstract class BaseUiTest {

    protected WebDriver driver;
    protected String baseUrl;

    @BeforeMethod(alwaysRun = true)   // alwaysRun: also when running only some groups (-Dgroups=smoke)
    public void openBrowser() throws IOException {
        baseUrl = PracticeSite.start();
        driver = DriverFactory.create();
    }

    @AfterMethod(alwaysRun = true)
    public void closeBrowser(ITestResult result) throws IOException {
        if (driver == null) {
            return;
        }
        if (result.getStatus() == ITestResult.FAILURE) {
            Path folder = Files.createDirectories(Path.of("target", "screenshots"));
            byte[] png = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
            Files.write(folder.resolve(result.getName() + ".png"), png);
        }
        driver.quit();   // quit() closes every window and ends the driver process
    }
}
```

`driver.quit()` closes all windows and the driver process; `driver.close()` closes only the current window. Forgetting `quit()` leaves zombie browsers eating your RAM.

The practice pages (a login page, a dashboard and a page full of tricky elements) are served from the test resources by a tiny local web server, so the tests need no internet:

```java
// File: src/test/java/com/skills101/qa/base/PracticeSite.java
package com.skills101.qa.base;

import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;

/** Serves the practice pages in src/test/resources/site/ on a free local port, so the UI tests need no internet. */
public final class PracticeSite {

    private static HttpServer server;

    private PracticeSite() {
    }

    public static synchronized String start() throws IOException {
        if (server == null) {
            server = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
            server.createContext("/", exchange -> {
                String path = exchange.getRequestURI().getPath();
                try (InputStream page = PracticeSite.class.getResourceAsStream("/site" + path)) {
                    if (page == null) {
                        exchange.sendResponseHeaders(404, -1);
                    } else {
                        byte[] body = page.readAllBytes();
                        exchange.getResponseHeaders().add("Content-Type", "text/html; charset=utf-8");
                        exchange.sendResponseHeaders(200, body.length);
                        exchange.getResponseBody().write(body);
                    }
                }
                exchange.close();
            });
            server.start();
        }
        return "http://localhost:" + server.getAddress().getPort();
    }
}
```

## 4. The first test

```java
// File: src/test/java/com/skills101/qa/ui/FirstSeleniumTest.java
package com.skills101.qa.ui;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

import com.skills101.qa.base.BaseUiTest;
import org.openqa.selenium.By;
import org.testng.annotations.Test;

public class FirstSeleniumTest extends BaseUiTest {

    @Test
    public void loginWithValidDetailsOpensDashboard() {
        driver.get(baseUrl + "/login.html");                       // 1. open the page
        assertEquals(driver.getTitle(), "Skills101 Practice - Login");

        driver.findElement(By.id("email")).sendKeys("asha@skills101.in");   // 2. find elements and act
        driver.findElement(By.id("password")).sendKeys("asha123");
        driver.findElement(By.id("login-btn")).click();

        assertTrue(driver.getCurrentUrl().endsWith("dashboard.html?name=Asha"));   // 3. check the result
        assertEquals(driver.findElement(By.id("welcome")).getText(), "Welcome, Asha!");
    }
}
```

Every UI test follows the same three steps: **open** a page, **find elements and act** (type, click), **check** the result with assertions. `assertEquals(actual, expected)` — in TestNG, *actual comes first*.

Run all tests with `./mvnw test` (Windows: `mvnw.cmd test`). In our build this test opens headless Chrome, logs in and lands on the dashboard — it passes.

## 5. Useful WebDriver methods

| Method | Does |
|---|---|
| `get(url)` | open a page and wait for it to load |
| `getTitle()`, `getCurrentUrl()` | read the page title / address |
| `findElement(By…)` | find one element (throws if missing) |
| `findElements(By…)` | find all matches (empty list if none) |
| `navigate().back()` / `.refresh()` | browser buttons |
| `manage().window().maximize()` | window size |
| `quit()` | end the session |

Element methods: `click()`, `sendKeys(text)`, `clear()`, `getText()`, `getDomAttribute(name)`, `isDisplayed()`, `isEnabled()`, `isSelected()`.

## Common mistakes

- Downloading driver .exe files by hand — Selenium Manager does it.
- Forgetting `driver.quit()` in an `@AfterMethod(alwaysRun = true)`.
- Sharing one browser across all tests — one test's state leaks into the next.
- Swapping actual/expected in TestNG's `assertEquals`.

## Quick recap

- Test code → W3C WebDriver over HTTP → driver → browser; Selenium Manager fetches drivers.
- Maven dependencies: selenium-java, testng (and rest-assured for APIs).
- Fresh browser per test in `@BeforeMethod`, `quit()` in `@AfterMethod`.
- Open → find and act → assert.


Practice quiz and free certificate: https://skills101.in/learn/test-automation/selenium-webdriver-java-first-test