SOLID Principles in Automation Framework

The SOLID principles are a set of design principles in object-oriented programming that help create robust, maintainable, and scalable systems. Applying these principles to an automation testing framework built with Selenium ensures a clean architecture and ease of maintenance.
Here’s how each SOLID principle can be implemented in a Selenium-based automation testing framework, with examples:
1. Single Responsibility Principle (SRP)
Definition: A class should have only one reason to change. It should have one responsibility.
Application in Selenium Framework:
- Separate concerns like test data management, page interactions, and test execution into different classes.
Example:
# Responsibility: Managing login page elements and actions
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.username_field = "username"
self.password_field = "password"
self.login_button = "loginBtn"
def login(self, username, password):
self.driver.find_element_by_id(self.username_field).send_keys(username)
self.driver.find_element_by_id(self.password_field).send_keys(password)
self.driver.find_element_by_id(self.login_button).click()
# Responsibility: Handling test data
class TestData:
LOGIN_CREDENTIALS = {"username": "test_user", "password": "test_pass"}
Here, LoginPage
handles only login-related actions, while TestData
stores test data.
2. Open/Closed Principle (OCP)
Definition: Classes should be open for extension but closed for modification.
Application in Selenium Framework:
- Use inheritance or composition to extend functionality without modifying existing classes.
Example:
class BasePage:
def __init__(self, driver):
self.driver = driver
def click_element(self, locator):
self.driver.find_element_by_locator(locator).click()
class LoginPage(BasePage):
def login(self, username, password):
self.driver.find_element_by_id("username").send_keys(username)
self.driver.find_element_by_id("password").send_keys(password)
self.click_element("loginBtn")
Here, BasePage
provides reusable methods, and LoginPage
extends it for specific functionality.
3. Liskov Substitution Principle (LSP)
Definition: Subtypes must be substitutable for their base types without altering the correctness of the program.
Application in Selenium Framework:
- Ensure that derived classes can replace base classes without breaking the functionality.
Example:
class Browser:
def open_url(self, url):
raise NotImplementedError
class ChromeBrowser(Browser):
def open_url(self, url):
print(f"Opening {url} in Chrome")
class FirefoxBrowser(Browser):
def open_url(self, url):
print(f"Opening {url} in Firefox")
def test_open_url(browser: Browser):
browser.open_url("https://example.com")
# Usage
chrome = ChromeBrowser()
firefox = FirefoxBrowser()
test_open_url(chrome) # Works
test_open_url(firefox) # Works
4. Interface Segregation Principle (ISP)
Definition: A class should not be forced to implement interfaces it does not use.
Application in Selenium Framework:
- Use smaller, specific interfaces or abstract methods for distinct functionalities.
Example:
class IClickable:
def click(self):
raise NotImplementedError
class ITypeable:
def type_text(self, text):
raise NotImplementedError
class Button(IClickable):
def click(self):
print("Button clicked")
class TextBox(IClickable, ITypeable):
def click(self):
print("TextBox clicked")
def type_text(self, text):
print(f"Text typed: {text}")
Here, Button
only implements IClickable
, while TextBox
implements both IClickable
and ITypeable
.
5. Dependency Inversion Principle (DIP)
Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions.
Application in Selenium Framework:
- Use interfaces or abstract classes to decouple test scripts from specific implementations.
Example:
from abc import ABC, abstractmethod
class DriverManager(ABC):
@abstractmethod
def get_driver(self):
pass
class ChromeDriverManager(DriverManager):
def get_driver(self):
from selenium import webdriver
return webdriver.Chrome()
class FirefoxDriverManager(DriverManager):
def get_driver(self):
from selenium import webdriver
return webdriver.Firefox()
# Test Script
def test_login(driver_manager: DriverManager):
driver = driver_manager.get_driver()
driver.get("https://example.com")
print("Test executed")
driver.quit()
# Usage
test_login(ChromeDriverManager()) # Works with Chrome
test_login(FirefoxDriverManager()) # Works with Firefox
Benefits of Applying SOLID Principles in Selenium Framework
Scalability: Easily add new features or extend existing ones.
Maintainability: Clean separation of concerns reduces complexity.
Reusability: Reusable components save development time.
Testability: Decoupled components are easier to mock and test.
By adhering to SOLID principles, your Selenium automation framework becomes robust, easier to maintain, and adaptable to changing requirements.
Subscribe to my newsletter
Read articles from Thiru gnanam directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
