Answer
POM vs Page Factory
POM is the design pattern concept (separate page classes with locators + actions). Page Factory is a Selenium built-in tool that implements POM using annotations.
POM Without Page Factory (Manual)
Java
public class LoginPage {
private WebDriver driver;
// Locators defined as By objects
private By emailInput = By.id("email");
private By passwordInput = By.id("password");
private By loginBtn = By.cssSelector("[data-testid='login-btn']");
public LoginPage(WebDriver driver) {
this.driver = driver;
}
public void login(String email, String password) {
// Element found EVERY TIME method is called
driver.findElement(emailInput).sendKeys(email);
driver.findElement(passwordInput).sendKeys(password);
driver.findElement(loginBtn).click();
}
}
Page Factory (Annotation-Based)
Java
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
public class LoginPage {
private WebDriver driver;
// @FindBy annotations replace By.id(), By.css() etc.
@FindBy(id = "email")
private WebElement emailInput;
@FindBy(id = "password")
private WebElement passwordInput;
@FindBy(css = "[data-testid='login-btn']")
private WebElement loginBtn;
@FindBy(className = "error-msg")
private WebElement errorMessage;
// Multiple locators — tries each until one works
@FindAll({
@FindBy(id = "submit"),
@FindBy(css = "button[type='submit']")
})
private WebElement submitBtn;
// List of elements
@FindBy(css = ".product-card")
private List<WebElement> productCards;
public LoginPage(WebDriver driver) {
this.driver = driver;
// This initializes all @FindBy elements (lazy — located on first use)
PageFactory.initElements(driver, this);
}
public void login(String email, String password) {
emailInput.sendKeys(email); // clean — no driver.findElement()
passwordInput.sendKeys(password);
loginBtn.click();
}
public String getErrorText() {
return errorMessage.getText();
}
}
@FindBy Locator Strategies
Java
@FindBy(id = "email") // By.id
@FindBy(name = "username") // By.name
@FindBy(className = "error-msg") // By.className
@FindBy(tagName = "h1") // By.tagName
@FindBy(linkText = "Forgot Password") // By.linkText
@FindBy(partialLinkText = "Forgot") // By.partialLinkText
@FindBy(css = ".btn.btn-primary") // By.cssSelector (most used)
@FindBy(xpath = "//button[@type='submit']") // By.xpath
Key Differences
| POM (Manual) | Page Factory | |
|---|---|---|
| Locator syntax | By.id("email") | @FindBy(id = "email") |
| Element type | Located fresh each call | Proxy — located on first access |
| Stale element | Less issue | Can get StaleElementReferenceException |
| Initialization | Not needed | PageFactory.initElements() required |
| Readability | Good | Better (less boilerplate) |
| Dynamic elements | Easier to handle | Can be tricky |
When Stale Element is a Problem (Page Factory)
Java
// Page Factory can cause StaleElementReferenceException
// if DOM changes AFTER initElements() but BEFORE the element is used
// Solution: re-initialize on stale
public WebElement getElement(WebElement element) {
try {
return element;
} catch (StaleElementReferenceException e) {
PageFactory.initElements(driver, this); // reinitialize
return element;
}
}
