Пишем UI авто тесты на TypeScript с использованием Page Object, Page Factory
Пишем UI автотесты на TypeScript используя Page Object + Page Factory, Playwright, Allure.
Вступление
В данной статье мы разберем, как писать UI автотесты с использованием паттернов Page Object, Page Factory на языке TypeScript. У меня уже была статья о том Как правильно писать UI авто тесты на Python, тут мы разберем аналогичный пример.
Requirements
Для примера написания UI авто тестов мы будем использовать:
- playwright - yarn add playwright/npm install playwright
- allure - yarn add allure-playwright/npm install allure-playwright
Вы можете использовать любой другой фреймворк и репортер, суть концепции работы с Page Object, Page Factory будет похожей на каждом фреймворке.
Обратите внимание, что все автотесты будут писаться через async, await, т.к. это единственный возможный способ для playwright. В других фреймворках может быть иначе.
Авто тесты будем писать на эту страницу https://playwright.dev/
Тест кейс:
- Открываем страницу https://playwright.dev
- Нажимаем на поиск
- Проверяем, что модальное окно поиска успешно открылось
- Вводим в поиск язык, в нашем случае будет python
- Выбираем из результатов первый
- Проверяем, что страница с Python открылась
Отмечу, что локаторы в примерах ниже не являются эталонными, а сайт для тестирования - это документация playwright, на фронтенд которой я никак не могу повлиять. В ваших проектах советую использовать кастомные data-qa-id, которые вы можете поставить в фронтенд приложении React/Vue/Angular или же попросить разработчиков сделать это.
Файл конфигурации playwright будет выглядеть стандартным образом, добавим лишь allure-report, headles, video, screenshot. Более подробно про конфигурацию playwright можно почитать тут.Playwright позволяет из коробки записывать видео и делать скриншоты + крепить их к отчету, для этого достаточно прописать video: "on", screenshot: "on", там есть и другие параметры. Если вам необходимо сохранять видео только на фейленный тест, то используйте video: "retain-on-failure, аналогично со скриншотом screenshot: "only-on-failure". Конечно же вы можете сделать тоже самое и с Selenium. В конце статьи посмотрим на видео и скриншот в отчете.
Base Page
По сути Base Page - это основная страница, которая не описывает какую-то конкретную страницу или компонент. Сама по себе Base Page не должна использоваться в тестах и от нее мы наследуем наши страницы или компоненты.
Внутри BasePage описываем базовые методы. Это лишь образец того, как можно делать BasePage
Page Factory
Теперь самое интересное. Мы определим несколько базовых компонентов для реализации работы паттерна. Но перед реализацией компонентов нам нужно добавить необходимые типы и вспомогательные методы.
По сути функция locatorTemplateFormat будет брать строку локатора, например, [data-qa-id="here-my-locator-with-index-{index}"] и подставлять параметры и context внутрь строки локатора. Например:
Реализация locatorTemplateFormat может быть любая, я лишь привел пример, как это можно сделать. В своем фреймворке вы можете использовать другой форматер для шаблона локатора.
Базовый Component. Сам по себе Component не должен использоваться в тестах и от него мы будем наследовать другие компоненты и при необходимости переопределять методы, поэтому сделаем его абстрактным классом.
Выше приведена очень упрощенная реализация Component. В своем проекте вы можете добавить больше методов, больше настроек к ним, можете изменить названия шагов test.step, которые больше подходят вам.
Давайте сделаем еще несколько компонентов.
Button - кнопка. В данном компоненте будут базовые методы для работы с кнопками.
Input - поле ввода. В данном компоненте будут базовые методы для работы с инпутами.
Link - ссылка. В данном компоненте будут базовые методы для работы со ссылками.
ListItem - любой элемент списка.
Title - заголовок. Можно использовать просто Text, но я предпочитаю разделать все для понятности. Title, Text, Label, Subtitle...
Лайфхак. Если не знаете как называть компоненты, то можете использовать библиотеки для дизайна. К примеру дизайнеры вашего продукта используют Material Design для компонентов, тогда вы можете взять посмотреть https://m3.material.io/components, как называются те или иные компоненты в Material Desgin и по аналогии называть свои компоненты Page Factory. Но если вам и так понятно, как и почему называть компоненты, то этот лайфхак не для вас :)
Теперь вопрос: "Зачем все это?". Данный подход решает сразу тонну проблем и вопросов, которые возникают у любого QA Automation, который хоть раз писал UI авто тесты.
- Дает удобный и понятный интерфейс для работы с объектами на странице. То есть мы работаем не с каким-то там локатором, а с конкретным объектом, например, Button.
- Универсализирует все взаимодействия и проверки компонентов. Очень хорошо для команд, где над авто тестами работают два и более QA Automation, ну или если авто тесты пишут разработчики тоже. С таким подходом у вас не будет споров и проблем, то есть один QA Automation может писать так expect(locator).toBeVisible(), что является единственно правильным с точки зрения playwright. Второй QA Automation может писать так expect(await locator.isVisible()).toBeTruthy(), что тоже по сути правильно, но костыльно. На этой основе могут возникать бесполезные споры или еще хуже: каждый пишет так, как он хочет. По итогу получаем проект, в котором одни и те же проверки пишутся по разному. С данным подходом мы один раз устанавливаем, как делается проверка и забываем об этом, все работает прекрасно.
- Дает возможность универсализировать все шаги для отчета. В примере выше я использовал test.step из playwright, но на самом деле это не важно, вы можете использовать любой репортер. При объявлении нового компонента нам не нужно переписывать все шаги, они динамически формируются на основе параметров, name, typeOf. Конечно же, вы можете их изменить и расширить под ваши требования. Достаточно переопределить typeOf и мы получаем новый компонент с полностью уникальными шагами.
- Динамические локаторы - это вечная боль, но не с данным подходом. Механизм форматирования локатора до безобразия прост. Мы передаем LocatorProps в каждый метод, далее все это идет в сам локатор через функцию форматирования locatorTemplateFormat. То есть это позволяет писать нам локаторы span#some-element-id-{userId}, далее передавать userIdчерез LocatorProps прямо в локатор, например так this.myUserInput.fill('Hello World', { userId }) Механизм прост, но он избавляет нас от локаторов в методах, от дублирования или еще хуже хардкода локаторов.
- Появляется возможность создавать компоненты, работа с которыми специфична. Например, у вас в продукте есть какой-то хитрый автокомплит, который нужно как-то хитро кликать, возможно, печатать через клавиатуру. Вы можете создать компонент MyCustomAutocomplete, унаследовать его от Input и переопределить метод fill. Далее такой компонент можно будет использовать во всем проекте без дублирования логики ввода.
- Если у вас над автотестами работают сразу несколько QA Automation команд, например, одна тестирует админку, другая тестирует сайт, то вы можете вынести весь Page Factory внутрь библиотеки. Библиотеку можно запушить на pypi или на свой приватный nexus сервер. Далее библиотекой могут пользоваться все QA Automation команды, вы получите общий ентри поинт для написания UI авто тестов, общие шаги и проверки.
- Last but not least. Предложенный мною подход Page Factory максимально простой, а это очень важно для масштабирования в будущем. Хуже, когда в коде наблюдается "магия" и эта "магия" понятна только тому, кто ее создал. В решение выше та "магия" отсутствует, все максимально прозрачно и в рамках обычного ООП.
Возможно, данный подход не является классической реализацией Page Factory, но это единственное рабочее и адекватное решение, которое мне удалось выработать. Предложенное мною решение способно закрыть все вопросы и проблемы работы с компонентами.
Для меня главным образом закрывается две задачи:
- Фокус на тестировании бизнес логики, без вечных головоломок с кодом;
- Красивый и понятный отчет, который спокойно могут читать Manual QA, менеджеры и разработчики.
Pages
Теперь опишем страницы, которые нам понадобятся, уже с использованием Page Factory.
Основная страница playwright https://playwright.dev
pages\playwright-home-page.ts
Страница с языками https://playwright.dev/python/docs/languages
Components
Теперь опишем компоненты, которые нам будут нужны.
Navbar
SearchModal
Лайфхак. Если вы путаетесь и не знаете, как назвать какой-либо компонент, то можете посмотреть, как называются компоненты у разработчиков внутри React/Vue/Angular frontend приложения. Да, не всегда есть такая возможность, но если имеется доступ к frontend приложению, то советую брать названия и структуру компонентов именно от туда. Разработчики точно лучше знают структуру frontend приложения, чем любой QA Engineer, + вам будет легче в будущем повторно использовать компоненты и композицию. В нашем примере c https://playwright.dev/ мы не имеем доступа к коду приложения, поэтому называем компоненты по смыслу.
Testing
Теперь настало время теста. Тут все просто, у нас есть готовые страницы, тест соберем, как конструктор, но перед этим напишем фикстуры.
Лайфхак. Наверняка у вас в проекте есть много ресурсов, которые не влияют на основную бизнес-логику приложения. Например такие, как шрифты (woff,woff2), картинки (png, jpg, jpeg), иконки (ico, svg), музыкальные файлы (mp3), какие-то конфиги, которые фронт подгружает с бэка, можно отключить, это позволит сильно ускорить ваши тесты, проверьте. Конечно, не все ресурсы можно отключить, к этому тоже нужно относиться очень внимательно, посмотрите, что можно отключить или посоветуйтесь с разработчиками, например, отключать main.js/index.js/app.js или чанки точно не стоит :) Мы отключим загрузку статических файлов c помощью playwright:
Теперь сделаем extend стандартного test объекта из playwright и добавим в него свои фикстуры
В данном примере combineFixtures - это вспомогательный метод, который поможет нам собрать несколько объектов фикстур в один. В принципе можно обойтись и без него, но мне так удобнее:
При использовании Page Object, Page Factory, как описано выше, тесты пишутся легко и понятно. И, самое главное, это позволяет нам фокусироваться на тестировании бизнес логики продукта, а не на ерунде по типу, как написать test.step, как написать проверку или как мне динамически подставить параметр в локатор.
Report
Запустим тесты и посмотрим на отчет:
Теперь запустим отчет:
Либо можете собрать отчет и в папке allure-reports открыть файл index.html:
Получаем прекрасный отчет, в котором отображается вся нужная нам информация.
Заключение
Весь исходный код проекта расположен на моем github.