Loading
October 26, 2026

AmirHesamTech

Raspberry Pi, homelab, Linux and automation — projects and practical guides

Test Automation Framework: a Java starter with Selenium, Cucumber BDD, REST Assured, JDBC and Apache POI

Every new test automation project starts with the same ten decisions: where the browser driver lives, how configuration is read, what happens after a failed test, how to talk to the database, how to read a spreadsheet. I made those decisions once and put them in a starter project, MyFrameWorkTemp on GitHub, so a new project can start writing scenarios on day one.

It combines Selenium for the browser, Cucumber for readable scenarios, REST Assured for APIs, JDBC for the database and Apache POI for Excel — UI, API and database testing in one Maven project. Here’s what’s inside and how the important parts work.

What’s in the project

Layer Tool (version in the pom) Used for
Browser automation Selenium 4.18.1 UI tests in Chrome or Firefox, locally, headless or on a Grid
Scenarios Cucumber 7.3.2 with the JUnit runner Plain-English .feature files, run by tag
API testing REST Assured 5.2.1 Calling APIs and checking status codes and JSON
Database testing JDBC (MySQL connector) Checking what actually got written
Test data Apache POI 5.2.3, JavaFaker Reading and writing Excel, generating fake data
src/test/
├── java/com/FrameWork/
│   ├── pages/      page objects: the locators for each screen
│   ├── steps/      step definitions, one class per user story (US01-US11) + Hooks
│   ├── runner/     CukesRunner and FailedTestRunner
│   ├── utility/    Driver, BrowserUtil, ConfigurationReader, DB_Util, API_Util
│   └── javaFiles/  plain Java interview exercises, separate from the framework
└── resources/features/   the .feature files

Quick start

You need Java 11 or newer, Maven, and Chrome or Firefox.

Advertisement
git clone https://github.com/amirh3sam/MyFrameWorkTemp.git
cd MyFrameWorkTemp
mvn clean install -DskipTests

There’s no driver to download. Since Selenium 4.6, Selenium Manager finds or downloads the right browser driver on its own, so there’s no chromedriver path to configure.

The eleven example stories

Each user story shows one capability, so you can copy the pattern you need:

Story What it shows
US01 A UI test against a menu bar — the plain Selenium starting point
US02 An API test with REST Assured against the Star Wars API
US03 A database test over JDBC against an HR schema
US04 / US05 Validating API responses (Game of Thrones and Pokémon data)
US06 Getting a token before the real API call
US07 / US08 Reading test data from Excel, and writing results back
US09 Reading a value from an environment variable instead of a file
US10 Running headless, the way a CI server runs it
U11 Reading and setting browser cookies

How the important parts work

One browser per thread: the Driver class

Tests never create a browser themselves. They call Driver.getDriver(), which opens the browser named in config.properties the first time and returns the same one after that. The driver is kept in an InheritableThreadLocal, so tests running in parallel each get their own browser. A shortened version:

private static InheritableThreadLocal<WebDriver> driverPool = new InheritableThreadLocal<>();

public static WebDriver getDriver(){
    if(driverPool.get() == null){
        String browserType = ConfigurationReader.getProperty("browser");
        switch(browserType){
            case "chrome":
                driverPool.set(new ChromeDriver());
                break;
            case "chrome-headless":
                ChromeOptions options = new ChromeOptions();
                options.addArguments("--headless");
                driverPool.set(new ChromeDriver(options));
                break;
            // firefox, remote-chrome and remote-firefox (Selenium Grid) work the same way
        }
    }
    return driverPool.get();
}

Switching browsers is one line in config.properties: browser=chrome, firefox, chrome-headless, or remote-chrome / remote-firefox for a Selenium Grid — set your own Grid address in Driver.java first.

Choosing what runs: CukesRunner

The runner says where the features and steps are, which reports to write, and which tags to run:

@RunWith(Cucumber.class)
@CucumberOptions(
        plugin = {
                "html:target/cucumber-report.html",
                "rerun:target/rerun.txt",
                "me.jvt.cucumber.report.PrettyReports:target/cucumber",
                "json:target/cucumber.json"
        },
        features = "src/test/resources/features",
        glue = "com/FrameWork/steps",
        tags = "@US02"
)
public class CukesRunner {}

Change tags to run a different story, or use an expression such as "@US01 or @US02". From the command line you don’t need to touch the code at all:

mvn test -Dcucumber.filter.tags="@US01"

An API test in a few lines

The feature file stays readable for anyone on the team:

Feature: check the status code for star wars website
  @US02
  Scenario: checking status code after GET HTTPS request with star wars website
    Given status code has to be 200;

and the step definition does the work with REST Assured, then reads values out of the JSON (shortened):

@Given("status code has to be {int};")
public void status_code_has_to_be(Integer int1) {
    Response response = RestAssured.given().get("http://swapi.dev/api/planets/1")
            .prettyPeek().then().statusCode(200).extract().response();
    JsonPath jsonPath = response.jsonPath();
    System.out.println(jsonPath.getString("rotation_period"));
}

One lesson that comes with public practice APIs: if the API is down, the test fails even though your code is fine. For real projects, point tests at an environment your team controls.

Screenshots on failure, cleanup always: Hooks

Hooks run around scenarios tagged @ui or @db. UI scenarios open the URL from the config before they start; if one fails, a screenshot is attached to the report; and the browser is always closed at the end:

@After("@ui")
public void tearDown(Scenario scenario){
    if(scenario.isFailed()){
        final byte[] screenshot = ((TakesScreenshot) Driver.getDriver()).getScreenshotAs(OutputType.BYTES);
        scenario.attach(screenshot, "image/png", "screenshot");
    }
    Driver.closeDriver();
}

Database scenarios get the same treatment: @Before("@db") opens the JDBC connection and @After("@db") closes it.

Re-running only what failed

Every run writes the failed scenarios to target/rerun.txt. FailedTestRunner reads that file and runs only those — a big time-saver on a long suite.

Pointing it at your own application

  1. Put your URL in config.properties.
  2. Write page objects for your screens under pages.
  3. Add a .feature file and its step definitions next to the existing ones, with a new tag.

The utility layer doesn’t need to change.

Common problems

  • Nothing runs. Almost always the tags value doesn’t match any feature file. Check the tag at the top of the .feature, including capital letters.
  • The database tests can’t connect. The connection details in config.properties are placeholders for a practice database. Put your own in before running US03.
  • Credentials in the repo. config.properties is committed so the examples run out of the box. Before you point this at anything real, move secrets into environment variables — US09 shows how — and keep the file out of version control.

Frequently asked questions

Can I use TestNG instead of JUnit?

Yes. Swap cucumber-junit for cucumber-testng in the pom and make the runner extend AbstractTestNGCucumberTests. Nothing in the utility layer depends on the runner.

Where’s the report?

Open target/cucumber-report.html after a run. The JSON report in target/cucumber.json is what CI tools usually pick up.

What are the files in javaFiles?

Plain Java exercises — reversing an array, bubble sort, second-largest value — the kind that come up in SDET interviews. The framework doesn’t depend on them.

Want to practise the API and database layers on their own first? Try my Postman local-server guide and the SQL practice lab. Then clone the framework, break it, and make it yours: MyFrameWorkTemp on GitHub.

Advertisement

65 Posts

SDET and QA engineer. I build Raspberry Pi servers, a Proxmox homelab and automations, and share how on TikTok and Instagram (@techwithamirh3sam).

View All Posts

You Missed

REWARDS 🔰 LV1 0🪙 ❯
🔰
Recruit
Guest Operative
Level1
XP0 / 100
💬 Need help?
coins0
Streak0 days · best 0
📋 Daily missions
↻ Resets daily at midnight
Rank ladder
How to earn
✓ Welcome bonus for joining — +200 🪙 & +100 XP (one-time)
✓ Daily check-in (streak bonus) — +20 XP & +12–24 🪙 (more 🪙 the longer your streak)
✓ Spin the daily wheel — +10 to +150 🪙 (free, once a day)
✓ Daily missions — check-in +15 🪙/+10 XP · daily spin +15 🪙/+10 XP
✓ Finish ALL daily missions — +30 🪙 & +20 XP bonus
✓ Level up — +25 🪙 every new level
✓ Achievements — first sign-in +50 🪙 · Lv5 +75 · Lv10 +150 · Lv25 +300 · 7-day streak +100 · 30-day +250 · collector +100 · subscriber +50
✓ Complete your profile (name, bio, photo) — up to +55 XP (one-time)
✓ Subscribe to the newsletter — +250 🪙 (one-time)
Coins and XP are just for fun: they have no cash value and can't be exchanged for money or prizes. You never earn them for viewing or clicking ads, or for how many pages or articles you open.
🛒 Token Shop0 🪙
🛍️ Shop0items
🎒 Inventory0items

🎡 Daily Spin

One free spin every day — pure bonus coins!
🪙
💬Need help?