Как правильно писать UI авто тесты на Python
Как правильно писать UI авто тесты на python используя Playwright, Page Object, Page Component, Page Factory.
Вступление
Устал смотреть на то, как многие QA Automation пишут свои абсолютно костыльные решения, используя паттерны Page Object, Page Factory. Так происходит, потому что в сфере QA Automation нет каких-то определенных рамок и паттернов, по которым стоит писать авто тесты. Да, есть всеми известный Page Object, но даже его часто используют очень криво. Например, в бэкенд разработке есть много паттернов, один из них MVC, который четко говорит, куда складывать роутинг, куда модели, а куда бизнес логику. Но в автоматизации нет каких-то конкретных паттернов, которые скажут, куда писать allure.step, куда писать проверки, как динамически форматировать локатор. Отсюда возникают мнения, и каждое якобы правильное, каждый лучше знает, как лучше, но на самом деле нет. Возникают множество "правильных" решений, но только по мнению создателя этих решений.
Поэтому решил написать статью о том, как правильно писать UI авто тесты и описать те подходы, к которым я пришел через годы практики. Все описанное ниже имеет конкретное предназначение для написания UI авто тестов в реальных, коммерческих проектах. Главной задачей этой статьи сделать так, чтобы тестировалась бизнес логика продукта, при этом в коде и в отчете авто тестирования все выглядело красиво.
Requirements
Для примера написания UI авто тестов мы будем использовать:
- pytest - pip install pytest
- playwright - pip install pytest-playwright
- allure - pip install allure-pytest, не обязательная зависимость, вы можете использовать любой другой репортер
Почему не Selenium? Скорее всего, потому что playwright удобнее и современнее. У playwright есть много крутых фич, с которыми он рвет Selenium в пух, ниже разберемся, как и почему. Все, что будет описано ниже с использованием playwright, также применимо к любому другому фреймворку.
Но опять же, фреймворк - это всего лишь инструмент. Неумелый QA Automation, используя самый крутой фреймворк, напишет код гораздо хуже, чем опытный QA Automation, используя обычный Selenium без всяких оберток.
Во всех примерах ниже мы будем использовать синхронный API для playwright.
Авто тесты будем писать на эту страницу https://playwright.dev/. Сам тест кейс будет простой, но он будет показывать всю концепцию правильной работы с Page Object, Page Factory.
Тест кейс:
- Открываем страницу https://playwright.dev
- Нажимаем на поиск
- Проверяем, что модальное окно поиска успешно открылось
- Вводим в поиск язык, в нашем случае будет python
- Выбираем из результатов первый
- Проверяем, что страница с Python открылась
Отмечу, что локаторы в примерах ниже не являются эталонными, а сайт для тестирования, это документация playwright, на фронтенд которой я никак не могу повлиять. В ваших проектах советую использовать кастомные data-qa-id, которые вы можете поставить в фронтенд приложении React/Vue/Angular, ну или попросить разработчиков сделать это.
Base Page
По сути Base Page - это основная страница, которая не описывает какую-то конкретную страницу или компонент. Сама по себе Base Page не должна использоваться в тестах, от нее мы наследуем наши страницы или компоненты.
Внутри BasePage описываем базовые методы. Это лишь образец того, как можно делать BasePage
Page Factory
Теперь самое интересное. Мы определим несколько базовых компонентов, для реализации работы паттерна.
Базовый Component. В python нет интерфейсов и понятия имплементации, поэтому сделаем Component абстрактным классом и унаследуем его от ABC
Выше приведена очень упрощенная реализация Component. В своем проекте, вы сможете добавить больше методов, больше настроек к ним, заменить allure.steps на другие.
Давайте сделаем еще несколько компонентов
Button - кнопка. В данном компоненте будут базовые методы для работы с кнопками.
Input - поле ввода. В данном компоненте будут базовые методы для работы с инпутами.
Link - ссылка. В данном компоненте будут базовые методы для работы со ссылками.
ListItem - любой элемент списка.
Title - заголовок. Можно использовать просто Text, но я предпочитаю разделать все, для понятности. Title, Text, Label, Subtitle...
Теперь вопрос: "Зачем все это?". Данный подход решает сразу тонну проблем и вопросов, которые возникают у любого QA Automation, который хоть раз писал UI авто тесты.
- Дает удобный и понятный интерфейс для работы с объектами на странице. То есть мы работаем не с каким-то там локатором, а с конкретным объектом, например, Button.
- Универсализирует все взаимодействия и проверки компонентов. Очень хорошо для команд, где над авто тестами работают два и более QA Automation, ну или если авто тесты пишут разработчики тоже. С таким подходом у вас не будет споров и проблем, то есть один QA Automation может писать так expect(locator).to_be_visible() , что является единственно правильным с точки зрения playwright. Второй QA Automation может писать так assert locator.is_visible(), что тоже по сути правильно, но костыльно. На этой основе могут возникать бесполезные споры или еще хуже: каждый пишет так, как он хочет. По итогу получаем проект, в котором одни и те же проверки пишутся по разному. С данным подходом мы один раз устанавливаем, как делается проверка и забываем об этом, все работает прекрасно.
- Дает возможность универсализировать все шаги для отчета. В примере выше я использовал allure, но на самом деле это не важно, вы можете использовать любой репортер. При объявлении нового компонента, нам не нужно переписывать все шаги, они динамически формируются на основе параметров, name, type_of. Конечно же, вы можете их изменить и расширить под ваши требования. Достаточно переопределить type_of и мы получаем новый компонент с полностью уникальными шагами.
- Динамические локаторы - это вечная боль, но не с данным подходом. Механизм форматирования локатора до безобразия прост. Мы передаем **kwargs в каждый метод, далее все это идет в сам локатор self.locator.format(**kwargs). То есть это позволяет писать нам локаторы span#some-element-id-{user_id}, далее передавать user_id через **kwargs прямо в локатор. Механизм прост, но он избавляет нас от локаторов в методах, от дублирования или еще хуже хардкода локаторов.
- Появляется возможность создавать компоненты, работа с которыми специфична. Например, у вас в продукте есть какой-то хитрый авто комплит, который нужно как-то хитро кликать, возможно, печатать через клавиатуру. Вы можете создать компонент 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
Страница с языками https://playwright.dev/python/docs/languages
Components
Теперь опишем компоненты, которые нам будут нужны.
Navbar
SearchModal
Testing
Теперь настало время теста. Тут все просто, у нас есть готовые страницы, тест соберем, как конструктор, но перед этим напишем фикстуры.
conftest.py
Я не буду объяснять, как работают и как писать фикстуры в pytest, для этого уже есть много информации. Скажу только то, что инициализацию объектов страниц лучше выносить внутрь фикстур, чтобы избежать дублирования внутри теста.
test_search.py
При использовании Page Object, Page Factory, как я описал выше, тесты пишутся легко и понятно. И, самое главное, это позволяет нам фокусироваться на тестировании бизнес логики продукта, а не на ерунде по типу, как написать allure.step, как написать проверку или как мне динамически подставить параметр в локатор.
Заключение
- Весь исходный код проекта вы можете посмотреть на моем github https://github.com/Nikita-Filonov/playwright_python
- Пример проекта на selenium https://github.com/Nikita-Filonov/selenium_python
- Полную версию allure отчета посмотрите тут.
Подход, который описан выше, можно использовать не только с Playwright. Его можно использовать с Selenium, Pylenium, PyPOM, Selene, с чем угодно. Фреймворк всего лишь инструмент и применять его можно по-разному.