Паттерны автоматизации и архитектура автотестов
Добрый день, меня зовут Виктория и я много лет занимаюсь автоматизацией. В этой статье я хотела бы рассказать о паттернах автоматизации, которые использую, а также о такой штуке, как архитектура проекта.
Я разворачивала проекты на разных языках программирования и для различных типов проектов (мобильные, веб, как чистый фронтенд, так и бэкенд). Для себя я выделила 6 паттернов программирования:
Page Object Pattern (Паттерн Объекта Страницы)
Этот паттерн разделяет логику взаимодействия с веб-страницами от тестового кода, улучшая читаемость и обслуживаемость тестов. Паттерн позволяет отделить логику тестирования от логики реализации веб-страниц.
Data Generation Pattern (Паттерн генерации данных)
К этому паттерну относится:
2.1. Классический Data-Driven Testing (Тестирование на Основе Данных) паттерн, который позволяет запускать один и тот же тест с разными наборами данных для проверки различных сценариев.
2.2. Я также отношу к нему создание отдельных классов или библиотек, которые автоматически генерирует тестовые данные для тестов (рандомные и/или кастомные).
Steps Pattern
Этот паттерн отвечает за создания слоя бизнес-логики. Это паттерн проектирования автоматизированных тестов или автоматизации действий, когда тестовый сценарий разбивается на отдельные шаги (steps) или этапы для удобства выполнения и поддержки. Чаще всего каждый шаг описывает какое-то бизнес действие системы. Такое как: «Пользователь_вошел_в_систему», «Пользователь_сделал_заказ» и прочие.
Этот подход позволяет разделять тестовые действия на небольшие и понятные шаги, каждый из которых выполняет конкретное действие или проверку. Каждый шаг теста представляет собой отдельную функцию, метод или действие, которое может быть повторно использовано в различных тестовых сценариях.
В этот паттерн я включаю:
3.1. Классический Behavior-Driven Development (BDD) Pattern — о нем можно почитать самостоятельно.
3.2. Любой другой код суть которого описать поведение пользователя в отдельных блоках / шагах / методами / ключевых словах.
Паттерн проверок (Assert/Checker)
Это паттерн в тестировании программного обеспечения, который используется для утверждения (assert) того, что программа работает ожидаемым образом и находится в определенном состоянии после выполнения определенных действий. Основная цель этого паттерна — проверка корректности функционирования программы на разных этапах ее выполнения. Это включает в себя утверждение фактов о состоянии данных, результатах выполнения кода или ожидаемых действиях программы.
Примеры этого паттерна: сравнения ожидаемых результатов с фактическими, проверка, что программа находится в определенном состоянии после выполнения определенных операций или функций. Паттерн также переиспользуеться в разных тестах для разных проверок.
Паттерн логирования
Паттерн логирования — это методика, используемая для записи информации о действиях, событиях и состоянии системы во время ее работы. Целью логирования является создание удобочитаемых и информативных записей, которые могут помочь при отладке, мониторинге и анализе работы программы или системы.
Эти паттерны помогают создавать структурированные и устойчивые тесты, повышая их читаемость, обслуживаемость и эффективность.
Но возникает вопрос: как же эти паттерны работают вместе и образую одну единую архитектруру?
Для себя я выделила следующую структуру- есть три слоя или контекстных области, которые взаимодействуют друг с другом:
Контекстная область взаимодействия с приложением, которое мы тестируем. Я называю ее ядром (Core).
Контекстная область тестов (Tests).
Контекстная область раннера автотестов (Runner).
Первый слой — это ядро, в котором находятся материалы, связанные с описанием нашего приложения и взаимодействием нашего кода с ним. Он содержит методы, которые вызывают API, методы. Они в свою очередь взаимодействуют с базой данных, методы, которые описывают архитектуру веб страниц с локаторами. Тут описывается все, что связано с контекстом приложения. Сюда относится паттерн Page Object Pattern.
Второй слой — тестовый слой. Сюда относится всё, что связано с автотестами, включая всё необходимое для запуска тестов (пред настройки), создания тестовых данных для тестов и проверок, которые происходят в тесте. Я называю этот слой — контекстом автотестов. Сюда относятся паттерн шагов, паттерн проверок и паттерн данных.
Третий слой — это слой раннера автотестов. Сюда относится паттерн логирования и всё, что связано с запуском тестов в определенной среде (предварительные настройки, последующие настройки).
Расскажу чуть подробнее про каждый слой:
1. Контекстная область взаимодействия с приложением
Core — в нем находятся материалы, связанные с описанием нашего приложения и взаимодействием нашего кода с ним. Он содержит три блока. Эти блоки представляют собой модули, которые работают независимо друг от друга, обеспечивая модульность, читаемость и легкость поддержки:
1.Блок Page Object — это шаблон проектирования для автоматизации тестирования веб-приложений. Он предполагает создание объектно-ориентированных моделей страниц вашего веб-приложения, которые отражают функциональность и элементы интерфейса страниц.
Page Object упрощает обслуживание тестовых сценариев, так как при изменении элементов интерфейса или логики страницы, нужно вносить изменения только в соответствующий объект Page Object, не затрагивая другие части тестового кода.
Кратко говоря, Page Object позволяет абстрагировать веб-страницы в объекты, делая код тестов более читаемым, модульным и легким в поддержке.
2.Блок API методы — этот блок предоставляет функции для взаимодействия с веб-сервисом API. Он включает отправку запросов к бекенд серверу и перехват запросов, что может быть полезным для эмуляции данных или обработки входящих запросов.
Отправка запросов: позволяют через API получить данные или взаимодействовать с данными на сервере.
Перехват запросов: некоторые API методы также могут предоставлять возможность перехвата или обработки запросов, которые поступают к приложению. Это может быть полезным для моканья ответа с сервера.
3.Блок BD методы — предоставляет собой инструмент взаимодействия в базой данных из кода.
Основные аспекты этих блоков включают:
Модульность и виртуальные границы: Каждый блок представляет собой отдельный компонент с четко определенными функциями. Они не зависят друг от друга и выполняют свою задачу независимо. К примеру, если вам нужен метод, который проверит, что вся страница «Заказы» загрузилась корректно т.е. метод. который визуально просмотрит страницу на предмет отображения элементов и проверит, что API запрос пришел со статусом 200, тогда вам нужно для этого создать тестовый шаг.
Этот шаг будет описан в слое Tests в блоке Steps. Его можно назвать User_check_opders_page. Он будет включать в себя переход на страницу через pageOpject order класс этой страницы, проверка видимости элементов страницы, а также в в этом шаге будет проверка на то, что API запрос о получение данных всех заказов вернулся со статусом 200.
Использование настроек: Настройки для вызова методов, такие как конфигурации базы данных, доменные URL страниц для Page Object, и URL для API, хранятся в слое runner. Это упрощает изменение конфигурации без изменения кода.
2. Контекстная область тестовых сценариев
Концепция второго слоя, тестового слоя, включает в себя все аспекты автотестов. Она включает в себя несколько ключевых компонентов:
Файлы автотестов: это сценарии, тест- кейсы или файлы, в которых описаны автотесты.
Блок генерации тестовых данных: эти методы используются для создания данных, необходимых для проведения тестов. Обычно эти методы вызываются в начале теста для подготовки данных прогона тестов.
Тестовые шаги (Steps): они представляют собой методы, которые описывают последовательность действий, выполняемых пользователем в приложении. Эти шаги выполняются в тесте. Эти методы как раз таки используют методы из первого слоя (например, методы Page Object) для взаимодействия с тестируемой системой.
Проверки состояния системы (Check): после выполнения тестовых шагов идут проверки для подтверждения, что система находится в ожидаемом состоянии. Эти проверки сравнивают фактическое состояние системы с ожидаемым результатом. Они также проверяют состояние системы на соответствие ожиданиям.
На диаграмме ниже показаны то, как блоки вызывают друг друга.
Реальный тест включает в себя следующий ход:
В начале автотеста вызывается Шаг, который вызывает генерацию тестовых данных. Эти данные в дальнейшем используются в тесте. Потом выполняются шаги пользователя в системы. В конце автотеста вызывается шаг, который вызывает методы проверки состояния системы.
Получаем следующий тестовый путь:
Подготовка тестовых данных
Выполнение шагов взаимодействия с системой
Проверка состояния системы
Вынос генерации тестовых данных и тестовых проверок в отдельные методы — это не только организационная стратегия, но и мощное средство для повторного использования, в процессе создания тестовых сценариев. Такой подход дарит нам возможность эффективно использовать эти методы в различных тестах, создавая универсальные инструменты, которые не привязаны к конкретному сценарию.
Вынося генерацию тестовых данных в отдельные методы, мы создаем механизм, который может создавать разнообразные варианты начального состояния системы для различных тест-кейсов.
Точно так же, разбивая последовательные действия пользователя на отдельные шаги, мы формируем модули, которые описывают конкретные этапы взаимодействия с системой. Эти шаги становятся повторно используемыми блоками, что облегчает поддержку и модификацию тестовых сценариев. Кроме того, такая модульная структура улучшает читаемость кода и позволяет легко добавлять или изменять шаги без внесения изменений во всю тестовую логику.
3. Контекстная область запуска автотестов
Кратко расскажу о содержании этого слоя. Он включает в себя настройку окружения, логирование и создание скриншотов во время выполнения автотестов, создание репортов и отправку репортов в различные каналы. Кроме того, сюда входят глобальные хуки «before» и «after», где осуществляются глобальные настройки, передача переменных окружения, предварительная и завершающая настройка приложения. Важно отметить, что эти блоки также независимы друг от друга и связаны (вызываются) исключительно с тестовым слоем. То есть, имеют свои виртуальные границы. Отдельно настройки окружения, отдельно логирование, отдельно создание репорта и отправка.
4. Как между собой взаимодействуют слои
Слои взаимодействия в этой архитектуре крайне четко определены. Методы из ядра напрямую общаются только с шагами. Слой Core, содержащий основные методы и функциональность, организован таким образом, чтобы взаимодействовать исключительно с шагами, облегчая тем самым структуру и чистоту кода.
С другой стороны, компоненты раннера (или компоненты запуска) взаимодействуют только с сущностью автотестов, или, иначе говоря, с основными элементами, необходимыми для выполнения тестовых сценариев. Этот слой фокусируется на подготовке и запуске самих тестов, обеспечивая эффективное и правильное их выполнение.
5. Общая картина нашей структуры и взаимодействия
Общая картина нашей архитектуры выглядит как на картинке нижу, однако, мы все понимаем, что в реальности такая структурна не всегда нужна.
Важно понимать, что созданный нами фреймворк представляет собой все таки монолит — набор модулей, которые мы никогда не будем выносить отдельно в микросервисы. Однако, необходимо учитывать логические связи и предотвращать их пересечение. В нашем контексте важно соблюдать эти виртуальные границы, чтобы поддерживать структурную чистоту и управляемость нашего фреймворка.
Подводя итог всего написаного выше: выносите контексты в отдельные модули и соблюдайте виртуальные границы)))
Спасибо за внимание!