Q17 of 37 · Selenium

What is the @FindBy annotation and how does PageFactory work?

SeleniumMidseleniumpage-factoryfind-byannotations

Short answer

Short answer: @FindBy declares a locator on a WebElement field, and PageFactory.initElements wires up lazy proxies — the actual lookup happens when you use the field. It's syntactic sugar for declaring locators alongside elements; some teams skip it for explicit findElement calls.

Detail

@FindBy is a Java annotation (Selenium has language-specific equivalents) that declares a locator at field-declaration time:

public class LoginPage {
    @FindBy(css = "[data-test=email]") private WebElement email;
    @FindBy(css = "[data-test=password]") private WebElement password;
    @FindBy(css = "[data-test=submit]") private WebElement submit;
    @FindBy(css = ".error") private List<WebElement> errors;

    public LoginPage(WebDriver driver) {
        PageFactory.initElements(driver, this);
    }
}

How PageFactory works: initElements walks the class's fields, sees the @FindBy annotation, and replaces each field with a lazy WebElement proxy. The DOM lookup doesn't happen at construction time — it happens at the moment you call email.click() or email.sendKeys(...). The proxy re-locates on every call (mostly), which keeps things fresh as the page changes.

Annotation flavours:

  • @FindBy(id = "..."), (css = "..."), (xpath = "..."), etc.
  • @FindBys chains multiple locators (AND).
  • @FindAll matches any of multiple locators (OR).
  • @CacheLookup caches the reference. Don't use this on dynamic pages — it's the #1 cause of StaleElementReferenceException.

The honest assessment: PageFactory is a 2010s pattern. Many modern Java teams skip it in favour of explicit findElement calls inside method bodies, because:

  • The lazy-proxy magic confuses newcomers.
  • @CacheLookup is a foot-gun for SPA pages.
  • IDE refactor support is weaker against annotation strings than against code.

If your team uses PageFactory, follow the rules: never @CacheLookup on dynamic content, prefer CSS over xpath, and test the pages on a re-render.

// EXAMPLE

LoginPage.java

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(css = "[data-test=email]")    private WebElement email;
    @FindBy(css = "[data-test=password]") private WebElement password;
    @FindBy(css = "[data-test=submit]")   private WebElement submit;

    public LoginPage(WebDriver driver) {
        PageFactory.initElements(driver, this);
    }

    public DashboardPage loginAs(String e, String p) {
        email.sendKeys(e);
        password.sendKeys(p);
        submit.click();
        return new DashboardPage(driver);
    }
}

// WHAT INTERVIEWERS LOOK FOR

Knowing PageFactory creates lazy proxies, the @CacheLookup pitfall, and a balanced view that many modern teams use plain findElement instead.

// COMMON PITFALL

Using @CacheLookup by default and getting StaleElementReferenceException on every re-render. Or treating PageFactory as mandatory for the Page Object pattern — it isn't.