
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.
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
- Put your URL in
config.properties. - Write page objects for your screens under
pages. - Add a
.featurefile 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
tagsvalue 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.propertiesare placeholders for a practice database. Put your own in before running US03. - Credentials in the repo.
config.propertiesis 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.
