Skills101

Software Testing & Automation · Lesson 2 of 11 · Updated ·

Selenium WebDriver with Java — Setup and Your First Automated Test

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

  • 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:

<!-- 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

// 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:

// 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:

// 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

// 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

MethodDoes
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 what you learned

📝 Quiz: Selenium Setup5 questions · instant answers & explanations · +10 XP · counts towards your free certificateTake the quiz →