Principle:SeleniumHQ Selenium PageFactory Initialization
| Knowledge Sources | |
|---|---|
| Domains | Test_Design, Design_Patterns, Reflection |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Runtime mechanism for scanning Page Object class fields, converting locator annotations to By objects, and replacing fields with lazy-loading WebElement proxies.
Description
PageFactory initialization is the bridge between declarative annotations and executable element lookups. When PageFactory.initElements() is called, it uses Java reflection to scan all declared fields of the Page Object class and its entire superclass hierarchy up to Object.class. For each field, DefaultFieldDecorator checks whether the field type is assignable to WebElement or is a List<WebElement> with a @FindBy, @FindAll, or @FindBys annotation. If so, it creates an ElementLocator via DefaultElementLocatorFactory, then uses LocatingElementHandler or LocatingElementListHandler to create a JDK dynamic proxy (via java.lang.reflect.Proxy) that intercepts method calls and performs the actual DOM lookup lazily.
Usage
Call PageFactory.initElements(driver, pageObject) immediately after creating a Page Object instance, typically in the constructor. The static factory method PageFactory.initElements(driver, PageClass.class) can create and initialize in one step -- it first attempts to find a constructor taking WebDriver, then falls back to a no-arg constructor. A third overload accepts an ElementLocatorFactory for custom locator strategies, and a fourth accepts a FieldDecorator for fully custom field decoration.
Theoretical Basis
# Pseudocode: PageFactory.initElements(FieldDecorator, Object)
1. proxyIn = page.getClass()
2. While proxyIn != Object.class:
a. fields = proxyIn.getDeclaredFields()
b. For each field in fields:
i. value = decorator.decorate(classLoader, field)
ii. If value != null:
- field.setAccessible(true)
- field.set(page, value)
c. proxyIn = proxyIn.getSuperclass()
The DefaultFieldDecorator.decorate() method performs the following:
# DefaultFieldDecorator.decorate(classLoader, field)
1. If field type is not WebElement and not decoratable List -> return null
2. locator = factory.createLocator(field) // DefaultElementLocator
3. If locator is null -> return null
4. If field type is WebElement:
a. handler = new LocatingElementHandler(locator)
b. return Proxy.newProxyInstance(loader, [WebElement, WrapsElement, Locatable], handler)
5. If field type is List:
a. handler = new LocatingElementListHandler(locator)
b. return Proxy.newProxyInstance(loader, [List], handler)
The instantiatePage() method for the class-based overload:
# PageFactory.instantiatePage(searchContext, pageClass)
1. Try: constructor = pageClass.getConstructor(WebDriver.class)
return constructor.newInstance(searchContext)
2. Catch NoSuchMethodException:
return pageClass.getDeclaredConstructor().newInstance()