An advanced, feature-rich Selenium test automation framework implementing industry-standard design patterns and best practices. The framework focuses on robustness, maintainability, and scalability in test automation.
- Multi-strategy element interaction system
- Fallback mechanism for handling complex scenarios
- Three core strategies:
- Default WebDriver strategy with enhanced stability
- JavaScript-based interactions for complex DOM scenarios
- Actions API for advanced mouse and keyboard operations
- Strategy auto-selection based on context and success rates
- Progressive polling intervals based on system state
- Adaptive timeouts with context awareness
- Custom wait conditions with fluent interface
- Performance-optimized waiting strategies
- Auto-retry with exponential backoff
- Comprehensive state checking mechanisms
- Multi-factor validation approach
- Performance-optimized validation
- Custom validation rule support
- State change monitoring capabilities
- Dynamic retry strategies based on runtime conditions
- Network condition analysis
- Browser state monitoring
- Automatic strategy adjustment
- Custom retry rule configuration
- Alternative locator strategy generation
- Dynamic element relocation
- Success rate tracking
- Automatic locator optimization
- Failure pattern analysis
- Element interaction timing tracking
- Network request monitoring
- Resource usage tracking
- Performance metric collection
- Automatic bottleneck detection
- Real-time state change detection
- Network activity monitoring
- DOM mutation tracking
- Custom event handlers
- Asynchronous event processing
- Runtime configuration adjustment
- Performance-based optimization
- Environment-aware settings
- Metric-driven configuration
- Auto-tuning capabilities
-
Design Patterns Implementation
- Strategy Pattern for element interactions
- Singleton Pattern for resource management
- Factory Pattern for driver initialization
- Builder Pattern for test data creation
- Observer Pattern for event handling
- Command Pattern for action queueing
-
Smart Element Implementation
- Enhanced WebElement wrapper
- Built-in retry mechanisms
- Automatic error recovery
- Performance monitoring
- State tracking
- Event handling
-
Validation Framework
- Comprehensive element state checking
- Custom validation rules
- Chain validation support
- Async validation capabilities
- Result aggregation
-
Recovery Mechanisms
- Automatic error detection
- Strategic recovery attempts
- Custom recovery strategies
- Failure analysis
- Session recovery
-
Configuration Management
- Dynamic property adjustment
- Environment-based configuration
- Runtime optimization
- Profile management
- Remote configuration support
- Core: Java, Selenium WebDriver
- Testing: TestNG, Cucumber
- Reporting: ExtentReports
- Logging: Log4j2
- Build: Maven
- Utilities: Lombok, WebDriverManagerements industry-standard design patterns and best practices to create a robust, maintainable, and scalable test automation solution.
-
Design Patterns
- Strategy Pattern: Multiple element interaction strategies
- Singleton Pattern: Thread-safe WebDriver management
- Factory Pattern: Dynamic browser configuration
- Page Object Model: Maintainable page element management
- Builder Pattern: Flexible test data creation
- Observer Pattern: Event-based reporting
-
Core Features
- Smart element interactions with multiple strategies
- Dynamic waiting mechanisms with adaptive polling
- Comprehensive element state validation
- Automatic error recovery mechanisms
- Cross-browser testing (Chrome, Firefox, Edge)
- Parallel test execution support
- Screenshot capture on failure
- Comprehensive logging with Log4j2
- Configurable test environments
- Retry mechanism for flaky tests
-
Test Frameworks
- TestNG: For traditional test organization
- Cucumber: For BDD-style testing
- ExtentReports: For detailed reporting
- Data-driven testing support
-
Utilities
- Smart element interactions with retry logic
- Automatic screenshot capture
- Custom exception handling
- Configuration management
- Logging utilities
- Wait helper methods
- Language: Java
- Build Tool: Maven
- Testing: TestNG, Cucumber
- Automation: Selenium WebDriver
- Reporting: ExtentReports
- Logging: Log4j2
- Annotations: Lombok
- Dependencies: WebDriverManager, Cucumber-PicoContainer
@Test
public class LoginTest {
private static final Logger log = LogUtils.getLogger(LoginTest.class);
@Test
public void testSuccessfulLogin() {
// 1. Create test data using Builder Pattern
UserData userData = UserData.builder()
.username("testuser")
.password("password123")
.build();
// 2. Initialize page object with built-in driver management
LoginPage loginPage = new LoginPage(DriverManager.getInstance().getDriver());
// 3. Perform test actions with automatic logging and error handling
loginPage.login(userData.getUsername(), userData.getPassword());
// 4. Assertions with automatic screenshot on failure
Assert.assertTrue(loginPage.isLoggedIn(), "Login was not successful");
}
@Test(dataProvider = "invalidLoginData")
public void testFailedLogin(String username, String password, String expectedError) {
LoginPage loginPage = new LoginPage(DriverManager.getInstance().getDriver());
loginPage.login(username, password);
Assert.assertEquals(loginPage.getErrorMessage(), expectedError);
}
@DataProvider(name = "invalidLoginData")
public Object[][] getInvalidLoginData() {
return new Object[][] {
{"invalid", "wrong123", "Invalid credentials"},
{"", "", "Username and password are required"}
};
}
}
public class DriverManager {
private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
private static DriverManager instance = null;
private DriverManager() {}
public static synchronized DriverManager getInstance() {
if (instance == null) {
instance = new DriverManager();
}
return instance;
}
public WebDriver getDriver() {
WebDriver localDriver = driver.get();
if (localDriver == null) {
localDriver = DriverFactory.createDriver();
driver.set(localDriver);
}
return localDriver;
}
}
public class DriverFactory {
public static WebDriver createDriver() {
String browser = ConfigManager.getProperty("browser");
WebDriver driver;
switch (browser.toLowerCase()) {
case "chrome":
ChromeOptions options = new ChromeOptions();
if (Boolean.parseBoolean(ConfigManager.getProperty("headless"))) {
options.addArguments("--headless");
}
driver = new ChromeDriver(options);
break;
case "firefox":
driver = new FirefoxDriver();
break;
default:
throw new BrowserNotSupportedException(browser);
}
driver.manage().timeouts().implicitlyWait(
Duration.ofSeconds(Integer.parseInt(
ConfigManager.getProperty("implicit.wait"))));
return driver;
}
}
public class LoginPage extends BasePage {
@FindBy(id = "username")
private WebElement usernameInput;
@FindBy(id = "password")
private WebElement passwordInput;
@FindBy(css = ".login-btn")
private WebElement loginButton;
@FindBy(css = ".error-message")
private WebElement errorMessage;
public LoginPage(WebDriver driver) {
super(driver);
PageFactory.initElements(driver, this);
}
public void login(String username, String password) {
log.info("Attempting login with username: {}", username);
WebElementUtils.safeType(usernameInput, username);
WebElementUtils.safeType(passwordInput, password);
WebElementUtils.safeClick(loginButton);
}
public boolean isLoggedIn() {
return waitForUrlContains("/dashboard");
}
public String getErrorMessage() {
return waitForElementVisible(errorMessage).getText();
}
}
@Data
@Builder
public class UserData {
private String username;
private String password;
private String email;
private String firstName;
private String lastName;
// Example usage
public static UserData createDefaultUser() {
return UserData.builder()
.username("default_user")
.password("Pass123!")
.email("test@example.com")
.firstName("John")
.lastName("Doe")
.build();
}
}
// In your test or step definition
public void setUp() {
// Get thread-safe driver instance
WebDriver driver = DriverManager.getInstance().getDriver();
}
// Configuration (config.properties)
browser=chrome
headless=false
implicit.wait=10
explicit.wait=20
public class WebElementUtils {
// Smart click with retry and logging
public static void safeClick(WebElement element) {
try {
waitForElementClickable(element);
element.click();
} catch (ElementClickInterceptedException e) {
// Automatic retry with JavaScript click
jsClick(element);
} catch (StaleElementReferenceException e) {
// Automatic retry with fresh element
retryClick(element);
}
}
// Smart type with clear and verify
public static void safeType(WebElement element, String text) {
waitForElementVisible(element);
element.clear();
element.sendKeys(text);
// Verify text was entered correctly
Assert.assertEquals(element.getAttribute("value"), text);
}
}
public class ScreenshotUtils {
public static String captureScreenshotOnFailure(
WebDriver driver, String testName) {
try {
String timestamp = getTimestamp();
String filename = String.format(
"failure_%s_%s.png", testName, timestamp);
TakesScreenshot ts = (TakesScreenshot) driver;
File source = ts.getScreenshotAs(OutputType.FILE);
File destination = new File("test-output/screenshots/" + filename);
FileUtils.copyFile(source, destination);
return destination.getAbsolutePath();
} catch (Exception e) {
log.error("Failed to capture screenshot", e);
return null;
}
}
}
<!-- testng.xml -->
<suite name="Parallel Test Suite" parallel="methods" thread-count="3">
<test name="Login Tests">
<classes>
<class name="com.designpattern.tests.LoginTest"/>
<class name="com.designpattern.tests.RegistrationTest"/>
</classes>
</test>
</suite>
# login.feature
Feature: User Login
Scenario Outline: Login with different credentials
Given I am on the login page
When I enter username "<username>" and password "<password>"
Then I should see "<message>"
Examples:
| username | password | message |
| valid_user | Pass123! | Welcome back! |
| wrong_user | wrong123 | Invalid credentials |
public class LoginSteps {
private LoginPage loginPage;
private WebDriver driver;
@Before
public void setUp() {
driver = DriverManager.getInstance().getDriver();
loginPage = new LoginPage(driver);
}
@Given("I am on the login page")
public void navigateToLoginPage() {
driver.get(ConfigManager.getProperty("app.url"));
}
@When("I enter username {string} and password {string}")
public void enterCredentials(String username, String password) {
loginPage.login(username, password);
}
@Then("I should see {string}")
public void verifyMessage(String expectedMessage) {
if (expectedMessage.equals("Welcome back!")) {
Assert.assertTrue(loginPage.isLoggedIn());
} else {
Assert.assertEquals(loginPage.getErrorMessage(), expectedMessage);
}
}
}
selenium-framework/
├── src/
│ ├── main/
│ │ └── java/com/designpattern/
│ │ ├── core/ # Core framework components
│ │ ├── strategy/ # Implementation strategies
│ │ ├── config/ # Dynamic configuration
│ │ ├── monitoring/ # Performance & events
│ │ ├── healing/ # Self-healing mechanisms
│ │ ├── retry/ # Retry mechanisms
│ │ ├── pages/ # Page objects
│ │ └── utils/ # Utilities
│ └── test/
│ ├── java/ # Test implementations
│ └── resources/ # Test configurations
└── pom.xml
-
Prerequisites:
- Java 8 or higher
- Maven 3.6+
- Chrome/Firefox/Edge browser
-
Installation:
mvn clean install
- Running Tests:
# Run all tests
mvn test
# Run specific test suite
mvn test -Dsuite=regression
# Run with specific browser
mvn test -Dbrowser=chrome
- Test Reports Location:
- TestNG:
test-output/index.html
- Extent:
test-output/extent-report.html
- Screenshots:
test-output/screenshots/
- Logs:
logs/automation.log
- TestNG:
- Always use Page Object Model for better maintenance:
public class BasePage {
protected WebDriver driver;
protected WebDriverWait wait;
protected static final Logger log = LogUtils.getLogger();
public BasePage(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver,
Duration.ofSeconds(ConfigManager.getExplicitWaitTimeout()));
}
protected WebElement waitForElementVisible(WebElement element) {
return wait.until(ExpectedConditions.visibilityOf(element));
}
protected WebElement waitForElementClickable(WebElement element) {
return wait.until(ExpectedConditions.elementToBeClickable(element));
}
protected boolean waitForUrlContains(String fraction) {
return wait.until(ExpectedConditions.urlContains(fraction));
}
}
- Use Builder Pattern for complex test data creation:
TestData data = TestData.builder()
.firstName("John")
.lastName("Doe")
.email("john.doe@example.com")
.age(30)
.build();
- Implement reusable step definitions:
@Given("I am logged in as {string}")
public void loginAs(String userType) {
UserData userData = TestDataFactory.getUserData(userType);
LoginPage loginPage = new LoginPage(driver);
loginPage.login(userData.getUsername(), userData.getPassword());
}
- Use scenario outlines for data-driven tests:
Scenario Outline: Search with different criteria
Given I am on the search page
When I search for "<item>"
Then I should see "<count>" results
Examples:
| item | count |
| laptop | 10 |
| phone | 20 |
| tablet | 15 |
- Maintain clean and organized test structure:
@Test
public void testUserRegistration() {
// Given - Setup
RegistrationPage registrationPage = new RegistrationPage(driver);
UserData userData = TestData.createRandomUser();
// When - Action
registrationPage.registerNewUser(userData);
// Then - Verification
Assert.assertTrue(registrationPage.isRegistrationSuccessful());
Assert.assertEquals(registrationPage.getWelcomeMessage(),
"Welcome " + userData.getFirstName());
}
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request