0% found this document useful (0 votes)
12 views13 pages

Selenium Java Framework StepByStep Prabhu

This document provides a comprehensive guide for building a Java-based Selenium automation framework using various tools such as TestNG, Maven, and Cucumber BDD. It includes detailed folder structures, key dependencies, configuration settings, and examples of implementing a thread-safe driver factory, TestNG hooks, and Cucumber steps. Additionally, it covers utilities for waiting, taking screenshots, and generating reports, along with a sample test case for login functionality using data-driven testing.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views13 pages

Selenium Java Framework StepByStep Prabhu

This document provides a comprehensive guide for building a Java-based Selenium automation framework using various tools such as TestNG, Maven, and Cucumber BDD. It includes detailed folder structures, key dependencies, configuration settings, and examples of implementing a thread-safe driver factory, TestNG hooks, and Cucumber steps. Additionally, it covers utilities for waiting, taking screenshots, and generating reports, along with a sample test case for login functionality using data-driven testing.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Selenium Java Automation Framework

– Step-by-Step Guide + Interview Q&A


Build a Java-based Selenium framework with TestNG, Maven, Log4j2, Extent Reports,
Cucumber BDD, REST Assured, Jenkins, and Selenium Grid on Docker. Includes code
skeletons and interview Q&A.

1) Recommended Folder Structure


selenium-java-framework/
├─ Jenkinsfile
├─ [Link]
├─ [Link]
├─ [Link]
├─ src
│ ├─ main/java/{base,driver,utils,listeners}
│ └─ test/java/{pages,tests,steps,runners,api}
└─ src/test/resources/{features,config,[Link],[Link]}

2) [Link] (Key Dependencies & Plugins)


<dependencies>
<dependency><groupId>[Link]</groupId><artifactId>selenium-
java</artifactId><version>4.23.0</version></dependency>

<dependency><groupId>[Link]</groupId><artifactId>testng</artifactId><version
>7.10.2</version><scope>test</scope></dependency>

<dependency><groupId>[Link]</groupId><artifactId>webdrivermanager<
/artifactId><version>5.8.0</version></dependency>
<dependency><groupId>[Link]</groupId><artifactId>cucumber-java</
artifactId><version>7.15.0</version><scope>test</scope></dependency>
<dependency><groupId>[Link]</groupId><artifactId>cucumber-testng</
artifactId><version>7.15.0</version><scope>test</scope></dependency>
<dependency><groupId>[Link]-assured</groupId><artifactId>rest-assured</
artifactId><version>5.4.0</version><scope>test</scope></dependency>
<dependency><groupId>[Link].log4j</groupId><artifactId>log4j-
core</artifactId><version>2.23.1</version></dependency>
<dependency><groupId>[Link]</groupId><artifactId>extentreports</
artifactId><version>5.1.1</version></dependency>
<dependency><groupId>[Link]</groupId><artifactId>poi-ooxml</
artifactId><version>5.2.5</version></dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>[Link]</groupId><artifactId>maven-surefire-
plugin</artifactId><version>3.2.5</version>
<configuration><suiteXmlFiles><suiteXmlFile>[Link]</suiteXmlFile></
suiteXmlFiles></configuration>
</plugin>
</plugins>
</build>

3) Configuration (src/test/resources/config/[Link])
baseUrl=[Link]
browser=chrome
headless=false
grid=false
gridUrl=[Link]
implicitWait=0
explicitWait=10
takeScreenshotOnFailure=true

Override properties at runtime:


mvn clean test -Dbrowser=edge -Denv=qa -Dgrid=true
-DgridUrl=[Link]

4) Thread-safe Driver Factory (local + Grid)


package driver;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

import [Link];
import [Link];
import [Link];

public class DriverFactory {


private static ThreadLocal<WebDriver> tlDriver = new ThreadLocal<>();
private static ConcurrentHashMap<Long, String> threadToBrowser = new
ConcurrentHashMap<>();

public static WebDriver getDriver() { return [Link](); }


public static void setDriver(WebDriver driver) { [Link](driver); }
public static void unload() { WebDriver d = [Link](); if (d != null)
{ [Link](); [Link](); } }
public static void initDriver(String browser, boolean headless, boolean grid,
String gridUrl, int implicitWaitSec) {
try {
WebDriver driver;
switch ([Link]()) {
case "firefox": {
FirefoxOptions o = new FirefoxOptions(); if (headless)
[Link]("-headless");
if (grid) driver = new RemoteWebDriver(new URL(gridUrl), o); else
{ [Link]().setup(); driver = new FirefoxDriver(o);}
break; }
case "edge": {
EdgeOptions o = new EdgeOptions(); if (headless) [Link]("--
headless=new");
if (grid) driver = new RemoteWebDriver(new URL(gridUrl), o); else
{ [Link]().setup(); driver = new EdgeDriver(o);} break; }
default: {
ChromeOptions o = new ChromeOptions(); if (headless)
[Link]("--headless=new"); [Link]("--disable-gpu", "--window-
size=1920,1080");
if (grid) driver = new RemoteWebDriver(new URL(gridUrl), o); else
{ [Link]().setup(); driver = new ChromeDriver(o);} }
}

[Link]().timeouts().implicitlyWait([Link](implicitWaitSec));
[Link]().window().maximize();
setDriver(driver);
[Link]([Link]().getId(), browser);
} catch (Exception e) { throw new RuntimeException("Failed to init driver: "
+ [Link](), e); }
}
}

5) BaseTest with TestNG Hooks


package base;

import [Link];
import [Link].*;
import [Link];
import [Link];

public class BaseTest {


protected Properties config;

@Parameters({"browser", "headless", "grid", "gridUrl"})


@BeforeMethod(alwaysRun = true)
public void setUp(@Optional String browser, @Optional String headless,
@Optional String grid, @Optional String gridUrl) {
config = [Link]();
String br = [Link]("browser", browser != null ? browser :
[Link]("browser"));
boolean hl = [Link]([Link]("headless", headless !=
null ? headless : [Link]("headless")));
boolean useGrid = [Link]([Link]("grid", grid !=
null ? grid : [Link]("grid")));
String gUrl = [Link]("gridUrl", gridUrl != null ? gridUrl :
[Link]("gridUrl"));
int implicitWait = [Link]([Link]("implicitWait",
"0"));
[Link](br, hl, useGrid, gUrl, implicitWait);
}

@AfterMethod(alwaysRun = true)
public void tearDown() { [Link](); }
}

6) Config Reader Utility


package utils;

import [Link];
import [Link];
import [Link];

public class ConfigReader {


private static Properties props;
public static Properties getProperties() {
if (props == null) {
try (InputStream is = new
FileInputStream("src/test/resources/config/[Link]")) {
props = new Properties(); [Link](is);
} catch (Exception e) { throw new RuntimeException("Unable to load
[Link]", e); }
}
return props;
}
}

7) Wait & Screenshot Utilities


package utils;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class WaitUtils {


private static final long EXPLICIT_WAIT =
[Link]([Link]().getProperty("explicitWait", "10"));
public static WebElement waitForVisible(By locator) {
return new WebDriverWait([Link](),
[Link](EXPLICIT_WAIT))
.until([Link](locator));
}
}

package utils;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class ScreenshotUtil {


public static String takeScreenshot(String namePrefix) {
try {
File src = ((TakesScreenshot)
[Link]()).getScreenshotAs([Link]);
String ts = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS").format(new
Date());
String path = "target/screenshots/" + namePrefix + "_" + ts + ".png";
File dest = new File(path); [Link]().mkdirs();
[Link](src, dest);
return [Link]();
} catch (Exception e) { return null; }
}
}

8) Page Object Model Example (Fluent Style)


package pages;

import [Link];
import [Link];
import [Link];
import [Link];

public class LoginPage {


private WebDriver driver = [Link]();
private By username = [Link]("username");
private By password = [Link]("password");
private By loginBtn = [Link]("button[type='submit']");
private By error = [Link](".error");

public LoginPage goTo(String url) { [Link](url); return this; }


public LoginPage typeUsername(String user)
{ [Link](username).sendKeys(user); return this; }
public LoginPage typePassword(String pass)
{ [Link](password).sendKeys(pass); return this; }
public HomePage clickLoginSuccess() { [Link](loginBtn).click();
return new HomePage(); }
public String getError() { return [Link](error).getText(); }
}

package pages;

import [Link];
import [Link];

public class HomePage {


private By header = [Link]("[Link]-title");
public String getHeaderText() { return
[Link]().findElement(header).getText(); }
}

9) TestNG Test with DataProvider (Excel)


package utils;

import [Link].*;
import [Link];

public class ExcelUtil {


public static Object[][] readSheet(String path, String sheetName) {
try (FileInputStream fis = new FileInputStream(path)) {
Workbook wb = [Link](fis); Sheet sheet =
[Link](sheetName);
int rows = [Link](); int cols =
[Link](0).getPhysicalNumberOfCells();
Object[][] data = new Object[rows-1][cols];
for (int i=1; i<rows; i++) { Row r = [Link](i); for (int j=0;
j<cols; j++) { Cell c = [Link](j); data[i-1][j] =
(c==null)?"":[Link](); } }
return data;
} catch (Exception e) { throw new RuntimeException("Excel read error:
"+[Link](), e); }
}
}

package tests;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public class LoginTest extends BaseTest {
@DataProvider(name="loginData", parallel=true)
public Object[][] loginData() { return
[Link]("test-data/[Link]", "Sheet1"); }

@Test(dataProvider="loginData", description="Validate login with multiple


credentials")
public void loginTest(String username, String password, String expected) {
String baseUrl = [Link]().getProperty("baseUrl");
HomePage home = new
LoginPage().goTo(baseUrl).typeUsername(username).typePassword(password).clickLog
inSuccess();
[Link]([Link](), expected);
}
}

10) [Link] for Parallel Cross-Browser


<!DOCTYPE suite SYSTEM "[Link] >
<suite name="UI Suite" parallel="tests" thread-count="3">
<test name="Chrome Suite">
<parameter name="browser" value="chrome"/>
<classes><class name="[Link]"/></classes>
</test>
<test name="Firefox Suite">
<parameter name="browser" value="firefox"/>
<classes><class name="[Link]"/></classes>
</test>
<test name="Edge Suite">
<parameter name="browser" value="edge"/>
<classes><class name="[Link]"/></classes>
</test>
</suite>

11) Log4j2 Configuration (src/test/resources/[Link])


<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:[Link]} %-5level [%t] %c{1} - %msg%n"/>
</Console>
<RollingFile name="File" fileName="target/logs/[Link]"
filePattern="target/logs/test-%d{yyyy-MM-dd}-%[Link]">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="10 MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>

12) Extent Reports Listener


package listeners;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link].*;
import [Link];

public class ExtentReportListener implements ITestListener {


private static ExtentReports extent = createInstance();
private static ThreadLocal<ExtentTest> test = new ThreadLocal<>();

private static ExtentReports createInstance() {


ExtentSparkReporter spark = new
ExtentSparkReporter("target/extent-report/[Link]");
ExtentReports ext = new ExtentReports(); [Link](spark); return
ext;
}

public void onTestStart(ITestResult r)


{ [Link]([Link]([Link]().getMethodName())); }
public void onTestSuccess(ITestResult r) { [Link]().log([Link], "Test
passed"); }
public void onTestFailure(ITestResult r) { String p =
[Link]([Link]().getMethodName());
[Link]().fail([Link]()); if (p!=null) try
{ [Link]().addScreenCaptureFromPath(p);} catch (Exception e) {} }
public void onTestSkipped(ITestResult r) { [Link]().log([Link], "Test
skipped"); }
public void onFinish(ITestContext c) { [Link](); }
}

13) Cucumber BDD: Feature, Steps, Runner & Hooks


Feature: Login
As a user I want to login so that I can see my home page

Scenario Outline: Valid login


Given I navigate to the application
When I login with username "<user>" and password "<pass>"
Then I should see the home page title "<title>"

Examples:
| user | pass | title |
| standard1 | secret123 | My Home Page |

package steps;

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

public class LoginSteps extends BaseTest {


private HomePage home;

@Given("I navigate to the application")


public void i_navigate_to_the_application() {
String baseUrl = [Link]().getProperty("baseUrl");
new LoginPage().goTo(baseUrl);
}

@When("I login with username {string} and password {string}")


public void i_login_with_username_and_password(String user, String pass) {
home = new
LoginPage().typeUsername(user).typePassword(pass).clickLoginSuccess();
}

@Then("I should see the home page title {string}")


public void i_should_see_the_home_page_title(String title) {
[Link]([Link](), title);
}
}

package runners;

import [Link];
import [Link];
import [Link];

@CucumberOptions(
features = "src/test/resources/features",
glue = {"steps"},
plugin = {"pretty", "json:target/[Link]", "html:target/[Link]"},
monochrome = true
)
public class TestRunner extends AbstractTestNGCucumberTests {
@Override
@DataProvider(parallel = true)
public Object[][] scenarios() { return [Link](); }
}

14) REST Assured API Test (Same Suite)


package api;

import [Link];
import [Link];
import [Link];

import static [Link];


import static [Link].*;

public class UserApiTest {


@Test
public void createUser_shouldReturn201() {
[Link] = "[Link]

given().contentType([Link]).body("{\"name\":\"morpheus\", \"job\":\"le
ader\"}")
.when().post("/users")
.then().statusCode(201).body("name", equalTo("morpheus"));
}
}

15) Jenkins Declarative Pipeline (Jenkinsfile)


pipeline {
agent any
options { timestamps() }
parameters {
choice(name: 'BROWSER', choices: ['chrome','firefox','edge'], description:
'Browser to run')
booleanParam(name: 'GRID', defaultValue: false, description: 'Run on
Selenium Grid?')
string(name: 'GRID_URL', defaultValue: '[Link]
description: 'Grid URL')
booleanParam(name: 'HEADLESS', defaultValue: true, description: 'Headless
mode')
}
stages {
stage('Checkout') { steps { checkout scm } }
stage('Build') { steps { sh 'mvn -B -q -DskipTests clean compile' } }
stage('Test UI & API') {
steps {
sh 'mvn -B test -Dbrowser=${BROWSER} -Dgrid=${GRID} -DgridUrl=$
{GRID_URL} -Dheadless=${HEADLESS}'
}
post {
always {
junit 'target/surefire-reports/*.xml'
publishHTML(target: [allowMissing: true, alwaysLinkToLastBuild: true,
keepAll: true, reportDir: 'target/extent-report', reportFiles: '[Link]',
reportName: 'Extent Report'])
}
}
}
}
}

16) Selenium Grid v4 on Docker Compose ([Link])


version: '3.8'
services:
selenium-hub:
image: selenium/hub:4.21.0
container_name: selenium-hub
ports:
- '4442:4442'
- '4443:4443'
- '4444:4444'
chrome:
image: selenium/node-chrome:4.21.0
shm_size: '2gb'
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
firefox:
image: selenium/node-firefox:4.21.0
shm_size: '2gb'
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
edge:
image: selenium/node-edge:4.21.0
shm_size: '2gb'
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
17) How to Run (Local, Grid, Jenkins)
Local run (Chrome, headed):
mvn clean test -Dbrowser=chrome -Dheadless=false

Run via Selenium Grid (Docker):


docker compose up -d
# Wait for Grid health page: [Link]
mvn test -Dgrid=true -DgridUrl=[Link] -Dbrowser=chrome

18) Why BDD (Cucumber) and How It Fits


• Business-readable scenarios for collaboration.

• Step Definitions call Page Objects. Hooks (@Before/@After) reuse BaseTest to manage
drivers.

• Parallel at scenario level via TestNG DataProvider override in Cucumber runner.

• Reports: Cucumber HTML/JSON + (optional) Extent Cucumber adapter.

19) Interview Q&A – Selenium Framework (Senior/Lead)


Q1. How do you make WebDriver thread-safe for parallel execution?

Answer: Use ThreadLocal<WebDriver> and avoid static driver references.

Q2. Same test in different browsers at the same time?

Answer: Use TestNG suite with <test> per browser (parallel="tests").

Q3. Wait strategy best practices?

Answer: Keep implicit wait = 0; centralize explicit waits; avoid [Link]; use
WebDriverWait.

Q4. POM vs Page Factory vs Fluent?

Answer: Classic POM = By locators; Page Factory = @FindBy; Fluent POM returns next page
and allows chaining.

Q5. Failures & screenshots?

Answer: [Link] -> TakesScreenshot -> attach to Extent report.

Q6. Flaky test reduction?

Answer: Stable locators, proper waits, deterministic test data, optional retry.
Q7. Config & secrets in CI?

Answer: Properties + -D overrides; secrets via Jenkins credentials.

Q8. UI + API in one pipeline?

Answer: Yes; same Maven run or split by groups.

Q9. How to scale with Grid?

Answer: Hub + multiple nodes; tune TestNG thread-count and containers.

Q10. Data for parallel runs?

Answer: DataProvider(parallel=true); use independent rows or generate unique data.

Q11. Extent vs Allure?

Answer: Extent = easy HTML; Allure = trend/history; choose per standards.

Q12. Maintainability & reviews?

Answer: SRP, DRY, naming, avoid sleeps, linting, PR checks.

20) Bonus: RetryAnalyzer


package utils;

import [Link];
import [Link];

public class RetryAnalyzer implements IRetryAnalyzer {


private int count = 0;
private static final int MAX_RETRY = 1;

public boolean retry(ITestResult result) {


if (count < MAX_RETRY) { count++; return true; }
return false;
}
}

You might also like