Principle:SeleniumHQ Selenium Page Object Annotation
| Knowledge Sources | |
|---|---|
| Domains | Test_Design, Design_Patterns, Browser_Automation |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Declarative annotation mechanism for binding web page element locators to Java fields in Page Object classes, enabling maintainable and readable test automation.
Description
The Page Object Annotation system allows developers to declare element locators as Java annotations on class fields rather than writing imperative lookup code. @FindBy binds a single locator strategy (ID, CSS, XPath, etc.) to a WebElement or List<WebElement> field. @FindAll combines multiple @FindBy annotations with OR semantics (matches any), delegating to ByAll internally. @FindBys chains multiple @FindBy annotations with AND semantics (all must match), delegating to ByChained internally. The How enum provides the complete set of locator strategies. These annotations are processed at runtime by PageFactory to create lazy-loading element proxies.
Usage
Use @FindBy for single-strategy element location (the most common case). Use @FindAll when an element might match multiple different locators. Use @FindBys when you need to chain locators (find within find). Choose the locator strategy based on stability: prefer IDs and data-test attributes over XPath and CSS class names. The @FindBy annotation supports two forms: shorthand (@FindBy(id = "foo")) and long form (@FindBy(how = How.ID, using = "foo")).
Theoretical Basis
# Annotation Processing Model
1. Developer annotates WebElement fields with @FindBy
2. PageFactory scans class fields at runtime via reflection
3. For each annotated field:
a. AbstractFindByBuilder converts annotation to By locator
b. DefaultFieldDecorator wraps the By in a lazy-loading proxy
c. Proxy replaces the field value
4. First access to the proxied field triggers findElement(by)
5. Subsequent accesses may re-query or return cached result
The How enum maps to By factory methods via the buildBy(String value) abstract method:
How.CLASS_NAME -> By.className(value)
How.CSS -> By.cssSelector(value)
How.ID -> By.id(value)
How.ID_OR_NAME -> new ByIdOrName(value)
How.LINK_TEXT -> By.linkText(value)
How.NAME -> By.name(value)
How.PARTIAL_LINK_TEXT -> By.partialLinkText(value)
How.TAG_NAME -> By.tagName(value)
How.XPATH -> By.xpath(value)
How.UNSET -> By.id(value) (defaults to ID)
The shorthand form is resolved by AbstractFindByBuilder.buildByFromShortFindBy() which checks each annotation attribute (className, css, id, linkText, name, partialLinkText, tagName, xpath) for a non-empty value. The long form is resolved by buildByFromLongFindBy() which calls findBy.how().buildBy(findBy.using()). Validation via assertValidFindBy() ensures at most one location strategy is specified per @FindBy annotation.