Hands-On Java Automation Testing From Beginner To Advanced Fundamentals: Build Your First Working Solution

Hands‑On Java Automation Testing From Beginner To Advanced

Build Your First Working Solution

Goal: By the end of this chapter you will be able to

  • set up a Java Selenium project from scratch
  • write, run and debug a simple test
  • extend it into a mini‑project (login test)
  • troubleshoot common problems
  • add advanced features (parallel runs, data‑driven, reporting)

Audience: Developers, QA engineers, and anyone who wants a solid, production‑ready foundation in Java‑based automation testing.


Table of Contents

SectionWhat you’ll learn
1. FundamentalsCore concepts, environment set‑up, Maven/Gradle, Selenium basics
2. ImplementationHands‑on coding – first test, mini‑project, troubleshooting
3. Advanced TopicsParallel execution, data‑driven, custom listeners, reporting
4. Real‑World ApplicationsE‑commerce, mobile web, API integration
5. ExercisesPractice projects to cement knowledge

1. Fundamentals

1.1 Quick Setup

ToolWhyHow to install
JDK 17Java 17 is the current LTS version.brew install openjdk@17 (mac) or download from Oracle
IDEVSCode + Java Extension, IntelliJ IDEA Community
MavenDependency managementbrew install maven
Selenium 4.xWeb automationMaven dependency: <dependency>...</dependency>
WebDriverManagerAuto‑download drivers<dependency>io.github.bonigarcia:webdrivermanager</dependency>
TestNGTest framework<dependency>org.testng:testng</dependency>
AllureReporting<dependency>io.qameta.allure:allure-testng</dependency>

Project Skeleton

# Create a Maven project
mvn archetype:generate -DgroupId=com.example -DartifactId=automation-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
cd automation-demo

Add the following to pom.xml (excerpt):

<dependencies>
  <!-- Selenium -->
  <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.21.0</version>
  </dependency>
  <!-- WebDriverManager -->
  <dependency>
    <groupId>io.github.bonigarcia</groupId>
    <artifactId>webdrivermanager</artifactId>
    <version>5.9.1</version>
  </dependency>
  <!-- TestNG -->
  <dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.9.0</version>
    <scope>test</scope>
  </dependency>
  <!-- Allure -->
  <dependency>
    <groupId>io.qameta.allure</groupId>
    <artifactId>allure-testng</artifactId>
    <version>2.20.1</version>
    <scope>test</scope>
  </dependency>
</dependencies>

Pro Tip: Keep your pom.xml tidy. Use <dependencyManagement> to pin versions once, then reference them in modules.


1.2 Essential Commands & Immediate Practice

CommandWhat it doesWhen to use
mvn clean compileClean target folder, compile sourcesBefore first run
mvn testExecute TestNG testsAfter writing tests
mvn test -Dtest=LoginTestRun a specific test classDebugging
mvn surefire:testRun tests with Surefire pluginSame as mvn test
mvn clean test -DfailIfNoTests=falseRun tests, ignore if noneCI pipelines

Practice Exercise

  1. Create a src/test/java/com/example/HelloTest.java.
  2. Add a trivial test that prints “Hello, Selenium!”.
  3. Run mvn test.
package com.example;

import org.testng.Assert;
import org.testng.annotations.Test;

public class HelloTest {
    @Test
    public void hello() {
        System.out.println("Hello, Selenium!");
        Assert.assertTrue(true);
    }
}

Why – This confirms the environment works, Maven resolves dependencies, and TestNG runs tests.


1.3 Core Concepts

ConceptWhat you’ll seePractical Example
Page Object Model (POM)Encapsulate page elements & actionsLoginPage.java
Implicit vs Explicit WaitsReduce flakinessWebDriverWait
TestNG Annotations@BeforeSuite, @AfterSuite, @DataProviderSetup/teardown
Allure ReportingVisual test results@Attachment

Tip: Keep page objects in src/main/java/com/example/pages. Tests in src/test/java/com/example/tests.


2. Implementation

2.1 First Running Example – “Open Browser & Verify Title”

package com.example.tests;

import com.example.pages.HomePage;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.testng.Assert;
import org.testng.annotations.Test;
import io.github.bonigarcia.webdrivermanager.WebDriverManager;

public class OpenBrowserTest {
    @Test
    public void openHomePage() {
        WebDriverManager.setUpChromeDriver();
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless"); // run without UI
        WebDriver driver = new org.openqa.selenium.chrome.ChromeDriver(options);

        HomePage home = new HomePage(driver);
        home.navigate();

        String title = driver.getTitle();
        System.out.println("Page title: " + title);
        Assert.assertEquals(title, "Example Domain");

        driver.quit();
    }
}

Explanation

  • WebDriverManager automatically downloads the correct ChromeDriver.
  • HomePage is a simple page object (see next section).
  • Headless mode speeds up CI runs.

2.2 Build Your First Mini‑Project – Login Test

Step 1: Page Objects

package com.example.pages;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public class LoginPage {
    private final WebDriver driver;
    private final By username = By.id("user");
    private final By password = By.id("pass");
    private final By loginBtn = By.id("login");
    private final By errorMsg = By.id("error");

    public LoginPage(WebDriver driver) { this.driver = driver; }

    public void navigate() { driver.get("https://example.com/login"); }

    public void setUsername(String user) { driver.findElement(username).sendKeys(user); }

    public void setPassword(String pass) { driver.findElement(password).sendKeys(pass); }

    public void clickLogin() { driver.findElement(loginBtn).click(); }

    public String getError() { return driver.findElement(errorMsg).getText(); }
}

Step 2: Test Class

package com.example.tests;

import com.example.pages.LoginPage;
import io.github.bonigarcia.webdrivermanager.WebDriverManager;
import org.openqa.selenium.WebDriver;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class LoginTest {
    @Test(dataProvider = "credentials")
    public void testLogin(String user, String pass, boolean expected) {
        WebDriverManager.setUpChromeDriver();
        WebDriver driver = new org.openqa.selenium.chrome.ChromeDriver();

        LoginPage login = new LoginPage(driver);
        login.navigate();
        login.setUsername(user);
        login.setPassword(pass);
        login.clickLogin();

        if (expected) {
            Assert.assertTrue(driver.getCurrentUrl().contains("/dashboard"));
        } else {
            Assert.assertEquals(login.getError(), "Invalid credentials");
        }

        driver.quit();
    }

    @DataProvider
    public Object[][] credentials() {
        return new Object[][] {
            {"validUser", "validPass", true},
            {"invalid", "wrong", false}
        };
    }
}

Key Take‑aways

  • DataProvider lets you run the same test with different inputs.
  • Page Objects isolate locators – easier to maintain.
  • Assertions verify expected behavior.

2.3 Common Issues & Hands‑On Troubleshooting

SymptomLikely CauseFix
SessionNotCreatedExceptionWrong ChromeDriver versionUse WebDriverManager or set -Dwebdriver.chrome.driver
NoSuchElementExceptionElement not found (timing)Add explicit wait: new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.id("login")));
StaleElementReferenceExceptionPage re‑renderedRefresh reference or use ExpectedConditions.stalenessOf(element)
ElementNotInteractableExceptionElement hidden or disabledUse Actions to click or scroll into view
TestNG failures due to @Test not executedWrong package name or missing @Test annotationVerify classpath, mvn test

Pro Tip: Use driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS); sparingly. Prefer explicit waits for determinism.


2.4 Extend and Customize Your Implementation

FeatureHow to addExample
Parallel Test Execution@Test(threadPoolSize = 5) or parallel="tests" in testng.xmlRun 5 login tests concurrently
Custom ListenersImplement ITestListenerLog start/end times
ReportingAllure annotations (@Step, @Attachment)Attach screenshots on failure
Parameterization@Parameters({"env"})Run tests against staging/production
Dockerized Selenium Griddocker-compose.yml with Selenium Hub & NodesScale out tests in CI

Example: Custom Listener

public class MyListener implements ITestListener {
    @Override
    public void onTestStart(ITestResult result) {
        System.out.println("Starting: " + result.getName());
    }
    @Override
    public void onTestSuccess(ITestResult result) {
        System.out.println("Success: " + result.getName());
    }
    @Override
    public void onTestFailure(ITestResult result) {
        System.out.println("Failure: " + result.getName());
    }
}

Add to testng.xml:

<listeners>
    <listener class-name="com.example.listeners.MyListener"/>
</listeners>

3. Advanced Topics

3.1 Parallel Execution & Thread‑Safety

  • Use TestNG parallel="methods" or JUnit 5 @Execution(ExecutionMode.CONCURRENT).
  • Ensure WebDriver is thread‑local: ThreadLocal<WebDriver> driver = ThreadLocal.withInitial(() -> new ChromeDriver());.
  • Use Selenium Grid or Selenoid for distributed execution.

3.2 Data‑Driven Testing

  • CSV, JSON, or Excel as data sources.
  • Use @DataProvider with a Stream<Object[]> for large data sets.
  • Example: public Object[][] loadCsv(String path).

3.3 Custom Listeners & Reporting

  • Allure: Add @Attachment for screenshots.
  • ExtentReports: Add logs, screenshots, and HTML reports.
  • Selenium‑Grid‑Reports: Visual grid execution.

3.4 Performance & Optimizations

  • Page Object Cache: Cache elements with @CacheLookup.
  • Explicit Waits: Use FluentWait for flexible polling.
  • Headless + GPU: --disable-gpu for faster runs.
  • Parallel Grid: Up to 10 nodes with docker-compose.

3.5 Security & Data Privacy

  • Avoid hardcoding credentials. Use environment variables or Vault.
  • Mask sensitive data in reports (@Attachment with mask=true).

4. Real‑World Applications

DomainTypical Test CasesTools/Tech
E‑CommerceCart flow, payment gateway, promo codesSelenium, TestNG, Allure
Mobile WebResponsiveness, touch gesturesAppium, Java, TestNG
API IntegrationREST calls, JSON schema validationRestAssured, Java, TestNG
CI/CDJenkins, GitHub Actions, DockerMaven, Allure, Docker

Case Study:

  • Project: Automate login, checkout, and order confirmation for a demo e‑commerce site.
  • Approach: Page Object Model, Data‑Driven, Parallel runs on Docker Compose.
  • Outcome: 95% test coverage, 10x faster regression suite.

5. Exercises

#Skill FocusDescriptionExpected Deliverable
1FundamentalsWrite a test that opens a browser, navigates to https://example.com, and verifies the page title.OpenBrowserTest.java
2Page ObjectsCreate a SearchPage with methods: enterQuery(String), clickSearch(), getResults().SearchPage.java
3DataProviderUse a CSV file to test login with 20 credential sets.LoginTest.java with @DataProvider
4Parallel ExecutionConfigure TestNG to run all tests in parallel with 5 threads.testng.xml
5ReportingIntegrate Allure, attach a screenshot on failure.Allure report
6AdvancedImplement a custom ITestListener that logs test start/end times to a file.MyListener.java
7Real‑WorldAutomate a checkout flow for a demo e‑commerce site. Use Page Objects, Data‑Driven, and Parallel execution.Full test suite

Pro Tip: Commit your exercises to GitHub; run them on GitHub Actions to simulate CI.


Quick Reference Cheat Sheet

TopicCommand / CodeNotes
Maven compilemvn clean compile
Run all testsmvn test
Run specific testmvn test -Dtest=MyTest
Add Page Objectpublic class MyPage { ... }
Explicit waitnew WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(By.id("login")));
DataProvider@DataProvider(name="dp") public Object[][] data() { ... }
Parallel configtestng.xml <suite parallel="tests" thread-count="5">
Allure attachment@Attachment(value = "Screenshot", type = "image/png") public byte[] screenshot() { return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES); }

Final Thoughts

  • Start small, iterate fast.
  • Keep tests readable. Use descriptive names, avoid magic strings.
  • Maintain page objects. Refactor when locators change.
  • Automate only what you need. Avoid flaky tests.
  • Integrate early. Add tests to CI to catch regressions.

Happy coding! 🚀