Page Object – один из наиболее полезных и используемых архитектурных решений в автоматизации. Данный паттерн помогает вынести элементы отдельных страниц в отдельные классы, отвечающие за работу с HTML каждой конкретной веб-страницы.
Такой подход значительно уменьшает объем повторяющегося кода, потому что одни и те же объекты страниц можно использовать в различных тестах.
Основное преимущество заключается в том, что в случае изменения пользовательского интерфейса, можно выполнить исправление только в одном месте, а не исправлять каждый тест, в котором этот интерфейс используется.
Дальше покажу пример использования, выполнив рефакторинг наших автотестов по сайту Wikipedia.
Начнем с того, что в каждом тесте мы переходим по URL сайта Wikipedia и выполняем поиск:

Как мы видим у нас дублируется код и локаторы при поиске элементов, а это не очень хорошо, поскольку в случае, когда измениться локатор к какому-либо элементу на этой странице, придется обновлять все тесты.
Чтобы решить эту проблему используем Page obgect паттерн.
- Создадим класс SearchPage.cs с конструктором, который будет принимать экзепляр нашего драйвера для того чтобы мы работали в рамках одной сессии.
- Перенесем в него поиск нужных нам элементов
public class SearchPage
{
private IWebDriver _driver;
public SearchPage(IWebDriver driver)
{
_driver = driver;
}
private IWebElement SearchField => _driver.FindElement(By.Id("searchInput"));
private IWebElement SearchButton => _driver.FindElement(By.XPath("//i[text()='Search']"));
public void Search(string query)
{
SearchField.SendKeys(query);
SearchButton.Click();
}
}
Дальше в тестах мы можем создавать экземпляр класса данной страницы, предавать туда экземпляр драйвера и вызывать метод Search.
Для удобства, чтобы в тесте не создавать экземпляр класса SearchPage, мы можем в базовом для всех тестов классе BaseTest написать метод, который будет выполнять переход на страницу поиска
public SearchPage OpenSearchPage()
{
driver.Navigate().GoToUrl(ConfigurationManager.AppSettings["URL"]);
return new SearchPage(driver);
}
Обновив тест в соответствии с созданным объектом страницы SearchPage тест будет выглядеть следующим образом:

На этом мы не останавливаемся и понимаем, что в обоих тестах после поиска открывается страница результатов (хоть и с разным содержанием, но с общими элементами).
Поэтому создадим класс результирующей страницы SearchResultsPage.cs с конструктором принимающим экзепляр драйвера и добавим в него поиск элемента “заглавие”
public class SearchResultsPage
{
private IWebDriver _driver;
public SearchResultsPage(IWebDriver driver)
{
_driver = driver;
}
public IWebElement PageHeader => _driver.FindElement(By.Id("firstHeading"));
}
Теперь нам нужно обновить метод Search в классе SearchPage.cs таким образом, чтобы он нам возвращал экземпляр класса SearchResultsPage.cs
public SearchResultsPage Search(string query)
{
SearchField.SendKeys(query);
SearchButton.Click();
return new SearchResultsPage(_driver);
}
И обновим наши тесты таким образом, чтобы мы работали с объектами наших страниц:

Как результат мы видим, что наши тесты стали компактными и более читабельными за счет работы с объектами страниц. Ну и поддерживать такие тесты проще, поскольку наши элементы инкапсулированы в рамках классов и не дублируются в каждом тесте.
