Page Transactions and Page Objects Model
Pros and Cons of Page Object Model (POM) and Page Transactions (PT)
Below is a comparison of Page Object Model (POM) and Page Transactions (PT) based on different aspects, rated from 0 to 5 (where 0 is the worst and 5 is the best).
Page Object Model (POM)
Criteria |
Pros |
Cons |
Rating (0-5) |
|---|---|---|---|
Code Maintainability |
✅ Separates locators from tests ✅ Easy to update elements |
❌ Still tightly coupled with page structure ❌ If UI changes, all affected pages must be updated |
4 |
Reusability |
✅ Page classes can be reused across multiple tests |
❌ Interactions are page-specific, not user-flow based |
4 |
Readability & Simplicity |
✅ Test cases look clean with method calls |
❌ Requires writing multiple classes for different pages, which can add complexity |
3 |
Scalability |
✅ Works well for large projects with many pages |
❌ Harder to manage when dealing with workflows spanning multiple pages |
4 |
Test Maintenance Effort |
✅ If a locator changes, it only needs to be updated in one place |
❌ If the user workflow changes, tests might need significant updates |
3 |
Suitability for UI Testing |
✅ Well-suited for UI automation with Selenium and similar tools |
❌ Not ideal for non-UI scenarios (e.g., REST API testing) |
4 |
🔹 Overall Score: 22/30
Page Transactions (PT)
Criteria |
Pros |
Cons |
Rating (0-5) |
|---|---|---|---|
Code Maintainability |
✅ Transactions focus on user actions rather than elements, reducing maintenance |
❌ Requires a different mindset for those used to POM |
5 |
Reusability |
✅ Highly reusable since transactions are user-action based |
❌ If UI structure changes drastically, transactions might need updates |
5 |
Readability & Simplicity |
✅ Tests read like human instructions, improving clarity ✅ Reduces WebDriver calls in test scripts |
❌ Requires defining multiple transaction classes for different actions |
5 |
Scalability |
✅ Works well for large applications, especially where user flows span multiple pages |
❌ Slightly more setup is needed compared to POM |
5 |
Test Maintenance Effort |
✅ Less maintenance required since tests are written based on actions rather than UI elements |
❌ If a transaction is reused across many tests, fixing one issue could impact multiple tests |
4 |
Suitability for UI Testing |
✅ Works for UI, API, and asynchronous testing |
❌ Less common in traditional UI automation frameworks |
5 |
🔹 Overall Score: 29/30
Which Pattern is Better?
Based on the overall scores, Page Transactions (PT) scores higher (29/30) than Page Object Model (POM) (22/30).
Why?
✔ PT focuses on user workflows, making it more maintainable and scalable.
✔ It allows for better test organization and is more suitable for different types of automation (UI, API, async).
✔ Tests written in PT are more readable, making them accessible to non-technical stakeholders.
However, POM is still a great choice for teams already familiar with traditional UI testing methods, and it provides a structured way to manage page elements.
If your project involves complex user workflows across multiple pages, Page Transactions is the better approach. If you need simple UI structure representation, POM works well.
Tips for Migrating from Page Object Model (POM) to Page Transactions (PT)
Moving from POM to PT requires shifting from page-centric automation to user action-centric automation. Below are key steps and examples to help with the transition.
Identify User Transactions
In POM, page objects represent pages. In PT, we focus on transactions (user actions) like:
Login
Logout
Submit Form
Change Language
Navigate to a Page
🔹 Tip: Instead of writing separate methods for every element on a page, create classes for each transaction.
Convert POM Methods to PT Transactions
POM Example (Before Migration)
from selenium.webdriver.common.by import By
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.username = (By.ID, "username")
self.password = (By.ID, "password")
self.login_button = (By.ID, "login")
def enter_username(self, user):
self.driver.find_element(*self.username).send_keys(user)
def enter_password(self, pwd):
self.driver.find_element(*self.password).send_keys(pwd)
def click_login(self):
self.driver.find_element(*self.login_button).click()
def test_login():
driver = webdriver.Chrome()
login_page = LoginPage(driver)
login_page.enter_username("user1")
login_page.enter_password("password1")
login_page.click_login()
assert "Dashboard" in driver.title
PT Example (After Migration)
from guara.transaction import AbstractTransaction
class LoginTransaction(AbstractTransaction):
def __init__(self, driver):
super().__init__(driver)
def do(self, username, password):
self._driver.find_element(By.ID, "username").send_keys(username)
self._driver.find_element(By.ID, "password").send_keys(password)
self._driver.find_element(By.ID, "login").click()
return self._driver.title
from guara.application import Application
from guara import it
from selenium import webdriver
def test_login():
driver = webdriver.Chrome()
app = Application(driver)
app.at(LoginTransaction, username="user1", password="password1").asserts(it.Contains, "Dashboard")
Benefits of the PT Approach
✔ Less boilerplate code – No need for page-specific methods in tests
✔ Readable test cases – Tests describe actions in plain English
✔ Easier maintenance – UI changes don’t break all related test methods
Additional Refactoring for Other Pages
Example: Changing Language Transaction
Instead of:
class HomePage:
def click_language_button(self):
self.driver.find_element(By.ID, "language").click()
Use:
class ChangeLanguageTransaction(AbstractTransaction):
def do(self, language):
self._driver.find_element(By.ID, f"lang-{language}").click()
return self._driver.find_element(By.TAG_NAME, "body").text
Test case:
app.at(ChangeLanguageTransaction, language="pt").asserts(it.Contains, "Página inicial")
Gradual Migration Strategy
Start with frequently changing tests
Convert one transaction at a time
Keep old POM code until full migration is complete