Page Object Model(POM) Automation Testing Framework
This blog post will guide you through setting up a robust Automation testing
framework for Salesforce using Selenium, TestNG, and ExtentReports. This
framework incorporates best practices for maintainability, reusability, and
reporting.
1. Project Directory Structure
Before diving into the code, let's establish the project's directory
structure:
SalesforceTesting/
├── src/
│ ├── main/
│ │
└── java/
│ │
└── com/salesforce/
│ │
├── base/
│ │
│ ├── BaseTest.java
│ │
│ └── DriverManager.java
│ │
├── common/
│ │
│ └── Utility.java
│ │
├── listeners/
│ │
│ └── TestListener.java
│ │
├── pages/
│ │
│ └── LoginPage.java
│ │
├── reporting/
│ │
│ └── ExtentReportManager.java
│ │
└── repository/
│ │ └── LocatorsRepository.java
│ └── test/
│ └── java/
│ └── com/salesforce/
│ └── tests/
│ └── LoginTest.java
├── pom.xml
├── testng.xml
└── run.bat
2. Core Dependencies (pom.xml)
First, let's define the necessary dependencies in your pom.xml file:
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.automationCodes</groupId>
<artifactId>pageobject</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<selenium.version>4.16.1</selenium.version>
<testng.version>7.8.0</testng.version>
<webdrivermanager.version>5.6.3</webdrivermanager.version>
<extentreports.version>5.1.1</extentreports.version>
<commons.io.version>2.13.0</commons.io.version>
</properties>
<dependencies>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
</dependency>
<!-- WebDriverManager -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${webdrivermanager.version}</version>
</dependency>
<!-- Extent Reports -->
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>${extentreports.version}</version>
</dependency>
<!-- Apache Commons IO -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>
Explanation:
Ø
Selenium: For browser automation.
Ø
TestNG: Testing framework for structuring and running tests.
Ø
WebDriverManager: Simplifies WebDriver setup (no more manual driver downloads!).
Ø
ExtentReports: For generating detailed and visually appealing test reports.
Ø
Apache Commons IO: Useful utility for file operations.
3. Base Components
3.1. BaseTest.java
This abstract class handles the setup and teardown of your test
environment.
package
com.salesforce.base;
import
org.openqa.selenium.WebDriver;
import
org.testng.annotations.AfterSuite;
import
org.testng.annotations.BeforeSuite;
import
org.testng.annotations.Listeners;
@Listeners(com.salesforce.listeners.TestListener.class)
public
abstract class BaseTest {
protected WebDriver driver;
@BeforeSuite
public void setUpSuite() {
// Initialize ExtentReports
com.salesforce.reporting.ExtentReportManager.getInstance();
}
@BeforeSuite
public void launchSalesforce() {
driver = DriverManager.getDriver();
String salesforceUrl =
"https://login.salesforce.com"; // Salesforce login URL
driver.get(salesforceUrl);
System.out.println("Launched
Salesforce URL: " + salesforceUrl);
}
@AfterSuite
public void tearDownSuite() {
// Quit the driver and flush
ExtentReports
DriverManager.quitDriver();
com.salesforce.reporting.ExtentReportManager.getInstance().flush();
}
}
Explanation:
Ø
@Listeners(com.salesforce.listeners.TestListener.class): This tells TestNG to use our custom TestListener class to handle test
events (start, success, failure, etc.).
Ø
setUpSuite(): Initializes
ExtentReports before the test suite begins.
Ø
launchSalesforce(): Launches the
Salesforce login page using the DriverManager.
Ø
tearDownSuite(): Quits the
WebDriver and flushes ExtentReports after the test suite completes.
3.2. DriverManager.java
This class manages the WebDriver instance. It ensures that only one
WebDriver instance exists per test run and provides a centralized way to access
and quit the driver.
package
com.salesforce.base;
import
org.openqa.selenium.WebDriver;
import
org.openqa.selenium.chrome.ChromeDriver;
import
org.openqa.selenium.edge.EdgeDriver;
import
org.openqa.selenium.firefox.FirefoxDriver;
import
io.github.bonigarcia.wdm.WebDriverManager;
public class
DriverManager {
private static WebDriver driver;
private DriverManager() {}
public static WebDriver getDriver() {
if(driver == null) {
String browser =
System.getProperty("browser", "chrome");
switch(browser.toLowerCase()) {
case "chrome":
WebDriverManager.chromedriver().setup();
driver = new
ChromeDriver();
break;
case "firefox":
WebDriverManager.firefoxdriver().setup();
driver = new
FirefoxDriver();
break;
case "edge":
WebDriverManager.edgedriver().setup();
driver = new EdgeDriver();
break;
default:
throw new
IllegalStateException("Invalid browser: " + browser);
}
driver.manage().window().maximize();
}
return driver;
}
public static void quitDriver() {
if(driver != null) {
driver.quit();
driver = null;
}
}
}
Explanation:
Ø getDriver(): This method
implements the Singleton pattern. It checks if a WebDriver instance already
exists. If not, it creates one based on the browser specified in the browser
system property (defaults to Chrome). It uses WebDriverManager to automatically
download and set up the appropriate driver.
Ø quitDriver(): This method
closes the WebDriver instance and sets the driver variable to null.
Utility Class
4.1 Utility.java
This class provides reusable utility methods for common Selenium
actions.
Code Implementation
package
com.salesforce.common;
import
org.openqa.selenium.*;
import
org.openqa.selenium.interactions.Actions;
import
org.openqa.selenium.support.ui.ExpectedConditions;
import
org.openqa.selenium.support.ui.WebDriverWait;
import
com.salesforce.base.DriverManager;
import
java.time.Duration;
import
java.util.List;
import
java.util.stream.Collectors;
public class
Utility {
private static final int TIMEOUT = 10;
public static WebElement waitForElement(By
locator) {
return new
WebDriverWait(DriverManager.getDriver(), Duration.ofSeconds(TIMEOUT))
.until(ExpectedConditions.presenceOfElementLocated(locator));
}
public static void openPage(String url) {
DriverManager.getDriver().get(url);
}
public static void click(By locator) {
waitForElement(locator).click();
}
public static void sendKeys(By locator,
String text) {
WebElement element =
waitForElement(locator);
element.clear();
element.sendKeys(text);
}
public static void doubleClick(By locator)
{
new Actions(DriverManager.getDriver())
.doubleClick(waitForElement(locator))
.perform();
}
public static void switchToWindow(int
index) {
List<String> windows =
DriverManager.getDriver().getWindowHandles().stream().collect(Collectors.toList());
DriverManager.getDriver().switchTo().window(windows.get(index));
}
public static boolean isElementPresent(By
locator) {
try {
waitForElement(locator);
return true;
} catch(TimeoutException e) {
return false;
}
}
}
Explanation
Ø
waitForElement(By locator): Waits for an element to be present on the page before interacting with
it. This helps prevent flaky tests caused by timing issues.
Ø
click(By locator): Clicks on an element.
Ø
sendKeys(By locator, String text): Enters text into an element. It first clears the field to ensure no
previous text remains.
Ø
doubleClick(By locator): Double clicks on an element.
Ø
switchToWindow(int index): Switches to a specific window based on its index.
Ø
isElementPresent(By locator): Checks if an element is present on the page.
Test Listener
5.1 TestListener.java
This class implements the ITestListener interface and is used to capture test execution events and log them to
the ExtentReports.
Code Implementation
package
com.salesforce.listeners;
import
com.aventstack.extentreports.ExtentTest;
import
com.aventstack.extentreports.Status;
import
com.salesforce.base.DriverManager;
import
com.salesforce.reporting.ExtentReportManager;
import
org.testng.ITestContext;
import
org.testng.ITestListener;
import
org.testng.ITestResult;
public class
TestListener implements ITestListener {
private static
ThreadLocal<ExtentTest> extentTest = new ThreadLocal<>();
@Override
public void onTestStart(ITestResult result)
{
ExtentTest test =
ExtentReportManager.getInstance()
.createTest(result.getMethod().getMethodName());
extentTest.set(test);
}
@Override
public void onTestSuccess(ITestResult
result) {
extentTest.get().log(Status.PASS,
"Test Passed");
}
@Override
public void onTestFailure(ITestResult
result) {
extentTest.get().log(Status.FAIL,
"Test Failed");
extentTest.get().fail(result.getThrowable());
}
@Override
public void onTestSkipped(ITestResult
result) {
extentTest.get().log(Status.SKIP,
"Test Skipped");
}
@Override
public void onFinish(ITestContext context)
{
ExtentReportManager.getInstance().flush();
DriverManager.quitDriver();
}
}
Explanation
Ø
onTestStart(ITestResult result): This method is called when a test starts. It creates a new ExtentTest
instance and associates it with the current thread.
Ø
onTestSuccess(ITestResult result): Logs a "Pass" status to the Extent report when a test
passes.
Ø
onTestFailure(ITestResult result): Logs a "Fail" status and the exception to the Extent report
when a test fails.
Ø
onTestSkipped(ITestResult result): Logs a "Skip" status to the Extent report when a test is
skipped.
Ø
onFinish(ITestContext context): Flushes the Extent report and quits the WebDriver after all tests in
the suite have finished.
Page Object Model (POM)
6.1 LoginPage.java
This class represents the Salesforce login page. It encapsulates the
locators and actions related to the login page.
Code Implementation
package
com.salesforce.pages;
import
com.salesforce.common.Utility;
import
com.salesforce.repository.LocatorsRepository;
public class
LoginPage {
public void enterUsername(String username)
{
Utility.sendKeys(LocatorsRepository.LoginPage.USERNAME, username);
}
public void enterPassword(String password)
{
Utility.sendKeys(LocatorsRepository.LoginPage.PASSWORD, password);
}
public void clickLogin() {
Utility.click(LocatorsRepository.LoginPage.LOGIN_BUTTON);
}
public boolean isErrorMessageDisplayed() {
return
Utility.isElementPresent(LocatorsRepository.LoginPage.ERROR_MESSAGE);
}
public void loginWithCredentials(String
username, String password) {
enterUsername(username);
enterPassword(password);
clickLogin();
}
}
Explanation
Ø
Each method in this class represents an action that
can be performed on the login page (e.g., enterUsername, enterPassword, clickLogin).
Ø
The locators for the elements on the login page are
stored in the LocatorsRepository class.
6.2.
LocatorsRepository.java
This
class stores all the locators for the elements in the application.
package com.salesforce.repository;
import org.openqa.selenium.By;
public class LocatorsRepository {
public
static class LoginPage {
public static final By USERNAME =
By.xpath("//input[@id='username']");
public static final By PASSWORD =
By.xpath("//input[@id='password']");
public static final By LOGIN_BUTTON =
By.xpath("//input[@id='Login']");
public static final By ERROR_MESSAGE = By.xpath("//div[@id='error']");
public static final By REMEMBER_ME =
By.xpath("//input[@id='rememberUn']");
}
}
Explanation:
Ø This class uses nested static
classes to group locators by page. This makes it easier to find and maintain
locators.
Ø All locators are defined as public static final By variables. This ensures that
they are constants and can be accessed from anywhere in the project.
7. Reporting
7.1. ExtentReportManager.java
This
class manages the ExtentReports instance.
package com.salesforce.reporting;
import com.aventstack.extentreports.ExtentReports;
import
com.aventstack.extentreports.reporter.ExtentSparkReporter;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ExtentReportManager {
private
static ExtentReports extent;
public
static ExtentReports getInstance() {
if
(extent == null) {
String timestamp = new
SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String reportName = "Salesforce-Test-Report-" + timestamp +
".html";
ExtentSparkReporter reporter = new
ExtentSparkReporter("reports/" + reportName);
extent = new ExtentReports();
extent.attachReporter(reporter);
extent.setSystemInfo("Created By", "AutomationCodes by
Ankit Vijay");
extent.setSystemInfo("env", "qa");
extent.setSystemInfo("Browser",
System.getProperty("browser", "chrome"));
}
return extent;
}
}
Explanation:
Ø getInstance(): This method implements the Singleton pattern to
ensure that only one ExtentReports instance exists.
Ø It creates a new ExtentSparkReporter instance and attaches it to the ExtentReports instance. The report is saved to the reports directory with a timestamped filename.
Ø It sets system information such
as the environment and browser.
8. Test Cases
8.1. LoginTest.java
This
class contains the test cases for the login page.
package com.salesforce.tests;
import com.salesforce.base.BaseTest;
import com.salesforce.pages.LoginPage;
import static org.testng.Assert.assertTrue;
import org.testng.annotations.Test;
public class LoginTest extends BaseTest {
@Test
public
void testInvalidLogin() {
LoginPage loginPage = new LoginPage();
loginPage.loginWithCredentials("invalid@user.com",
"wrongpass");
assertTrue(loginPage.isErrorMessageDisplayed());
}
@Test
public
void testValidLogin() {
LoginPage
loginPage = new LoginPage();
loginPage.loginWithCredentials("valid@user.com",
"correctpass");
//
Add validation for successful login
}
}
Explanation:
Ø testInvalidLogin(): This test case attempts to log in with invalid
credentials and verifies that the error message is displayed.
Ø testValidLogin(): This test case attempts to log in with valid
credentials. You would need to add validation to confirm a successful login
(e.g., check for the presence of an element on the home page).
9. TestNG Configuration (testng.xml)
This file
configures the TestNG test suite.
<!DOCTYPE suite SYSTEM
"https://testng.org/testng-1.0.dtd">
<suite name="Salesforce Test Suite"
verbose="1">
<!--
1. Define parameters -->
<parameter name="browser" value="chrome" />
<parameter name="env" value="A" />
<!--
2. Define listeners -->
<listeners>
<listener
class-name="com.salesforce.listeners.TestListener" />
</listeners>
<!--
3. Define parallel execution -->
<test
name="Parallel Tests">
<classes>
<class name="com.salesforce.tests.LoginTest" />
</classes>
</test>
</suite>
Explanation:
Ø <suite>: Defines the test suite.
Ø <parameter>: Defines parameters that can be used in the tests
(e.g., browser, environment).
Ø <listeners>: Specifies the listeners to use for the test
suite.
Ø <test>: Defines a test.
Ø <classes>: Specifies the classes to include in the test.
10. Execution Script (run.bat)
This
batch script simplifies the execution of the tests.
@echo off
REM Navigate to the project directory (if needed)
REM cd
C:\Users\anvij\eclipse-workspace_Test\pageobject
REM Run Maven command to execute tests
mvn clean test
REM Pause to see the output
Pause
Explanation:
Ø mvn clean test: This command tells Maven to clean the project
(remove previously built files) and then run the tests.
Ø pause: This command pauses the script after the tests
have finished, so you can see the output.
0 Comments