Q17 of 37 · Selenium
What is the @FindBy annotation and how does PageFactory work?
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.@FindByschains multiple locators (AND).@FindAllmatches any of multiple locators (OR).@CacheLookupcaches 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.
@CacheLookupis 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);
}
}