PageFactory vs By Fields

PageFactory and @FindBy

Selenium also ships PageFactory, which fills WebElement fields annotated with @FindBy. You will see it in many older company frameworks.

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class LoginPage {
    @FindBy(id = "email")      private WebElement email;
    @FindBy(id = "password")   private WebElement password;
    @FindBy(id = "login-btn")  private WebElement loginButton;
    @FindBy(css = ".product-card") private List<WebElement> productCards;

    public LoginPage(WebDriver driver) {
        PageFactory.initElements(driver, this);   // creates lazy proxies for every @FindBy field
    }

    public void loginAs(String user, String pwd) {
        email.sendKeys(user);
        password.sendKeys(pwd);
        loginButton.click();
    }
}

Which One to Use

By fieldsPageFactory @FindBy
Element lookupwhen the method runslazy proxy, looked up on each use
Works with explicit waitsdirectly: wait.until(visibilityOfElementLocated(email))needs visibilityOf(element); harder with dynamic pages
Dynamic locatorseasy: By.xpath("//a[text()='" + name + "']")not possible (annotation values are constants)
StaleElementReferenceExceptionrare (fresh lookup each time)possible with @CacheLookup
Selenium team viewrecommendedstill supported, not recommended for new code

Note: Interviewers often ask "PageFactory or POM?". Answer: POM is the pattern; PageFactory is just one way to initialise elements inside page objects. New frameworks usually use By fields with explicit waits.

Practice

Exercise: Convert this PageFactory field into a By field and a method that waits for it: @FindBy(id = "cart-count") WebElement cartCount;

Show answer
private final By cartCount = By.id("cart-count");

public String cartCount() {
    return new WebDriverWait(driver, Duration.ofSeconds(10))
            .until(ExpectedConditions.visibilityOfElementLocated(cartCount))
            .getText();
}