
Comprehensive notes covering Selenium WebDriver, TestNG/PyTest frameworks, Page Object Model, advanced interactions, Selenium Grid, and CI/CD integration with Jenkins.
Selenium is the most widely used open-source test automation framework for web applications. It supports multiple programming languages (Java, Python, C#, JavaScript, Ruby, and more) and all major browsers (Chrome, Firefox, Edge, Safari, and Opera).
Selenium WebDriver is the core component that directly communicates with the browser using browser-specific drivers. Unlike its predecessor Selenium RC (Remote Control), WebDriver does not require a separate server and interacts directly with the browser's native automation API. This makes it faster, more reliable, and capable of handling modern web applications with dynamic content.
Key Features of Selenium WebDriver:
pom.xml.
<!-- pom.xml dependencies -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.15.0</version>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.6.2</version>
</dependency>
pip install seleniumpip install webdriver-manager# Install Selenium and WebDriver Manager pip install selenium webdriver-manager
Selenium WebDriver uses a client-server architecture where the test script (client) communicates with the browser driver (server) via HTTP. The browser driver controls the actual browser instance.
Browser Drivers:
WebDriver Architecture Flow:
driver.findElement(By.id("username"))).Locating elements accurately is the foundation of successful test automation. Selenium provides multiple locator strategies:
Java
// By ID (most reliable)
WebElement username = driver.findElement(By.id("username"));
// By Name
WebElement password = driver.findElement(By.name("password"));
// By Class Name
WebElement message = driver.findElement(By.className("alert-success"));
// By CSS Selector
WebElement loginBtn = driver.findElement(By.cssSelector("button.submit-btn"));
// By XPath (use sparingly)
WebElement link = driver.findElement(By.xpath("//a[text()='Forgot Password']"));
// By Link Text
WebElement homeLink = driver.findElement(By.linkText("Home"));
// By Partial Link Text
WebElement aboutLink = driver.findElement(By.partialLinkText("About"));
// By Tag Name
WebElement heading = driver.findElement(By.tagName("h1"));
Python
# By ID (most reliable)
username = driver.find_element(By.ID, "username")
# By Name
password = driver.find_element(By.NAME, "password")
# By Class Name
message = driver.find_element(By.CLASS_NAME, "alert-success")
# By CSS Selector
login_btn = driver.find_element(By.CSS_SELECTOR, "button.submit-btn")
# By XPath (use sparingly)
link = driver.find_element(By.XPATH, "//a[text()='Forgot Password']")
# By Link Text
home_link = driver.find_element(By.LINK_TEXT, "Home")
# By Partial Link Text
about_link = driver.find_element(By.PARTIAL_LINK_TEXT, "About")
# By Tag Name
heading = driver.find_element(By.TAG_NAME, "h1")
ID and CSS Selector over XPath as they are faster and more reliable. Use XPath only when other locators are not feasible. Always use WebDriverWait with appropriate conditions for dynamic elements.
Java
// Navigate to a URL
driver.get("https://example.com");
// Navigate back and forward
driver.navigate().back();
driver.navigate().forward();
// Refresh the page
driver.navigate().refresh();
// Get page title
String title = driver.getTitle();
// Get current URL
String currentUrl = driver.getCurrentUrl();
// Maximize window
driver.manage().window().maximize();
// Set window size
driver.manage().window().setSize(new Dimension(1024, 768));
Python
# Navigate to a URL
driver.get("https://example.com")
# Navigate back and forward
driver.back()
driver.forward()
# Refresh the page
driver.refresh()
# Get page title
title = driver.title
# Get current URL
current_url = driver.current_url
# Maximize window
driver.maximize_window()
# Set window size
driver.set_window_size(1024, 768)
Java
// Text input
driver.findElement(By.id("username")).sendKeys("admin");
// Clear and type
driver.findElement(By.id("search")).clear();
driver.findElement(By.id("search")).sendKeys("Selenium");
// Button click
driver.findElement(By.id("login-btn")).click();
// Dropdown (using Select class)
Select dropdown = new Select(driver.findElement(By.id("country")));
dropdown.selectByVisibleText("United Kingdom");
dropdown.selectByValue("GB");
dropdown.selectByIndex(2);
// Checkbox
WebElement checkbox = driver.findElement(By.id("terms"));
if (!checkbox.isSelected()) {
checkbox.click();
}
// Radio button
driver.findElement(By.cssSelector("input[value='male']")).click();
// Dynamic elements - wait for visibility
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement dynamicElement = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.id("dynamic-content"))
);
Python
# Text input
driver.find_element(By.ID, "username").send_keys("admin")
# Clear and type
driver.find_element(By.ID, "search").clear()
driver.find_element(By.ID, "search").send_keys("Selenium")
# Button click
driver.find_element(By.ID, "login-btn").click()
# Dropdown (using Select class)
from selenium.webdriver.support.ui import Select
dropdown = Select(driver.find_element(By.ID, "country"))
dropdown.select_by_visible_text("United Kingdom")
dropdown.select_by_value("GB")
dropdown.select_by_index(2)
# Checkbox
checkbox = driver.find_element(By.ID, "terms")
if not checkbox.is_selected():
checkbox.click()
# Radio button
driver.find_element(By.CSS_SELECTOR, "input[value='male']").click()
# Dynamic elements - wait for visibility
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
dynamic_element = wait.until(
EC.visibility_of_element_located((By.ID, "dynamic-content"))
)
Implicit Wait — sets a default timeout for all find operations. It tells WebDriver to wait for a specified time before throwing an exception if an element is not found.
Java
// Implicit wait (global timeout)
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
Python
# Implicit wait (global timeout)
driver.implicitly_wait(10)
Explicit Wait — waits for a specific condition on a specific element. More precise and flexible than implicit waits.
Java
// Explicit wait for element visibility
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement element = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.id("result"))
);
// Clickable condition
WebElement button = wait.until(
ExpectedConditions.elementToBeClickable(By.id("submit-btn"))
);
// Alert presence
wait.until(ExpectedConditions.alertIsPresent());
// Custom condition
wait.until(driver -> driver.findElement(By.id("status")).getText().equals("Complete"));
Python
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Explicit wait for element visibility
wait = WebDriverWait(driver, 10)
element = wait.until(
EC.visibility_of_element_located((By.ID, "result"))
)
# Clickable condition
button = wait.until(
EC.element_to_be_clickable((By.ID, "submit-btn"))
)
# Alert presence
wait.until(EC.alert_is_present())
# Custom condition
wait.until(lambda d: d.find_element(By.ID, "status").text == "Complete")
Java
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import io.github.bonigarcia.wdm.WebDriverManager;
public class LoginTest {
public static void main(String[] args) {
// Setup WebDriverManager
WebDriverManager.chromedriver().setup();
WebDriver driver = new ChromeDriver();
try {
// Navigate to login page
driver.get("https://example.com/login");
// Login
driver.findElement(By.id("username")).sendKeys("testuser");
driver.findElement(By.id("password")).sendKeys("password123");
driver.findElement(By.id("login-btn")).click();
// Verify login
String expectedTitle = "Dashboard";
String actualTitle = driver.getTitle();
if (actualTitle.equals(expectedTitle)) {
System.out.println("Login successful!");
} else {
System.out.println("Login failed. Title: " + actualTitle);
}
} finally {
// Clean up
driver.quit();
}
}
}
Python
from selenium import webdriver
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.service import Service
# Setup WebDriver
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
try:
# Navigate to login page
driver.get("https://example.com/login")
# Login
driver.find_element(By.ID, "username").send_keys("testuser")
driver.find_element(By.ID, "password").send_keys("password123")
driver.find_element(By.ID, "login-btn").click()
# Verify login
expected_title = "Dashboard"
actual_title = driver.title
if actual_title == expected_title:
print("Login successful!")
else:
print(f"Login failed. Title: {actual_title}")
finally:
# Clean up
driver.quit()
A test automation framework provides a structured environment for writing, organizing, and executing tests. Key components include:
TestNG is a powerful testing framework inspired by JUnit but with additional features. It provides annotations to control test execution order, grouping, and dependencies.
Java
import org.testng.annotations.*;
public class TestNGExample {
@BeforeSuite
public void beforeSuite() {
System.out.println("Before Suite - Setup once before all tests");
}
@BeforeTest
public void beforeTest() {
System.out.println("Before Test - Setup before test block");
}
@BeforeClass
public void beforeClass() {
System.out.println("Before Class - Setup before class");
}
@BeforeMethod
public void beforeMethod() {
System.out.println("Before Method - Setup before each test");
}
@Test(priority = 1, groups = {"smoke", "regression"})
public void testLogin() {
System.out.println("Test Login");
// Test logic
}
@Test(priority = 2, groups = {"regression"})
public void testDashboard() {
System.out.println("Test Dashboard");
// Test logic
}
@Test(expectedExceptions = NullPointerException.class)
public void testException() {
// Test that expects an exception
throw new NullPointerException();
}
@Test(timeOut = 5000)
public void testTimeout() {
// Test that must complete within 5 seconds
}
@AfterMethod
public void afterMethod() {
System.out.println("After Method - Cleanup after each test");
}
@AfterClass
public void afterClass() {
System.out.println("After Class - Cleanup after class");
}
@AfterTest
public void afterTest() {
System.out.println("After Test - Cleanup after test block");
}
@AfterSuite
public void afterSuite() {
System.out.println("After Suite - Cleanup after all tests");
}
}
PyTest is a mature, feature-rich testing framework for Python. It's simple to learn, highly extensible, and widely used in the Python community.
Python
import pytest
def setup_module():
print("Setup Module - Runs once before all tests in module")
def teardown_module():
print("Teardown Module - Runs once after all tests in module")
class TestLogin:
@classmethod
def setup_class(cls):
print("Setup Class - Runs once before all tests in class")
@classmethod
def teardown_class(cls):
print("Teardown Class - Runs once after all tests in class")
def setup_method(self):
print("Setup Method - Runs before each test")
def teardown_method(self):
print("Teardown Method - Runs after each test")
@pytest.mark.smoke
def test_valid_login(self):
print("Test Valid Login")
assert True
@pytest.mark.regression
def test_dashboard(self):
print("Test Dashboard")
assert True
@pytest.mark.skip(reason="Feature not implemented yet")
def test_forgot_password(self):
print("Test Forgot Password")
@pytest.mark.xfail
def test_failing_feature(self):
print("This test is expected to fail")
assert False
@pytest.mark.parametrize("username,password,expected", [
("admin", "admin123", "success"),
("user", "wrongpass", "failure"),
("", "password", "error")
])
def test_multiple_login_scenarios(self, username, password, expected):
print(f"Testing login with {username} and {password}")
# Test logic
assert expected in ["success", "failure", "error"]
Java
import org.testng.Assert;
import org.testng.annotations.Test;
public class AssertionExample {
@Test
public void testAssertions() {
// Equals assertions
Assert.assertEquals(actual, expected);
Assert.assertEquals(actual, expected, "Custom error message");
// Boolean assertions
Assert.assertTrue(condition);
Assert.assertFalse(condition);
// Null assertions
Assert.assertNull(object);
Assert.assertNotNull(object);
// Fail test
Assert.fail("Test failed due to unexpected condition");
// Exception handling with try-catch
try {
driver.findElement(By.id("non-existent"));
} catch (NoSuchElementException e) {
// Log or handle exception
System.out.println("Element not found: " + e.getMessage());
}
// Soft assertions (multiple assertions in one test)
SoftAssert softAssert = new SoftAssert();
softAssert.assertEquals(actual1, expected1);
softAssert.assertTrue(condition2);
softAssert.assertEquals(actual3, expected3);
softAssert.assertAll(); // Call at the end to report all failures
}
}
Python
import pytest
def test_assertions():
# Equals assertions
assert actual == expected
assert actual == expected, "Custom error message"
# Boolean assertions
assert condition
assert not condition
# Null assertions
assert obj is None
assert obj is not None
# Contains assertions
assert "text" in string
assert item in list
# Type assertions
assert isinstance(value, int)
# Exception handling with pytest.raises
with pytest.raises(ValueError):
int("not a number")
# Multiple assertions with soft assertions
# Using pytest-check package or manual collection
errors = []
try:
assert actual1 == expected1
except AssertionError as e:
errors.append(str(e))
try:
assert condition2
except AssertionError as e:
errors.append(str(e))
if errors:
pytest.fail("\n".join(errors))
Java
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataDrivenTest {
@DataProvider(name = "loginData")
public Object[][] getLoginData() {
return new Object[][] {
{"admin", "admin123", true},
{"user", "wrongpass", false},
{"", "password", false}
};
}
@Test(dataProvider = "loginData")
public void testLogin(String username, String password, boolean expectedResult) {
System.out.println("Testing: " + username + " | " + password);
// Test logic with provided data
// Assert result matches expectedResult
}
// Data provider from Excel or CSV file
@DataProvider(name = "excelData")
public Object[][] getExcelData() {
return ExcelUtils.readData("testdata.xlsx", "Sheet1");
}
}
Python
import pytest
# Single parameter
@pytest.mark.parametrize("browser", ["chrome", "firefox", "edge"])
def test_cross_browser(browser):
print(f"Testing on {browser}")
# Test logic
# Multiple parameters
@pytest.mark.parametrize("username,password,expected", [
("admin", "admin123", True),
("user", "wrongpass", False),
("", "password", False)
])
def test_login_data(username, password, expected):
print(f"Testing: {username} | {password}")
# Test logic
# Assert result matches expected
# Parameterize with fixtures
@pytest.fixture(params=["chrome", "firefox", "edge"])
def browser(request):
return request.param
def test_browser_launch(browser):
print(f"Launching {browser} browser")
# Test logic
# Dynamic parameterization from external source
@pytest.fixture
def test_data():
return ExcelUtils.read_data("testdata.xlsx", "Sheet1")
def test_dynamic_data(test_data):
for username, password, expected in test_data:
# Test logic for each row
pass
Java
import org.testng.annotations.Test;
public class TestGrouping {
@Test(groups = {"smoke", "regression"}, priority = 1)
public void testLogin() {
// Smoke and regression test
}
@Test(groups = {"regression"}, priority = 2)
public void testDashboard() {
// Regression test only
}
@Test(groups = {"smoke"}, priority = 3)
public void testLogout() {
// Smoke test only
}
@Test(groups = {"regression", "integration"}, priority = 4)
public void testPayment() {
// Regression and integration test
}
// Dependent tests
@Test(dependsOnMethods = "testLogin")
public void testUserProfile() {
// Runs only if testLogin passes
}
// Always run even if dependent method fails
@Test(dependsOnMethods = "testLogin", alwaysRun = true)
public void testCleanup() {
// Runs even if testLogin fails
}
}
// Run specific groups in testng.xml:
// <groups>
// <run>
// <include name="smoke"/>
// </run>
// </groups>
Python
import pytest
# Define custom markers in pytest.ini:
# [pytest]
# markers =
# smoke: Smoke tests
# regression: Regression tests
# integration: Integration tests
@pytest.mark.smoke
@pytest.mark.regression
def test_login():
# Smoke and regression test
pass
@pytest.mark.regression
def test_dashboard():
# Regression test only
pass
@pytest.mark.smoke
def test_logout():
# Smoke test only
pass
@pytest.mark.regression
@pytest.mark.integration
def test_payment():
# Regression and integration test
pass
# Run specific markers:
# pytest -m smoke
# pytest -m "smoke and regression"
# pytest -m "not integration"
# Ordering with pytest-order plugin
@pytest.mark.order(1)
def test_first():
pass
@pytest.mark.order(2)
def test_second():
pass
# Dependency with pytest-dependency plugin
@pytest.mark.dependency()
def test_login():
assert True
@pytest.mark.dependency(depends=["test_login"])
def test_user_profile():
# Runs only if test_login passes
pass
test-output folder.Java
// ExtentReports setup
ExtentReports extent = new ExtentReports();
ExtentSparkReporter spark = new ExtentSparkReporter("extent-report.html");
extent.attachReporter(spark);
@Test
public void testWithReporting() {
ExtentTest test = extent.createTest("Login Test");
test.info("Navigating to login page");
// Test logic
test.pass("Login successful");
}
// TestNG Listener for custom logging
public class TestListener implements ITestListener {
@Override
public void onTestStart(ITestResult result) {
System.out.println("Started: " + result.getName());
}
@Override
public void onTestSuccess(ITestResult result) {
System.out.println("Passed: " + result.getName());
}
@Override
public void onTestFailure(ITestResult result) {
System.out.println("Failed: " + result.getName());
// Capture screenshot
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
}
}
Python
# Generate HTML reports with pytest-html
# pytest --html=report.html --self-contained-html
# Allure reports for PyTest
# pytest --alluredir=allure-results
# allure serve allure-results
# Custom logging
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('test.log'),
logging.StreamHandler()
]
)
def test_with_logging():
logger = logging.getLogger(__name__)
logger.info("Starting test")
# Test logic
logger.info("Test completed")
# pytest.ini configuration
# [pytest]
# log_cli = True
# log_cli_level = INFO
# log_cli_format = %(asctime)s - %(levelname)s - %(message)s
# log_file = test.log
# log_file_level = DEBUG
Design patterns are reusable solutions to common problems in software design. In test automation, they help create maintainable, scalable, and readable test code. The most important patterns for Selenium are:
The Page Object Model is a design pattern that creates an object repository for web UI elements. Each web page is represented as a class that:
Java
// Base Page class
public class BasePage {
protected WebDriver driver;
protected WebDriverWait wait;
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
public String getPageTitle() {
return driver.getTitle();
}
public void takeScreenshot(String name) {
File screenshot = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
// Save screenshot
}
}
// Login Page
public class LoginPage extends BasePage {
@FindBy(id = "username")
private WebElement usernameInput;
@FindBy(id = "password")
private WebElement passwordInput;
@FindBy(id = "login-btn")
private WebElement loginButton;
@FindBy(className = "error-message")
private WebElement errorMessage;
public LoginPage(WebDriver driver) {
super(driver);
PageFactory.initElements(driver, this);
}
public LoginPage enterUsername(String username) {
usernameInput.clear();
usernameInput.sendKeys(username);
return this;
}
public LoginPage enterPassword(String password) {
passwordInput.clear();
passwordInput.sendKeys(password);
return this;
}
public DashboardPage clickLogin() {
loginButton.click();
return new DashboardPage(driver);
}
public String getErrorMessage() {
wait.until(ExpectedConditions.visibilityOf(errorMessage));
return errorMessage.getText();
}
public boolean isLoginPageDisplayed() {
return usernameInput.isDisplayed();
}
}
// Dashboard Page
public class DashboardPage extends BasePage {
@FindBy(id = "welcome-message")
private WebElement welcomeMessage;
@FindBy(id = "logout-btn")
private WebElement logoutButton;
public DashboardPage(WebDriver driver) {
super(driver);
PageFactory.initElements(driver, this);
}
public String getWelcomeMessage() {
wait.until(ExpectedConditions.visibilityOf(welcomeMessage));
return welcomeMessage.getText();
}
public LoginPage logout() {
logoutButton.click();
return new LoginPage(driver);
}
public boolean isDashboardDisplayed() {
return welcomeMessage.isDisplayed();
}
}
Python
# Base Page
class BasePage:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def get_page_title(self):
return self.driver.title
def take_screenshot(self, name):
self.driver.save_screenshot(f"screenshots/{name}.png")
# Login Page
class LoginPage(BasePage):
def __init__(self, driver):
super().__init__(driver)
self.username_input = driver.find_element(By.ID, "username")
self.password_input = driver.find_element(By.ID, "password")
self.login_button = driver.find_element(By.ID, "login-btn")
self.error_message = driver.find_element(By.CLASS_NAME, "error-message")
def enter_username(self, username):
self.username_input.clear()
self.username_input.send_keys(username)
return self
def enter_password(self, password):
self.password_input.clear()
self.password_input.send_keys(password)
return self
def click_login(self):
self.login_button.click()
return DashboardPage(self.driver)
def get_error_message(self):
self.wait.until(EC.visibility_of(self.error_message))
return self.error_message.text
def is_login_page_displayed(self):
return self.username_input.is_displayed()
# Dashboard Page
class DashboardPage(BasePage):
def __init__(self, driver):
super().__init__(driver)
self.welcome_message = driver.find_element(By.ID, "welcome-message")
self.logout_button = driver.find_element(By.ID, "logout-btn")
def get_welcome_message(self):
self.wait.until(EC.visibility_of(self.welcome_message))
return self.welcome_message.text
def logout(self):
self.logout_button.click()
return LoginPage(self.driver)
def is_dashboard_displayed(self):
return self.welcome_message.is_displayed()
Page Factory is a built-in Selenium class that initializes page elements with lazy loading. It uses annotations like @FindBy (Java) or @FindBy (Python with pom.py) to define elements.
Java
// Page Factory annotations
public class LoginPage extends BasePage {
@FindBy(id = "username")
private WebElement usernameInput;
@FindBy(name = "password")
private WebElement passwordInput;
@FindBy(css = "button[type='submit']")
private WebElement loginButton;
@FindBy(xpath = "//div[@class='error']")
private WebElement errorMessage;
@FindBy(how = How.ID, using = "remember-me")
private WebElement rememberMeCheckbox;
// Element with multiple locators
@FindAll({
@FindBy(id = "username"),
@FindBy(name = "user")
})
private WebElement usernameAlternative;
// Caching element (for elements that are found once)
@CacheLookup
@FindBy(id = "page-header")
private WebElement pageHeader;
public LoginPage(WebDriver driver) {
super(driver);
PageFactory.initElements(driver, this);
}
}
Python
# Python equivalent using selenium-page-factory
from selenium_page_factory import PageFactory
class LoginPage(BasePage):
def __init__(self, driver):
super().__init__(driver)
self.username_input = driver.find_element(By.ID, "username")
self.password_input = driver.find_element(By.NAME, "password")
self.login_button = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
self.error_message = driver.find_element(By.XPATH, "//div[@class='error']")
self.remember_me = driver.find_element(By.ID, "remember-me")
# For lazy loading, use properties
@property
def username_input(self):
return self.driver.find_element(By.ID, "username")
@property
def password_input(self):
return self.driver.find_element(By.NAME, "password")
Java
// Locator constants class
public class Locators {
public static final String USERNAME_INPUT = "username";
public static final String PASSWORD_INPUT = "password";
public static final String LOGIN_BUTTON = "login-btn";
}
// Using constants
driver.findElement(By.id(Locators.USERNAME_INPUT)).sendKeys("admin");
Project Structure:
src/
├── main/
│ └── java/
│ └── com/company/
│ ├── pages/
│ │ ├── BasePage.java
│ │ ├── LoginPage.java
│ │ └── DashboardPage.java
│ ├── utils/
│ │ ├── DriverManager.java
│ │ ├── ConfigReader.java
│ │ └── ExcelReader.java
│ └── base/
│ └── TestBase.java
└── test/
└── java/
└── com/company/
├── tests/
│ ├── LoginTest.java
│ └── DashboardTest.java
└── testdata/
└── TestData.java
Java
// ConfigReader utility
public class ConfigReader {
private static Properties properties;
static {
properties = new Properties();
try {
FileInputStream fis = new FileInputStream("config.properties");
properties.load(fis);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getProperty(String key) {
return properties.getProperty(key);
}
public static String getBaseUrl() {
return getProperty("base.url");
}
public static String getBrowser() {
return getProperty("browser");
}
}
// DriverManager (Singleton pattern)
public class DriverManager {
private static WebDriver driver;
private DriverManager() {}
public static WebDriver getDriver() {
if (driver == null) {
String browser = ConfigReader.getBrowser();
switch (browser) {
case "chrome":
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
break;
case "firefox":
WebDriverManager.firefoxdriver().setup();
driver = new FirefoxDriver();
break;
default:
throw new IllegalArgumentException("Browser not supported");
}
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(
Duration.ofSeconds(Long.parseLong(
ConfigReader.getProperty("implicit.wait"))
)
);
}
return driver;
}
public static void quitDriver() {
if (driver != null) {
driver.quit();
driver = null;
}
}
}
Java
// Alert handling
Alert alert = driver.switchTo().alert();
// Get alert text
String alertText = alert.getText();
// Accept alert (OK)
alert.accept();
// Dismiss alert (Cancel)
alert.dismiss();
// Send text to prompt
alert.sendKeys("Enter some text");
alert.accept();
// Check if alert is present
try {
Alert alert = driver.switchTo().alert();
alert.accept();
} catch (NoAlertPresentException e) {
// No alert present
}
// Using WebDriverWait for alert
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.alertIsPresent());
Python
# Alert handling
alert = driver.switch_to.alert
# Get alert text
alert_text = alert.text
# Accept alert (OK)
alert.accept()
# Dismiss alert (Cancel)
alert.dismiss()
# Send text to prompt
alert.send_keys("Enter some text")
alert.accept()
# Check if alert is present
try:
alert = driver.switch_to.alert
alert.accept()
except NoAlertPresentException:
# No alert present
pass
# Using WebDriverWait for alert
wait = WebDriverWait(driver, 10)
wait.until(EC.alert_is_present())
Java
// Switch to iframe by index
driver.switchTo().frame(0);
// Switch to iframe by name/id
driver.switchTo().frame("iframe-name");
driver.switchTo().frame("iframe-id");
// Switch to iframe by WebElement
WebElement iframe = driver.findElement(By.id("my-iframe"));
driver.switchTo().frame(iframe);
// Switch to parent frame
driver.switchTo().parentFrame();
// Switch to default content
driver.switchTo().defaultContent();
// Handle nested frames
driver.switchTo().frame("outer-frame");
driver.switchTo().frame("inner-frame");
// Interact with elements in inner frame
driver.switchTo().defaultContent(); // Back to top
Python
# Switch to iframe by index
driver.switch_to.frame(0)
# Switch to iframe by name/id
driver.switch_to.frame("iframe-name")
driver.switch_to.frame("iframe-id")
# Switch to iframe by WebElement
iframe = driver.find_element(By.ID, "my-iframe")
driver.switch_to.frame(iframe)
# Switch to parent frame
driver.switch_to.parent_frame()
# Switch to default content
driver.switch_to.default_content()
# Handle nested frames
driver.switch_to.frame("outer-frame")
driver.switch_to.frame("inner-frame")
# Interact with elements in inner frame
driver.switch_to.default_content() # Back to top
Java // Get current window handle String mainWindow = driver.getWindowHandle(); // Get all window handles SetwindowHandles = driver.getWindowHandles(); // Switch to new window for (String handle : windowHandles) { if (!handle.equals(mainWindow)) { driver.switchTo().window(handle); break; } } // Close current window driver.close(); // Switch back to main window driver.switchTo().window(mainWindow); // Open new tab driver.switchTo().newWindow(WindowType.TAB); driver.get("https://example.com"); // Open new window driver.switchTo().newWindow(WindowType.WINDOW); driver.get("https://example.com");
Python
# Get current window handle
main_window = driver.current_window_handle
# Get all window handles
window_handles = driver.window_handles
# Switch to new window
for handle in window_handles:
if handle != main_window:
driver.switch_to.window(handle)
break
# Close current window
driver.close()
# Switch back to main window
driver.switch_to.window(main_window)
# Open new tab
driver.switch_to.new_window('tab')
driver.get("https://example.com")
# Open new window
driver.switch_to.new_window('window')
driver.get("https://example.com")
Java
import org.openqa.selenium.interactions.Actions;
Actions actions = new Actions(driver);
// Hover over element
WebElement element = driver.findElement(By.id("menu"));
actions.moveToElement(element).perform();
// Click and hold
actions.clickAndHold(element).perform();
// Double click
actions.doubleClick(element).perform();
// Right click
actions.contextClick(element).perform();
// Drag and drop
WebElement source = driver.findElement(By.id("source"));
WebElement target = driver.findElement(By.id("target"));
actions.dragAndDrop(source, target).perform();
// Key press (Ctrl + A)
actions.keyDown(Keys.CONTROL)
.sendKeys("a")
.keyUp(Keys.CONTROL)
.perform();
// Mouse wheel scroll
actions.scrollByAmount(0, 100).perform(); // Scroll down 100px
// Scroll to element
actions.scrollToElement(element).perform();
// Build complex action sequence
actions.moveToElement(element)
.click()
.keyDown(Keys.CONTROL)
.sendKeys("a")
.keyUp(Keys.CONTROL)
.build()
.perform();
Python
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
actions = ActionChains(driver)
# Hover over element
element = driver.find_element(By.ID, "menu")
actions.move_to_element(element).perform()
# Click and hold
actions.click_and_hold(element).perform()
# Double click
actions.double_click(element).perform()
# Right click
actions.context_click(element).perform()
# Drag and drop
source = driver.find_element(By.ID, "source")
target = driver.find_element(By.ID, "target")
actions.drag_and_drop(source, target).perform()
# Key press (Ctrl + A)
actions.key_down(Keys.CONTROL)\
.send_keys("a")\
.key_up(Keys.CONTROL)\
.perform()
# Mouse wheel scroll
actions.scroll_by_amount(0, 100).perform() # Scroll down 100px
# Scroll to element
actions.scroll_to_element(element).perform()
# Build complex action sequence
actions.move_to_element(element)\
.click()\
.key_down(Keys.CONTROL)\
.send_keys("a")\
.key_up(Keys.CONTROL)\
.perform()
Java
// Execute JavaScript
JavascriptExecutor js = (JavascriptExecutor) driver;
// Scroll to element
js.executeScript("arguments[0].scrollIntoView(true);", element);
// Scroll to bottom of page
js.executeScript("window.scrollTo(0, document.body.scrollHeight);");
// Get page title
String title = (String) js.executeScript("return document.title;");
// Highlight element
js.executeScript("arguments[0].style.border='3px solid red'", element);
// Click element via JavaScript
js.executeScript("arguments[0].click();", element);
// Set attribute
js.executeScript("arguments[0].setAttribute('disabled', 'true')", element);
// Get attribute
String value = (String) js.executeScript(
"return arguments[0].getAttribute('value');", element
);
// Create a custom JavaScript function
js.executeScript("window.testFunction = function() { return 'test'; }");
String result = (String) js.executeScript("return window.testFunction();");
Python
# Execute JavaScript
js = driver.execute_script
# Scroll to element
js("arguments[0].scrollIntoView(true);", element)
# Scroll to bottom of page
js("window.scrollTo(0, document.body.scrollHeight);")
# Get page title
title = js("return document.title;")
# Highlight element
js("arguments[0].style.border='3px solid red'", element)
# Click element via JavaScript
js("arguments[0].click();", element)
# Set attribute
js("arguments[0].setAttribute('disabled', 'true')", element)
# Get attribute
value = js("return arguments[0].getAttribute('value');", element)
# Create a custom JavaScript function
js("window.testFunction = function() { return 'test'; }")
result = js("return window.testFunction();")
Java
// Wait for AJAX to complete
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Wait for element to be present
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("ajax-content")));
// Wait for element to be visible
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("ajax-content")));
// Wait for element to be clickable
wait.until(ExpectedConditions.elementToBeClickable(By.id("ajax-button")));
// Wait for text to appear
wait.until(ExpectedConditions.textToBePresentInElementLocated(
By.id("status"), "Complete"
));
// Wait for attribute to contain value
wait.until(ExpectedConditions.attributeContains(
By.id("progress"), "aria-valuenow", "100"
));
// Wait for jQuery to finish
wait.until(driver -> (Boolean) ((JavascriptExecutor) driver)
.executeScript("return jQuery.active == 0"));
// Wait for document ready state
wait.until(driver -> "complete".equals(
((JavascriptExecutor) driver)
.executeScript("return document.readyState")
));
Java
// File upload
WebElement fileInput = driver.findElement(By.id("file-upload"));
fileInput.sendKeys("/path/to/file.txt");
// Multiple file upload
WebElement fileInput = driver.findElement(By.id("files"));
fileInput.sendKeys("/path/to/file1.txt\n/path/to/file2.txt");
// Download verification
// Use HTTP client or wait for file to appear in downloads folder
String downloadDir = System.getProperty("user.dir") + "/downloads";
File downloadedFile = new File(downloadDir + "/report.pdf");
// Wait for download to complete
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
wait.until(driver -> downloadedFile.exists() && downloadedFile.length() > 0);
// Validate file content
// Use Java I/O or Apache POI to read file content
Python
# File upload
file_input = driver.find_element(By.ID, "file-upload")
file_input.send_keys("/path/to/file.txt")
# Multiple file upload
file_input = driver.find_element(By.ID, "files")
file_input.send_keys("/path/to/file1.txt\n/path/to/file2.txt")
# Download verification
import os
import time
download_dir = os.path.join(os.getcwd(), "downloads")
downloaded_file = os.path.join(download_dir, "report.pdf")
# Wait for download to complete
timeout = 30
start_time = time.time()
while time.time() - start_time < timeout:
if os.path.exists(downloaded_file) and os.path.getsize(downloaded_file) > 0:
break
time.sleep(1)
# Validate file content
with open(downloaded_file, 'rb') as f:
content = f.read()
assert len(content) > 0
Selenium Grid allows you to run tests on different machines and browsers simultaneously. It uses a hub-node architecture:
# Start Hub (on hub machine)
java -jar selenium-server-standalone-4.15.0.jar hub
# Start Node (on node machine)
java -jar selenium-server-standalone-4.15.0.jar node \
--hub http://hub-ip:4444 \
--browser "chrome" \
--browser "firefox" \
--max-sessions 5
# Start Node with specific capabilities
java -jar selenium-server-standalone-4.15.0.jar node \
--hub http://hub-ip:4444 \
--browser "chrome" \
--browser "edge" \
--max-sessions 5 \
--detect-drivers false \
--driver-factory "chrome" "org.openqa.selenium.chrome.ChromeDriver" \
--driver-factory "edge" "org.openqa.selenium.edge.EdgeDriver"
<!-- testng.xml -->
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Parallel Test Suite" parallel="tests" thread-count="4">
<test name="Test on Chrome">
<parameter name="browser" value="chrome"/>
<classes>
<class name="com.company.tests.LoginTest"/>
<class name="com.company.tests.DashboardTest"/>
</classes>
</test>
<test name="Test on Firefox">
<parameter name="browser" value="firefox"/>
<classes>
<class name="com.company.tests.LoginTest"/>
<class name="com.company.tests.DashboardTest"/>
</classes>
</test>
<test name="Test on Edge">
<parameter name="browser" value="edge"/>
<classes>
<class name="com.company.tests.LoginTest"/>
<class name="com.company.tests.DashboardTest"/>
</classes>
</test>
</suite>
# Install pytest-xdist pip install pytest-xdist # Run tests in parallel with 4 workers pytest -n 4 # Run tests in parallel with auto-detected CPU count pytest -n auto # Run tests in parallel with specific test pattern pytest -n 4 tests/test_login.py tests/test_dashboard.py # Run tests across multiple nodes pytest -n 4 --dist loadscope # Distribute by test class/module pytest -n 4 --dist loadfile # Distribute by test file
# Docker Compose for Selenium Grid 4
# docker-compose.yml
version: '3'
services:
selenium-hub:
image: selenium/hub:4.15.0
container_name: selenium-hub
ports:
- "4442:4442"
- "4443:4443"
- "4444:4444"
chrome:
image: selenium/node-chrome:4.15.0
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
ports:
- "6900:5900"
firefox:
image: selenium/node-firefox:4.15.0
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
ports:
- "6901:5900"
edge:
image: selenium/node-edge:4.15.0
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_PUBLISH_PORT=4442
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
ports:
- "6902:5900"
# Start grid
docker-compose up -d
# Stop grid
docker-compose down
Java
// Grid connection with DesiredCapabilities
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setBrowserName("chrome");
capabilities.setPlatform(Platform.WINDOWS);
WebDriver driver = new RemoteWebDriver(
new URL("http://hub-ip:4444/wd/hub"),
capabilities
);
// With ChromeOptions
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless");
options.addArguments("--no-sandbox");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(ChromeOptions.CAPABILITY, options);
capabilities.setBrowserName("chrome");
WebDriver driver = new RemoteWebDriver(
new URL("http://hub-ip:4444/wd/hub"),
capabilities
);
Python
from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
# Grid connection with capabilities
capabilities = {
"browserName": "chrome",
"platform": "WINDOWS",
"version": "latest"
}
driver = RemoteWebDriver(
command_executor="http://hub-ip:4444/wd/hub",
desired_capabilities=capabilities
)
# With Chrome options
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
driver = RemoteWebDriver(
command_executor="http://hub-ip:4444/wd/hub",
options=options
)
Java
// Sauce Labs configuration
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("browserName", "chrome");
capabilities.setCapability("browserVersion", "119");
capabilities.setCapability("platformName", "Windows 10");
capabilities.setCapability("sauce:options", new SauceOptions()
.setUsername("your-username")
.setAccessKey("your-access-key")
.setBuild("Build-1")
.setName("Login Test")
);
WebDriver driver = new RemoteWebDriver(
new URL("https://ondemand.saucelabs.com/wd/hub"),
capabilities
);
CI/CD is the practice of automatically building, testing, and deploying software changes. In test automation, CI/CD ensures that tests are run on every code change, providing fast feedback to developers.
// Jenkinsfile (Declarative Pipeline)
pipeline {
agent any
tools {
maven 'Maven-3'
jdk 'JDK-17'
}
environment {
BROWSER = 'chrome'
HEADLESS = 'true'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Install Dependencies') {
steps {
sh 'mvn clean install -DskipTests'
}
}
stage('Run Tests') {
steps {
sh 'mvn test -Dbrowser=${BROWSER} -Dheadless=${HEADLESS}'
}
post {
always {
// Archive test results
junit 'target/surefire-reports/*.xml'
// Archive HTML reports
publishHTML([
reportDir: 'target/surefire-reports',
reportFiles: 'index.html',
reportName: 'Test Report'
])
}
}
}
stage('Report') {
steps {
// Generate Allure report
allure([
includeProperties: false,
jdk: '',
properties: [],
reportBuildPolicy: 'ALWAYS',
results: [[path: 'allure-results']]
])
}
}
}
post {
always {
// Cleanup
sh 'docker-compose down'
}
failure {
// Notify on failure
mail to: 'team@company.com',
subject: "Build failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Build failed. Check Jenkins for details."
}
}
}
// Jenkins pipeline triggers
triggers {
// Poll SCM every 5 minutes
pollSCM 'H/5 * * * *'
// Run daily at 2am
cron 'H 2 * * *'
// Run on specific days
cron 'H 2 * * 1,3,5' // Monday, Wednesday, Friday
}
// GitHub Actions scheduled trigger
# .github/workflows/playwright.yml
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 2 * * *' # Daily at 2am
workflow_dispatch: # Manual trigger
# Install Allure (macOS)
brew install allure
# Run tests with Allure reporting
# Java (TestNG)
mvn test
mvn allure:report
# Python (pytest)
pytest --alluredir=allure-results
allure serve allure-results
# Allure annotations
import io.qameta.allure.*;
@Severity(SeverityLevel.CRITICAL)
@Feature("Login")
@Story("User Authentication")
@Test
public void testLogin() {
Allure.step("Navigate to login page", () -> {
// Step logic
});
Allure.attachment("Screenshot", screenshot, "image/png");
}
Java
// Capture screenshot on failure
public void captureScreenshot(String fileName) {
File screenshot = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
String screenshotPath = "screenshots/" + fileName + ".png";
try {
FileUtils.copyFile(screenshot, new File(screenshotPath));
} catch (IOException e) {
e.printStackTrace();
}
}
// TestNG Listener for automatic screenshot on failure
public class ScreenshotListener implements ITestListener {
@Override
public void onTestFailure(ITestResult result) {
WebDriver driver = (WebDriver) result.getTestContext()
.getAttribute("driver");
File screenshot = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
// Attach to report
// Save to artifacts folder
}
}
Python
# Capture screenshot on failure
def capture_screenshot(driver, name):
driver.save_screenshot(f"screenshots/{name}.png")
# PyTest fixture for automatic screenshot on failure
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
if report.when == "call" and report.failed:
driver = item.funcargs.get('driver')
if driver:
driver.save_screenshot(f"screenshots/{item.name}.png")
# Logging configuration
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('test.log'),
logging.StreamHandler()
]
)
# Git workflow for test automation
# Initialize repository
git init
# Add remote
git remote add origin https://github.com/company/test-automation.git
# Create feature branch
git checkout -b feature/login-tests
# Add changes
git add .
git commit -m "Add login test cases"
# Push to remote
git push origin feature/login-tests
# Create pull request on GitHub
# GitHub Actions workflow
name: Test Automation
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
browser: [chrome, firefox, edge]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Run tests
run: mvn test -Dbrowser=${{ matrix.browser }}
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results-${{ matrix.browser }}
path: target/surefire-reports/
Project Objective: Build and deploy an end-to-end Selenium automation framework integrated with CI/CD pipelines.
Deliverables:
📚 References & Resources:
All references are provided for further study and research.