Автоматизация в Selenium: объектная модель страницы и фабрика страниц

Опубликовано: 2022-03-11

Написание автоматизированных тестов — это больше, чем просто роскошь для любой гибкой команды разработчиков программного обеспечения. Это необходимость и важный инструмент для быстрого поиска ошибок на ранних этапах цикла разработки программного обеспечения. Когда появляется новая функция, которая все еще находится в стадии разработки, разработчики могут запустить автоматизированные тесты и посмотреть, как эти изменения повлияют на другие части системы. В этой статье объясняется, как можно ускорить автоматическое тестирование с помощью модели Page Object в Selenium.

Благодаря автоматизации тестирования можно снизить стоимость исправления ошибок и в целом улучшить процесс обеспечения качества программного обеспечения (QA). При правильном тестировании разработчики получают возможность находить и устранять ошибки еще до того, как они попадут в отдел контроля качества. Автоматизация тестирования также помогает нам автоматизировать тестовые случаи и функции, которые постоянно регрессируют. Таким образом, у QA будет больше времени на тестирование других частей приложения. Кроме того, это помогает в обеспечении качества продукта в производственных выпусках. В результате мы получаем более стабильные продукты и более эффективный процесс контроля качества.

Автоматизация Selenium упрощает автоматизацию тестирования веб-приложений

Selenium упрощает автоматизацию тестирования веб-приложений
Твитнуть

Хотя написание автоматизированных тестов может показаться легкой задачей для разработчиков и инженеров, все же существует вероятность того, что в любом гибком процессе возникнут плохо реализованные тесты и высокая стоимость обслуживания кода. Попытка постоянно вносить изменения или функции в любой проект гибкой разработки может оказаться дорогостоящей, если задействованы тесты. Изменение одного элемента на веб-странице, на который опираются 20 тестов, потребует прохождения этих 20 процедур тестирования и обновления каждой из них, чтобы адаптироваться к этому недавно введенному изменению. Это может не только отнять много времени, но и стать серьезным демотивирующим фактором, когда дело доходит до внедрения автоматизированных тестов на раннем этапе.

Но что, если бы мы могли внести изменение только в одном месте и использовать его во всех соответствующих процедурах тестирования? В этой статье мы рассмотрим автоматизированные тесты в Selenium и то, как мы можем использовать модели Page Object для написания поддерживаемых и повторно используемых тестовых процедур.

Объектная модель страницы в Selenium

Объектная модель страницы — это шаблон проектирования объектов в Selenium, где веб-страницы представлены в виде классов, а различные элементы на странице определяются как переменные в классе. Затем все возможные взаимодействия с пользователем могут быть реализованы в виде методов класса:

 clickLoginButton(); setCredentials(user_name,user_password);

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

Для поддержки модели Page Object мы используем Page Factory. Фабрика страниц в Selenium является расширением Page Object и может использоваться по-разному. В этом случае мы будем использовать Фабрику страниц для инициализации веб-элементов, которые определены в классах веб-страниц или объектах страниц.

Классы веб-страниц или объекты страниц, содержащие веб-элементы, необходимо инициализировать с помощью Page Factory, прежде чем можно будет использовать переменные веб-элементов. Это можно сделать просто с помощью функции initElements на PageFactory:

 LoginPage page = new LoginPage(driver); PageFactory.initElements(driver, page);

Или еще проще:

 LoginPage page = PageFactory.intElements(driver,LoginPage.class)

Или внутри конструктора класса веб-страницы:

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

Фабрика страниц инициализирует каждую переменную WebElement ссылкой на соответствующий элемент на фактической веб-странице на основе настроенных «локаторов». Это делается с помощью аннотаций @FindBy . С помощью этой аннотации мы можем определить стратегию поиска элемента вместе с необходимой информацией для его идентификации:

 @FindBy(how=How.NAME, using="username") private WebElement user_name;

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

 @FindBy(how=How.NAME, using="username") @CacheLookup private WebElement user_name;

Все это определение переменной WebElement можно заменить гораздо более лаконичной формой:

 @FindBy(name="username") private WebElement user_name;

Аннотация @FindBy поддерживает несколько других стратегий, которые немного упрощают задачу:

 id, name, className, css, tagName, linkText, partialLinkText, xpath
 @FindBy() private WebElement user_name; @FindBy(name="passsword") private WebElement user_password; @FindBy(className="h3") private WebElement label; @FindBy(css=”#content”) private WebElement text;

После инициализации эти переменные WebElement можно использовать для взаимодействия с соответствующими элементами на странице. Следующий код будет, например:

 user_password.sendKeys(password);

… отправить заданную последовательность нажатий клавиш в поле пароля на странице, что эквивалентно:

 driver.findElement(By.name(“user_password”)).sendKeys(password);

Двигаясь дальше, вы часто будете сталкиваться с ситуациями, когда вам нужно найти список элементов на странице, и тогда @FindBys пригодится:

 @FindBys(@FindBy(css=”div[class='yt-lockup-tile yt-lockup-video']”))) private List<WebElement> videoElements;

Приведенный выше код найдет все элементы div , имеющие два имени класса «yt-lockup-tile» и «yt-lockup-video». Мы можем упростить это еще больше, заменив его следующим:

 @FindBy(how=How.CSS,using="div[class='yt-lockup-tile yt-lockup-video']") private List<WebElement> videoElements;

Кроме того, вы можете использовать @FindAll с несколькими аннотациями @FindBy для поиска элементов, соответствующих любому из заданных локаторов:

 @FindAll({@FindBy(how=How.ID, using=”username”), @FindBy(className=”username-field”)}) private WebElement user_name;

Теперь, когда мы можем представлять веб-страницы в виде классов Java и использовать Фабрику страниц для простой инициализации переменных WebElement , пришло время посмотреть, как мы можем писать простые тесты Selenium, используя шаблон Page Object и Фабрику страниц.

Простой проект автоматизации тестирования Selenium на Java

В нашем учебном пособии по модели Page Object давайте автоматизируем регистрацию разработчика в Toptal. Для этого нам нужно автоматизировать следующие шаги:

  • Посетите www.toptal.com

  • Нажмите на кнопку «Подать заявку в качестве разработчика».

  • На странице портала сначала проверьте, открыта ли она

  • Нажмите на кнопку «Присоединиться к Toptal».

  • Заполните форму

  • Отправьте форму, нажав кнопку «Присоединиться к Toptal».

Настройка проекта

  • Загрузите и установите Java JDK.

  • Скачайте и установите InteliJ Idea

  • Создайте новый проект Maven

  • Свяжите «Project SDK» с вашим JDK, например: в Windows «C:\Program Files\Java\jdkxxx»

  • Настройте идентификатор группы и идентификатор артефакта:

 <groupId>SeleniumTEST</groupId> <artifactId>Test</artifactId>
  • Добавьте зависимости Selenium и JUnit Maven в файл POM вашего проекта.
 <dependencies> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- Selenium --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-firefox-driver</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-support</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>${selenium.version}</version> </dependency> </dependencies>

Замените версию Selenium и версию JUnit последними номерами версий, которые можно найти, выполнив поиск JUnit Maven в Google и на сайте Selenium.

На этом этапе, если включена автоматическая сборка, зависимости должны начать загружаться автоматически. Если нет, просто активируйте «Плагины» > «Установить» > «Установить: установить» на панели «Проекты Maven» в правой части IDE IntelliJ Idea.

Скриншот IDE учебника по тестированию селена

После загрузки проекта мы можем приступить к созданию нашего тестового пакета в «src/test/java». Назовите пакет «com.toptal» и создайте под ним еще два пакета: «com.toptal.webpages» и «com.toptal.tests».

Скриншот учебника по тестированию селена

Мы сохраним классы Page Object/Page Factory в разделе «com.toptal.webpages», а процедуры тестирования — в разделе «com.toptal.tests».

Теперь мы можем начать создавать наши классы Page Object.

Домашняя страница Объект страницы

Самое первое, что нам нужно реализовать, — это домашняя страница Toptal (www.toptal.com). Создайте класс в разделе «com.toptal.webpages» и назовите его «HomePage».

 package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.How; import org.openqa.selenium.support.PageFactory; public class HomePage { private WebDriver driver; //Page URL private static String PAGE_URL="https://www.toptal.com"; //Locators //Apply as Developer Button @FindBy(how = How.LINK_TEXT, using = "APPLY AS A DEVELOPER") private WebElement developerApplyButton; //Constructor public HomePage(WebDriver driver){ this.driver=driver; driver.get(PAGE_URL); //Initialise Elements PageFactory.initElements(driver, this); } public void clickOnDeveloperApplyButton(){ developerApplyButton.click(); } }

Определение локаторов элементов

На домашней странице Toptal нас особенно интересует один элемент, а именно кнопка «Подать заявку в качестве разработчика». Мы можем найти этот элемент, сопоставив текст, что мы и делаем выше. При моделировании веб-страниц как классов Page Object поиск и идентификация элементов часто может стать рутиной. С помощью инструментов отладки Google Chrome или Firefox это можно упростить. Щелкнув правой кнопкой мыши по любому элементу на странице, вы можете активировать опцию «Проверить элемент» из контекстного меню, чтобы узнать подробную информацию об элементе.

Один распространенный (и мой предпочтительный) способ — найти элементы с помощью расширения Firefox FireBug в сочетании с веб-драйвером Firefox в Selenium. После установки и включения расширения FireBug вы можете щелкнуть правой кнопкой мыши на странице и выбрать «Проверить элемент с помощью FireBug», чтобы открыть FireBug. На вкладке HTML FireBug вы можете скопировать XPath, путь CSS, имя тега или «Id» (если есть) любого элемента на странице.

объектная модель страницы в селене: определение локаторов элементов

Скопировав XPath элемента на скриншоте выше, мы можем создать для него поле WebElement в нашем объекте страницы следующим образом:

 @FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") WebElement heading;

Или, для простоты, мы можем использовать здесь имя тега «h1», если оно однозначно идентифицирует интересующий нас элемент:

 @FindBy(tagName = "h1") WebElement heading;

Объект страницы DeveloperPortalPage

Далее нам нужен объект страницы, представляющий страницу портала разработчика, доступ к которому можно получить, нажав кнопку «Применить как разработчик».

На этой странице нас интересуют два элемента. Чтобы определить, загрузилась ли страница, мы хотим проверить существование заголовка. И нам также нужно поле WebElement для кнопки «Присоединиться к Toptal».

 package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperPortalPage { private WebDriver driver; @FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") private WebElement heading; @FindBy(linkText = "JOIN TOPTAL") private WebElement joinToptalButton; //Constructor public DeveloperPortalPage (WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } //We will use this boolean for assertion. To check if page is opened public boolean isPageOpened(){ return heading.getText().toString().contains("Developer portal"); } public void clikOnJoin(){ joinToptalButton.click(); } }

Объект страницы DeveloperApplyPage

И, наконец, для нашего третьего и последнего объекта страницы для этого проекта мы определяем тот, который представляет страницу, содержащую форму приложения разработчика. Поскольку здесь нам приходится иметь дело с несколькими полями формы, мы определяем одну переменную WebElement для каждого поля формы. Мы находим каждое поле по его «идентификатору» и определяем специальные методы установки для каждого поля, которые имитируют нажатия клавиш для соответствующих полей.

 package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperApplyPage { private WebDriver driver; @FindBy(tagName = "h1") WebElement heading; @FindBy() WebElement developer_email; @FindBy() WebElement developer_password; @FindBy() WebElement developer_password_confirmation; @FindBy() WebElement developer_full_name; @FindBy() WebElement developer_skype; @FindBy() WebElement join_toptal_button; //Constructor public DeveloperApplyPage(WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } public void setDeveloper_email(String email){ developer_email.clear(); developer_email.sendKeys(email); } public void setDeveloper_password(String password){ developer_password.clear(); developer_password.sendKeys(password); } public void setDeveloper_password_confirmation(String password_confirmation){ developer_password_confirmation.clear(); developer_password_confirmation.sendKeys(password_confirmation); } public void setDeveloper_full_name (String fullname){ developer_full_name.clear(); developer_full_name.sendKeys(fullname); } public void setDeveloper_skype (String skype){ developer_skype.clear(); developer_skype.sendKeys(skype); } public void clickOnJoin(){ join_toptal_button.click(); } public boolean isPageOpened(){ //Assertion return heading.getText().toString().contains("Apply to join our network as a developer"); } }

Написание простого теста Selenium

С классами Page Object, представляющими наши страницы, и взаимодействием с пользователем в качестве их методов, теперь мы можем написать нашу простую процедуру тестирования в виде серии простых вызовов методов и утверждений.

 package com.toptal.tests; import com.toptal.webpages.DeveloperApplyPage; import com.toptal.webpages.DeveloperPortalPage; import com.toptal.webpages.HomePage; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import java.net.URL; import java.util.concurrent.TimeUnit; public class ApplyAsDeveloperTest { WebDriver driver; @Before public void setup(){ //use FF Driver driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); } @Test public void applyAsDeveloper() { //Create object of HomePage Class HomePage home = new HomePage(driver); home.clickOnDeveloperApplyButton(); //Create object of DeveloperPortalPage DeveloperPortalPage devportal= new DeveloperPortalPage(driver); //Check if page is opened Assert.assertTrue(devportal.isPageOpened()); //Click on Join Toptal devportal.clikOnJoin(); //Create object of DeveloperApplyPage DeveloperApplyPage applyPage =new DeveloperApplyPage(driver); //Check if page is opened Assert.assertTrue(applyPage.isPageOpened()); //Fill up data applyPage.setDeveloper_email("[email protected]"); applyPage.setDeveloper_full_name("Dejan Zivanovic Automated Test"); applyPage.setDeveloper_password("password123"); applyPage.setDeveloper_password_confirmation("password123"); applyPage.setDeveloper_skype("automated_test_skype"); //Click on join //applyPage.clickOnJoin(); } @After public void close(){ driver.close(); } }

Запуск теста

На этом этапе структура вашего проекта должна выглядеть следующим образом:

пример тестирования селена

Если вы хотите запустить тест, выберите «ApplyAsDeveloperTest» в дереве, щелкните его правой кнопкой мыши и выберите «Выполнить» «ApplyAsDeveloperTest» .

пример тестирования селена

После запуска теста вы можете увидеть результаты в левом нижнем углу вашей IDE:

пример тестирования селена

Заключение

Page Object и Page Factory упрощают моделирование веб-страниц в Selenium и их автоматическое тестирование, а также значительно упрощают жизнь как разработчиков, так и QA. Если все сделано правильно, эти классы Page Object можно повторно использовать во всем вашем наборе тестов и дать вам возможность реализовать автоматизированные тесты Selenium для ваших проектов на раннем этапе, не ставя под угрозу гибкую разработку. Абстрагируя взаимодействие пользователя с объектными моделями страниц и сохраняя легкость и простоту процедур тестирования, вы можете без особых усилий адаптировать набор тестов к изменяющимся требованиям.

Надеюсь, мне удалось показать вам, как писать красивый и чистый тестовый код, который легко поддерживать. Я закончу статью моей любимой цитатой QA:

Подумай дважды, напиши один раз!

Связанный: Парсинг веб-страниц с помощью безголового браузера: Учебное пособие по кукловодам