Java Test Automation: Getting Started
Build and run your first Java browser test with Selenium and JUnit. Learn the setup, cleanup, common fixes, and when to use browser automation.
Java test automation means checking Java software automatically, but it can target different layers: Java logic, an API or service, or a browser user interface. For a first browser test, use JUnit Jupiter to organize and run the test, Selenium WebDriver to control the browser, and Maven or Gradle to manage dependencies and execution. The first milestone is one repeatable test that opens a page, performs an action, checks the result, and closes its browser session.
Selenium is not itself your test runner. WebDriver controls a browser; JUnit or TestNG provides test structure and assertions; Maven or Gradle manages the project and runs its tests. Selenium’s documentation says, “Selenium supports automation of all the major browsers in the market through the use of WebDriver.” Selenium WebDriver setup
1. Choose what to test
Use the simplest test layer that can verify the behavior you care about.
| Target | Useful for | Typical setup |
|---|---|---|
| Java unit test | Business rules and functions that do not need a browser or network | JUnit Jupiter with Maven or Gradle |
| Service or API test | HTTP endpoints, serialization, and service behavior | JUnit plus an HTTP client or a service-specific test library |
| Browser UI test | Navigation, forms, visible page state, and browser-dependent behavior | JUnit Jupiter, Selenium WebDriver, and an installed browser |
This guide builds the browser UI version because it demonstrates the complete loop of opening a browser, interacting with a page, asserting a result, and cleaning up. You do not need Selenium for every Java test.
2. Set up a Maven project
The example uses Maven, JUnit Jupiter, and Selenium Java. Keep dependencies in the project file so another developer or a CI job can reproduce the setup. Check the current Selenium installation instructions and your chosen browser’s requirements before pinning versions; version and Java requirements can change.
Create this layout:
java-test-automation/
pom.xml
src/
test/
java/
example/
SearchTest.java
Save the following as pom.xml. These dependency versions are example pins; verify current compatible releases before adopting them in a maintained project.
<?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>example</groupId>
<artifactId>java-test-automation</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.10.4</junit.version>
<selenium.version>4.25.0</selenium.version>
</properties>
<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
</plugin>
</plugins>
</build>
</project>
The Java 17 compiler release here is an example configuration, not a claim that Java 17 is Selenium’s universal minimum. Match the configured release to your JDK and the current requirements of your dependencies.
3. Install a browser and run the first test
Install the browser you want to automate. WebDriver uses a browser-specific implementation to communicate with it. Follow the current Selenium setup guidance for driver management and any browser-specific requirements; the Java bindings, browser, and driver implementation are distinct parts of the setup.
Save this test as src/test/java/example/SearchTest.java:
package example;
import java.time.Duration;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SearchTest {
private WebDriver driver;
@BeforeEach
void startBrowser() {
driver = new ChromeDriver();
}
@AfterEach
void closeBrowser() {
if (driver != null) {
driver.quit();
}
}
@Test
void submitsSearchAndShowsResults() {
driver.get("https://www.selenium.dev/");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement searchButton = wait.until(
ExpectedConditions.elementToBeClickable(By.cssSelector("button.DocSearch-Button"))
);
searchButton.click();
WebElement searchInput = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input.DocSearch-Input"))
);
searchInput.sendKeys("WebDriver");
WebElement result = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".DocSearch-Hit"))
);
assertTrue(result.isDisplayed(), "Expected a visible search result");
}
}
This illustrates the test shape: create a driver, navigate, find elements, interact, wait for a visible outcome, assert, then quit. A live demo site’s markup can change, so if the selectors stop matching, inspect the current page and update them. Selenium’s first-script guide and test organization guide provide official examples of browser interaction and lifecycle cleanup.
From the project directory, run:
mvn test
You can also run the test from an IDE after importing the Maven project. Use the command-line run as well: it confirms that the build file contains the dependencies and test configuration needed outside your local IDE.
4. Use Gradle if it matches your project
Maven and Gradle are both documented choices. Follow the build system already used by your team or repository; neither is a universal requirement. Gradle’s JVM projects use the standard test source set and task, with tests under src/test/java. For JUnit Jupiter, configure the test task to use JUnit Platform.
Example build.gradle using Groovy DSL (the versions are example pins to verify):
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.seleniumhq.selenium:selenium-java:4.25.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.4'
}
test {
useJUnitPlatform()
}
Put the same test class under src/test/java/example/SearchTest.java, then run:
./gradlew test
The wrapper is preferred for a shared project because it pins the Gradle distribution used by the repository. See the current Gradle Java testing guide for test dependencies and task configuration.
5. Understand the framework choices
- JUnit Jupiter: The current JUnit programming and extension model for writing tests. JUnit 5 also includes the Platform, which launches tests and provides engine infrastructure, and Vintage, which supports running JUnit 3 and 4 tests on the Platform. See the JUnit 5 User Guide.
- TestNG: Another supported test framework; Gradle and IntelliJ document integrations for it. Choose it when the existing project or team uses it.
- Maven or Gradle: Both manage project dependencies and test execution. Keep one consistent with the repository instead of adding parallel build setups for a first test.
For a new example, JUnit Jupiter keeps the test lifecycle and assertions straightforward. Do not add JUnit and TestNG together without a project-specific reason.
6. Make the test reliable
- Always clean up. Use an after-test lifecycle method and call
driver.quit(), including when an assertion fails. This closes the browser session and avoids abandoned processes. - Wait for conditions, not arbitrary time. Prefer an explicit wait for visibility or clickability. A fixed sleep can be too short on a slow run and waste time on a fast one.
- Use stable selectors. Prefer IDs, accessible labels, or dedicated test attributes when the application provides them. Long CSS paths tied to layout are prone to breaking after markup changes.
- Keep browser tests focused. Verify a user-visible browser behavior here; test calculations and business rules directly in Java where a browser adds no value.
- Start with one browser and one test. Add parallel execution, multiple browser versions, or a remote Grid after the local test is repeatable. Selenium presents Grid as a way to scale execution, not as a setup prerequisite.
- Keep environments aligned. Use the same JDK, dependency versions, browser assumptions, and build command locally and in CI. Record configuration in the repository rather than relying on IDE-only settings.
7. Common errors and fixes
| Symptom | Likely cause | What to do |
|---|---|---|
SessionNotCreatedException or browser fails at startup |
Browser and driver setup is missing or incompatible, or the browser cannot start in the current environment | Check the installed browser, Selenium’s current driver setup requirements, and headless or container-specific browser dependencies. Use the supported driver-management path for your chosen setup. |
ClassNotFoundException, missing imports, or test not discovered |
Dependency is absent, code is in the wrong source directory, or test engine/configuration is missing | Confirm Selenium and JUnit dependencies are in the test scope, the class is under src/test/java, and JUnit Platform is enabled for Gradle. Reimport the Maven or Gradle project. |
| Gradle reports no tests or cannot run Jupiter tests | The test task is not configured for the JUnit Platform or the Jupiter engine dependency is missing | Use useJUnitPlatform() and include the JUnit Jupiter dependency as shown in the Gradle setup. |
NoSuchElementException |
The element is not present yet, the selector is wrong, or the page changed | Inspect the rendered page and selector. Wait for the expected element condition instead of locating it immediately after navigation. |
TimeoutException |
The expected condition never became true, the page is slow, or the selector does not match | Check the page state and selector first. Increase the wait only when the application legitimately needs more time; do not hide a broken condition with a large timeout. |
| Browser remains open after a failure | Cleanup is missing or does not run for the test lifecycle | Put driver.quit() in an @AfterEach hook and guard against a null driver if startup can fail. |
| Works in IDE but fails from command line or CI | IDE-only configuration, different JDK, browser availability, or dependencies not represented in the build | Run mvn test or ./gradlew test locally, compare JDK and browser setup, and commit reproducible project configuration. |
8. Performance, reliability, and cost
Browser tests require launching and controlling a browser, so reserve them for behaviors that depend on browser interaction. Keep pure logic checks at the unit-test layer, and avoid introducing parallel runs or a remote Grid before you have a stable local baseline. This keeps setup and diagnosis manageable; the sources provide no universal runtime benchmark because it depends on the application and environment.
For reliability, make waits condition-based, choose stable selectors, close sessions, and keep browser, driver, JDK, and dependency setup explicit. A public demo page is convenient for a first exercise but can change or be unavailable; for an ongoing suite, use an application environment and test data that your project controls.
The Java libraries and build tools in this guide are software dependencies. The setup research does not establish a specific hardware purchase or paid service as necessary for getting started. Remote browser infrastructure is an optional scale-up, not a prerequisite for a local first test.
9. Capture a page image from Java
Browser testing and screenshot capture overlap in that both can involve a browser, but they solve different problems. Selenium is useful when the test must interact with the page and assert behavior. If the task is to save a page image or PDF rather than verify an interaction, a screenshot API can avoid managing a browser session in your Java process.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. Use its Java-friendly HTTP endpoint when you need a capture rather than a browser test. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Reasons to use it for page captures: cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed; an MCP server lets AI agents take screenshots; and 1,000 screenshots a month are free with no card, with paid plans starting at $5 for 3,000. Plans include Free, Starter ($5 for 3,000), Growth ($15 for 15,000), Pro ($39 for 60,000), Scale ($99 for 250,000), and Business ($249 for 1,000,000); yearly billing gives two months free, and every feature is available on every plan. Each response includes page-verdict and billing headers, so you can distinguish capture outcomes and billed shots.
Sign up free for 1,000 screenshots a month with no card.
10. FAQ
Do I need Selenium to start Java test automation?
No. Use JUnit alone for Java behavior that does not need a browser. Add Selenium when the test needs to control a browser and verify UI behavior.
Can I use TestNG instead of JUnit?
Yes. TestNG is another documented option. Follow the conventions of your project and configure its dependencies and runner consistently.
Is the Java version in the sample the Selenium minimum?
No. The compiler release is an example value. Check Selenium’s current language requirements and match the project to its supported JDK.
Should I use a remote browser Grid on the first day?
Usually not for a first local test. Get one local browser test stable, then consider remote or parallel execution when the project needs it.


