Page Factory

Проблема

Классический Page Object Model требует явно вызывать driver.findElement(By.id("...")) каждый раз при обращении к элементу. Это многословно: для страницы с 10 элементами нужно 10 полей с явной инициализацией в конструкторе. Кроме того, если элемент инициализируется в конструкторе, а страница ещё не загрузилась — тест сразу упадёт с NoSuchElementException, даже если элемент появится через секунду.

Page Factory — это усовершенствование POM, встроенное в Selenium. Аннотация @FindBy декларативно описывает локатор прямо над полем. Метод PageFactory.initElements() откладывает поиск элемента до момента первого обращения к нему (ленивая инициализация через прокси). Код страницы становится компактнее, а риск StaleElementReferenceException снижается, так как элемент заново ищется при каждом обращении.

Структура модуля

page-factory/
├── build.gradle
└── src/
    └── test/
        └── java/
            └── dev/shipovalov/pagefactory/
                ├── GoogleSearchPage.java      # Page Object с @FindBy-аннотациями
                ├── GoogleResultsPage.java     # Page Object результатов с @FindBy
                └── PageFactoryTest.java       # Тест, демонстрирующий Page Factory

Ключевые классы

Класс Назначение
GoogleSearchPage Поля помечены @FindBy, в конструкторе вызывается PageFactory.initElements(driver, this)
GoogleResultsPage Аналогично — элементы результатов через @FindBy(css = "...")
PageFactoryTest Тест, сравнивающий подход с явным findElement и с @FindBy

Сравнение подходов

// Классический POM — явный поиск при каждом использовании
public class SearchPagePOM {
    private WebDriver driver;
    public void search(String query) {
        driver.findElement(By.name("q")).sendKeys(query);
        driver.findElement(By.name("btnK")).click();
    }
}

// Page Factory — декларативные аннотации, ленивая инициализация
public class SearchPageFactory {
    @FindBy(name = "q")
    private WebElement searchInput;

    @FindBy(name = "btnK")
    private WebElement searchButton;

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

    public void search(String query) {
        searchInput.sendKeys(query);
        searchButton.click();
    }
}

Запуск тестов

./gradlew :page-factory:test

Отчёт о результатах: page-factory/build/reports/tests/test/index.html