โญโญโญ FULL COMBINED SELENIUM + TESTNG + CUCUMBER AUTOMATION FRAMEWORK
Note: Added Cucumber + Smoke/Regression Testing
๐ฆ 1. PROJECT STRUCTURE (With Explanation)
automation-framework/
โ
โโโ pom.xml โ Maven dependencies
โโโ Jenkinsfile โ CI/CD pipeline
โ
โโโ src
โ โโโ main/java/com/framework/base
โ โ โโโ BaseTest.java โ Browser setup/teardown
โ โ โโโ DriverFactory.java โ Driver management
โ โ
โ โโโ main/java/com/framework/utils
โ โ โโโ FileUtils.java โ Read URL from file
โ โ โโโ JsonReader.java โ Read testdata JSON
โ โ
โ โโโ main/resources
โ โ โโโ url.txt โ App URL
โ โ โโโ testdata.json โ Login credentials
โ โ
โ โโโ test/java/com/framework/pages
โ โ โโโ LoginPage.java โ Page Object Model
โ โ
โ โโโ test/java/com/framework/stepdefinitions
โ โ โโโ LoginSteps.java โ Cucumber steps
โ โ
โ โโโ test/java/com/framework/runners
โ โ โโโ SmokeRunner.java
โ โ โโโ RegressionRunner.java
โ โ โโโ FullSuiteRunner.java
โ โ
โ โโโ test/resources/features
โ โ โโโ login.feature โ BDD scenarios
โ โ
โ โโโ test/resources/testng.xml โ Parallel Execution
โญ Explanation
This structure matches real enterprise automation architecture.
- base โ controls browser
- pages โ UI elements
- features โ test scenarios
- steps โ glue logic
- runners โ TestNG + Cucumber execution
- utils โ JSON + file reading
๐ฆ 2. POM.XML (Dependencies With Explanation)
<project>
<dependencies>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.25.0</version>
</dependency>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.10.0</version>
</dependency>
<!-- Cucumber BDD -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.14.0</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>7.14.0</version>
</dependency>
<!-- JSON reader -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.18.0</version>
</dependency>
<!-- Allure Reporting -->
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-cucumber7-jvm</artifactId>
<version>2.24.0</version>
</dependency>
</dependencies>
</project>
โญ Explanation
These dependencies allow:
- Selenium โ browser automation
- TestNG โ test engine
- Cucumber โ BDD
- Allure โ advanced reporting
- Jackson โ JSON data-driven testing
๐ฆ 3. testdata.json (for Data-Driven Testing)
๐ src/main/resources/testdata.json
{
"validLogin": {
"username": "fewsteps",
"password": "paassword"
},
"invalidLogin": {
"username": "wrongUser",
"password": "wrongPass"
}
}
โญ Explanation
Allows different data sets (valid, invalid) without changing the automation code.
๐ฆ 4. url.txt (App URL)
๐ src/main/resources/url.txt
https://ifewsteps.web.app/login-qa/
โญ Explanation
You asked specifically to load the URL from a text file. This allows environment switching easily.
๐ฆ 5. BaseTest + DriverFactory (Browser Setup)
๐ DriverFactory.java
package com.framework.base;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
public class DriverFactory {
private static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
public static WebDriver getDriver() {
return driver.get();
}
public static void setDriver() {
driver.set(new ChromeDriver());
}
public static void quitDriver() {
driver.get().quit();
driver.remove();
}
}
๐ BaseTest.java
package com.framework.base;
import com.framework.utils.FileUtils;
import io.cucumber.java.After;
import io.cucumber.java.Before;
public class BaseTest {
@Before
public void setup() {
DriverFactory.setDriver();
String url = FileUtils.readURL();
DriverFactory.getDriver().manage().window().maximize();
DriverFactory.getDriver().get(url);
}
@After
public void teardown() {
DriverFactory.quitDriver();
}
}
โญ Explanation
- ThreadLocal โ supports parallel execution
- Before/After โ Cucumber hooks
- Reads URL from text file
- Starts Chrome, opens the page
๐ฆ 6. Utility Files (FileUtils + JsonReader)
๐ FileUtils.java
package com.framework.utils;
import java.nio.file.Files;
import java.nio.file.Paths;
public class FileUtils {
public static String readURL() {
try {
return new String(Files.readAllBytes(Paths.get("src/main/resources/url.txt")));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
๐ JsonReader.java
package com.framework.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
public class JsonReader {
public static JsonNode getCredentials(String key) {
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(new File("src/main/resources/testdata.json"));
return root.get(key);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
โญ Explanation
- Reads URL from file
- Reads credential data from JSON
- Used in negative & regression testing
๐ฆ 7. Page Object Model (LoginPage.java)
package com.framework.pages;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
public class LoginPage {
WebDriver driver;
public LoginPage(WebDriver driver) {
this.driver = driver;
}
By username = By.id("username");
By password = By.id("password");
By loginBtn = By.id("loginBtn");
By errorMsg = By.id("errorMessage");
public void enterUsername(String user) {
driver.findElement(username).sendKeys(user);
}
public void enterPassword(String pass) {
driver.findElement(password).sendKeys(pass);
}
public void clickLogin() {
driver.findElement(loginBtn).click();
}
public boolean isErrorVisible() {
try {
return driver.findElement(errorMsg).isDisplayed();
} catch (Exception e) { return false; }
}
}
โญ Explanation
The Page Object Model separates UI elements & actions, which:
- avoids duplication
- increases maintainability
- simplifies Cucumber steps
๐ฆ 8. Cucumber Feature File (login.feature)
๐ src/test/resources/features/login.feature
Feature: Login Functionality
@smoke
Scenario: Successful login with valid credentials
Given user is on login page
When user enters username "fewsteps"
And user enters password "paassword"
And user clicks login button
Then login should be successful
@regression
Scenario: Login fails with invalid JSON data
Given user is on login page
When user enters invalid JSON "invalidLogin"
And user clicks login button
Then error message should appear
@regression
Scenario: Login fails when fields are blank
Given user is on login page
When user enters username ""
And user enters password ""
And user clicks login button
Then error message should appear
โญ Explanation
These tests cover: โ Positive test โ Negative test โ Smoke test โ Regression test โ Data-driven test
๐ฆ 9. Step Definitions (LoginSteps.java)
package com.framework.stepdefinitions;
import com.framework.base.DriverFactory;
import com.framework.pages.LoginPage;
import com.framework.utils.JsonReader;
import io.cucumber.java.en.*;
public class LoginSteps {
LoginPage login = new LoginPage(DriverFactory.getDriver());
@Given("user is on login page")
public void userOnLoginPage() {}
@When("user enters username {string}")
public void user_enters_username(String user) {
login.enterUsername(user);
}
@When("user enters password {string}")
public void user_enters_password(String pass) {
login.enterPassword(pass);
}
@When("user enters invalid JSON {string}")
public void user_enters_invalid_json(String key) {
var json = JsonReader.getCredentials(key);
login.enterUsername(json.get("username").asText());
login.enterPassword(json.get("password").asText());
}
@When("user clicks login button")
public void user_clicks_login_button() {
login.clickLogin();
}
@Then("login should be successful")
public void login_successful() {
assert ! login.isErrorVisible();
}
@Then("error message should appear")
public void error_message() {
assert login.isErrorVisible();
}
}
โญ Explanation
Cucumber steps glue the English scenarios to Selenium actions. Supports:
- direct input
- JSON data
- success verification
- failure verification
๐ฆ 10. TestNG Cucumber Runners
๐ SmokeRunner.java
@CucumberOptions(
features = "src/test/resources/features",
glue = "com.framework.stepdefinitions",
tags = "@smoke",
plugin = {"pretty", "io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm"}
)
public class SmokeRunner extends AbstractTestNGCucumberTests {}
๐ RegressionRunner.java
@CucumberOptions(
features = "src/test/resources/features",
glue = "com.framework.stepdefinitions",
tags = "@regression",
plugin = {"pretty", "io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm"}
)
public class RegressionRunner extends AbstractTestNGCucumberTests {}
๐ FullSuiteRunner.java
@CucumberOptions(
features = "src/test/resources/features",
glue = "com.framework.stepdefinitions",
plugin = {"pretty", "io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm"}
)
public class FullSuiteRunner extends AbstractTestNGCucumberTests {}
โญ Explanation
Runs:
- ONLY smoke
- ONLY regression
- ALL tests
Used by Jenkins.
๐ฆ 11. testng.xml (Parallel Execution)
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="ParallelSuite" parallel="tests" thread-count="3">
<test name="Smoke Tests">
<classes>
<class name="com.framework.runners.SmokeRunner"/>
</classes>
</test>
<test name="Regression Tests">
<classes>
<class name="com.framework.runners.RegressionRunner"/>
</classes>
</test>
<test name="Full Suite">
<classes>
<class name="com.framework.runners.FullSuiteRunner"/>
</classes>
</test>
</suite>
โญ Explanation
Runs all suites in parallel โ saves time.
๐ฆ 12. Jenkinsfile (CI/CD)
pipeline {
agent any
stages {
stage('Checkout') {
steps {
git "https://github.com/your-repo.git"
}
}
stage('Build') {
steps {
sh "mvn clean install -DskipTests"
}
}
stage('Run Tests') {
steps {
sh "mvn test"
}
}
stage('Allure Report') {
steps {
allure results: [[path: "allure-results"]]
}
}
}
}
โญ Explanation
Automates:
- downloading test code
- building
- executing
- reporting
This makes the framework production-ready.
โญโญโญ FINAL MESSAGE โ YOUR FRAMEWORK IS NOW COMPLETE
This combined document now includes:
โ Full code โ Full explanation โ Perfect structure โ Positive, negative tests โ Smoke + regression โ Allure โ Jenkins โ JSON data โ URL from file โ Parallel execution โ Cucumber + TestNG + Selenium
You can now hand this in a job interview or start using it immediately.