This guide walks you through writing end-to-end (E2E) tests using Selenium 4 with Java. You’ll learn how to set up a project, write a test that interacts with a real browser, and run it reliably. By the end, you’ll have a reusable test structure you can extend to cover your own user journeys.
What You’ll Need
- Java JDK 8 or later (Selenium 4 requires Java 8+; Java 11 or 17 is recommended)
- Maven or Gradle for dependency management (examples use Maven)
- An IDE like IntelliJ IDEA, Eclipse, or VS Code with Java support
- A modern browser – Chrome, Firefox, Edge, or Safari
- WebDriver binaries – Selenium 4 can manage these automatically using Selenium Manager (bundled), but you can also download them manually if you prefer.
- Basic Java knowledge – classes, methods, and annotations.
Step-by-Step
-
Create a new Maven project
In your IDE, create a new Maven project (or a Gradle one). If you’re using the command line, run:
mvn archetype:generate -DgroupId=com.example -DartifactId=e2e-tests -DarchetypeArtifactId=maven-archetype-quickstartThis gives you a standard project structure with a
pom.xml. -
Add Selenium 4 dependencies
Open
pom.xmland add the Selenium Java dependency. As of Selenium 4.6+, Selenium Manager automatically handles driver binaries, so you don’t need to add separate driver dependencies.<dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>4.21.0</version> <scope>test</scope> </dependency>Also add JUnit 5 (or TestNG) for writing test cases:
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.10.2</version> <scope>test</scope> </dependency> -
Set up a WebDriver instance
Create a class that initializes the WebDriver. Using Selenium 4, you can simply do:
import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; public class DriverFactory { public static WebDriver createDriver() { return new ChromeDriver(); } }Selenium Manager will automatically download the matching ChromeDriver if it’s not present. For Firefox, use
FirefoxDriver(), and for Edge,EdgeDriver(). -
Write your first E2E test
Create a test class (e.g.,
LoginTest.java) insrc/test/java. Use JUnit 5 annotations:import org.junit.jupiter.api.*; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import static org.junit.jupiter.api.Assertions.*; public class LoginTest { private WebDriver driver; @BeforeEach void setUp() { driver = DriverFactory.createDriver(); } @AfterEach void tearDown() { driver.quit(); } @Test void testLogin() { driver.get("https://example.com/login"); driver.findElement(By.id("username")).sendKeys("testuser"); driver.findElement(By.id("password")).sendKeys("password123"); driver.findElement(By.cssSelector("button[type='submit']")).click(); WebElement welcome = driver.findElement(By.cssSelector(".welcome")); assertTrue(welcome.isDisplayed()); assertEquals("Welcome, testuser!", welcome.getText()); } }Replace the selectors and URL with your own application’s elements.
-
Run the test
From the command line, run
mvn test(or run the test from your IDE). The test will open a browser, perform the steps, and report pass/fail. -
Use explicit waits for dynamic content
Modern web apps load content asynchronously. Replace implicit waits (or sleeps) with explicit waits:
import org.openqa.selenium.support.ui.WebDriverWait; import org.openqa.selenium.support.ui.ExpectedConditions; WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("result")));This waits up to 10 seconds for the element to appear, reducing flakiness.
-
Organize tests with Page Object Model
For maintainability, create a page class for each page. For example:
public class LoginPage { private WebDriver driver; private By usernameField = By.id("username"); private By passwordField = By.id("password"); private By submitButton = By.cssSelector("button[type='submit']"); public LoginPage(WebDriver driver) { this.driver = driver; } public void login(String user, String pass) { driver.findElement(usernameField).sendKeys(user); driver.findElement(passwordField).sendKeys(pass); driver.findElement(submitButton).click(); } }Then use it in your test:
new LoginPage(driver).login("user", "pass"); -
Run tests in CI/CD
To run tests in a headless environment (like a build server), configure the driver to run headless:
ChromeOptions options = new ChromeOptions(); options.addArguments("--headless"); WebDriver driver = new ChromeDriver(options);This lets you run tests without a visible browser window.
Common Mistakes
- Using Thread.sleep() – Hard-coded sleeps are brittle; use explicit waits instead.
- Not closing the driver – Always call
driver.quit()in@AfterEachto release resources. - Ignoring element visibility – An element may exist in DOM but not be visible; use
isDisplayed()checks. - Hard-coding URLs – Use configuration files or environment variables for different environments.
- Testing only the happy path – Include negative tests (e.g., wrong password) to ensure error handling works.
- Running tests in parallel without isolation – Each test should create its own driver instance.
Tips
- Use relative locators – Selenium 4 introduced
RelativeLocator(e.g.,withTagName("button").below(By.id("email"))) for more robust element finding. - Leverage Selenium Manager – It automatically handles driver versions, so you don’t need to manually download drivers.
- Take screenshots on failure – Use
((TakesScreenshot) driver).getScreenshotAs(...)in a@AfterEachhook to debug. - Use data-driven tests – Parameterize tests with different input sets using JUnit’s
@ParameterizedTest. - Keep tests independent – Each test should be able to run alone; avoid dependencies on order.
Troubleshooting
- Driver not found error – If Selenium Manager fails (e.g., no internet), download the driver manually and set
webdriver.chrome.driversystem property. - Element not found exception – Double-check the selector; use the browser’s DevTools to inspect. Also ensure you’re waiting for the element to be present.
- Tests pass locally but fail in CI – Often due to different screen size or timing. Use explicit waits and set a viewport size in options.
- Browser opens but test hangs – Check for JavaScript alerts or pop-ups; handle them with
driver.switchTo().alert().accept(). - Version mismatch – Ensure your browser is compatible with the WebDriver version. Selenium Manager usually picks the right one, but if you manually manage drivers, keep them updated.
Frequently Asked Questions
- Do I need to download WebDriver separately? – No, Selenium 4.6+ includes Selenium Manager, which automatically downloads the correct driver for your browser and version.
- Can I use Selenium 4 with Java 8? – Yes, Selenium 4 requires Java 8 or higher, but Java 11+ is recommended for better performance and features.
- How do I run tests in parallel? – Use JUnit 5’s parallel execution or integrate with TestNG. Set
junit.jupiter.execution.parallel.enabled=trueinjunit-platform.properties. - What is the difference between
findElementandfindElements? –findElementreturns the first matching element or throws if none;findElementsreturns a list (empty if none). Use the latter when checking for existence. - How do I handle pop-ups or new tabs? – Use
driver.getWindowHandles()to switch between windows, ordriver.switchTo().alert()for JavaScript alerts. - Is Selenium 4 backward compatible with Selenium 3 code? – Most Selenium 3 code works, but some APIs were deprecated or changed (e.g.,
WebDriverWaitnow usesDuration). You’ll need to update imports and method signatures.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Campus Tech Innovation Hubs: What They Are
- Poco C51 Airtel Prepaid Locked Variant India?
- Jenkins vs GitHub Actions: Which CI/CD Fits Your Team?
- Turn Off Backup Encryption in iTunes?
- Shared VPS Hosting: Is It Better?
- Disk Defrag Pro: What It Does and Whether You Need It