- PVSM.RU - https://www.pvsm.ru -
Head of QA
Привет! В этой статье поделюсь личным опытом создания и эксплуатации проектного фреймворка автотестирования API. Это моя первая статья, надеюсь, получилось достаточно структурировано и понятно.
Статья получилась объемной и будет разделена на три части.
В первой части рассматриваются причины создания собственного проектного фреймворка, формулируются его ключевые принципы и приводится как краткий обзор технологического стека, так и развернутое обоснование выбора каждого компонента.
Во второй части описывается архитектура фреймворка. Рассматривается слоистая структура, подходы к генерации API-клиентов, стандарты качества кода, управление жизненным циклом прогона, стратегии обеспечения производительности и масштабируемости тестовых сценариев.
В третьей, заключительной части раскрываются аспекты эксплуатации. Рассказывается об организации воспроизводимого окружения через Docker, проектировании CI/CD-пайплайна, настройке отчетности с использованием Allure TestOps, руководстве по добавлению новых тестов, приводятся практические кейсы, построенные вокруг реальных задач и типовых трудностей.
Введение
Зачем создавать собственный проектный фреймворк
Принципы проектного фреймворка
Технологический стек проектного фреймворка в кратком обзоре
Расширенное описание и обоснование выбора технологий проектного фреймворка
Заключение
Немного расскажу о контексте и предыстории. Когда я пришел в известную компанию-маркетплейс, автотестирование в отделе только зарождалось – мне дали четкую задачу: создать быстрый, унифицированный, масштабируемый фреймворк для REST API-тестов и перенести на него все legacy-автотесты. Legacy-тесты были типичными для первого уровня зрелости модели автоматизации тестирования (Level 1 – Ad-hoc), когда тесты создаются разово, без единой архитектуры, переиспользования и учета дальнейшей поддержки.
К этому моменту у команды уже созрело понимание того, что такой подход исчерпал себя. Поддержка занимала с каждым новым тестом все больше и больше времени, в коде было трудно ориентироваться, а каждое изменение API грозило цепной реакцией падающих тестов. В итоге был взят курс на создание с нуля проектного фреймворка для автоматизированного тестирования.
В этой статье будут описаны выбранные технологии, подходы, устройство фреймворка и опыт его эксплуатации с описанием проблем, с которыми пришлось столкнуться.
Статья техническая и адресована инженерам-программистам в тестировании (SDET) и инженерам по автоматизации тестирования, которые перешли от написания обособленных тестов к осознанному проектированию тестового фреймворка или только начинают этот переход.
Зачем создавать собственный проектный фреймворк для API-автотестов, если есть коллекции автотестов Postman [1]?
На первый взгляд, инструменты вроде Postman [1] или Bruno [2] выглядят удобными: они позволяют быстро создавать коллекции запросов, параметризовать их, писать простые проверки и даже запускать тесты в CI/CD. Однако при переходе от единичных проверок к полноценной автоматизированной системе тестирования API эти инструменты начинают демонстрировать серьезные ограничения.
К таким ограничениям можно отнести:
Отсутствие гибкой логики и повторного использования кода.
В Postman/Bruno сложно реализовать сложную логику, например, динамическую генерацию данных, кастомные retry-механизмы или условные ветвления, без дублирования кода. В проектном фреймворке можно легко выносить общую логику в функции, классы и модули.
Прямая работа с базой данных для подготовки и верификации состояния.
Во многих сценариях недостаточно проверять только HTTP-ответы, требуется убедиться, что данные корректно сохранились, изменились или удалились в БД. Postman не предоставляет удобных средств подключения к внутренним базам, выполнения запросов и транзакций. В проектном фреймворке можно напрямую взаимодействовать с БД: создавать предусловия, проверять постусловия, откатывать изменения.
Ограниченная поддержка типов и статической проверки.
В инструментах вроде Postman/Bruno структура запросов и ответов описывается неявно, в виде сырых JSON-объектов или переменных. Это делает крайне затруднительной проверку полноты и корректности полей, можно легко пропустить обязательное поле, ошибиться в его типе или не заметить, что API изменило формат ответа.
Сложности с управлением тестовыми данными и окружением.
В Postman управление данными часто сводится к CSV/JSON-файлам и глобальным переменным, что затрудняет реализацию стратегий вроде data factories, фейковых сущностей или очистки состояния после тестов.
Плохая интеграция с инфраструктурой разработки.
Хотя Postman умеет запускаться в CI, он плохо сочетается с артефактами сборки, сикретами и внутренними библиотеками и не позволяет с ними гибко работать.
Отсутствие контроля над архитектурой и расширяемостью.
Готовые инструменты накладывают жесткие рамки на то, как можно организовывать тесты, обрабатывать ответы или интегрироваться с внешними системами. Их внутреннее устройство нельзя изменить, а расширение функциональности часто ограничено возможностями встроенного скриптового движка. Собственный проектный фреймворк дает полный контроль над архитектурой, позволяя самостоятельно определять правила написания тестов, формат логирования, обработку ошибок и генерацию отчетов и при необходимости легко адаптируете их под меняющиеся требования продукта.
Трудности с поддержкой и масштабированием.
Когда тестов ст��новится сотни или тысячи, коллекции в Postman превращаются в неструктурированный «ад», сложно навигировать, версионировать, рефакторить или вносить массовые изменения. В проектном фреймворке все это решается через обычные средства разработки – IDE, git, модульность и паттерны.
Postman/Bruno – это отличные инструменты для ручной и разведочной работы с API, а также для быстрого прототипирования. Но как только автоматизация становится частью процесса CI/CD, необходима полноценная кодовая база в виде проектного фреймворка, который обеспечивает устойчивость, поддерживаемость и масштабируемость автотестов.
Проектирование тестового фреймворка начинается с определения основополагающих принципов проектного фреймворка. Принципы задают критерии качества и служат ориентирами при принятии архитектурных и технических решений.
Выделим несколько ключевых принципов проектного фреймворка:
Надежность – автотесты выполняются в идентичных изолированных средах, обеспечивая идемпотентность и фокусируясь на реальных проблемах, а не на артефактах окружения.
Скорость – минимизация времени прогона для оперативной обратной связи.
Масштабируемость – гибкая архитектура, адаптирующаяся под рост количества тестов, продуктов и команд.
Самодокументируемость – интуитивно понятные названия и структура.
Чистота кода – лаконичная, предсказуемая структура с соблюдением общепринятых стандартов, понятная как новичкам, так и опытным инженерам.
Именно на этих принципах строится выбор всех технологий и компонентов фреймворка, включая основной язык программирования (ЯП), тестовое ядро, вспомогательные библиотеки, стратегии сбора проекта и его запуска.
В основу выбора технологий и инструментов легли заложенные в проектный фреймворк принципы. Ниже представлен стек, используемый при его разработке и эксплуатации. Каждая библиотека и каждый инструмент прошли отбор среди альтернатив и были утверждены по итогам оценки их соответствия этим принципам.
|
Категория |
Технология / Инструмент |
|---|---|
|
Основной язык программирования |
Python 3.12 |
|
Ядро автотестов |
pytest |
|
Вспомогательные библиотеки |
pytest-xdist, pytest-rerunfailures, pytest-dependency, allure-pytest, pytest-html |
|
Генерация клиентов API |
Swagger Codegen (на основе OpenAPI-спецификаций) |
|
HTTP-клиент |
requests, urllib3 |
|
Валидация данных |
кастомная валидация swagger-codegen, pydantic |
|
Генерация тестовых данных |
Faker + Builder pattern |
|
Отчётность |
Allure |
|
Управление задачами |
Invoke |
|
Окружение и запуск |
GitLab CI/CD, Docker |
Далее рассмотрим более подробно, почему именно эти инструменты были выбраны, и как они способствуют соблюдению основополагающих принципов проектного фреймворка.
Этот раздел содержит детальное техническое обоснование выбора используемых технологий. Если вас в первую очередь интересует устройство фреймворка, а не то, почему был выбран тот или иной язык программирования и конкретные библиотеки, то переходите к следующей статье.
Проектный фреймворк базируется на python версии 3.12. Выбор именно python был обусловлен практическими соображениями и особенностями внедрения автоматизированного тестирования в отделе, когда стояла острая необходимость максимально быстро и с минимальными затратами покрыть автотестами значительный объем API-функционала.
Благодаря низкому порогу входа, высокой скорости разработки и богатой экосистеме готовых, хорошо оптимизированных решений python как нельзя лучше подошел для наших целей. Выбор python позволил не только упростить адаптацию инженеров по автоматизированному тестированию, пришедших к нам с других ЯП, но и стал катализатором роста для инженеров по ручному тестированию, позволив им в короткие сроки (3-4 месяца) освоить написание автотестов и начать создавать и поддерживать автотесты уже самостоятельно.
В основе тестового движка лежит фреймворк-ориентированная библиотека pytest [3]. Pytest органично вписался в стратегию развития автотестирования, полностью отвечая заложенным в фреймворк принципам.
Библиотека обеспечивает надежность за счет изолированных и идемпотентных тестовых сред, ускоряет обратную связь благодаря простоте написания и возможностям параллелизации, поддерживает масштабируемость через гибкую архитектуру и богатую экосистему плагинов, способствует самодокументируемости за счет читаемого и декларативного синтаксиса, а также поощряет чистоту и предсказуемость кода.
В проектном фреймворке для расширения возможностей pytest используется ряд специализированных решений, которые усиливают фреймворк в ключевых направлениях:
pytest-xdist [4]ускоряет прогон автотестов за счет параллельного запуска, сокращая время получения обратной связи.
pytest-rerunfailures [5] повышает стабильность, автоматически перезапуская упавшие тесты и фильтруя временные сбои или flaky-падения.
pytest-dependency [6] позволяет выстраивать сквозные сценарии, явно задавать зависимости между тестами и избегать бессмысленных запусков, если предварительный шаг заведомо не пройден, последующие тесты автоматически пропускаются.
Взаимодействие автотестов с API в проектном фреймворке основывается на двух подходах:
Для стандартных сценариев используется автогенерация клиентского кода [7] на основе OpenAPI-спецификаций, где по умолчанию в качестве HTTP-транспорта задействована библиотека urllib3 [8].
Для нестандартных сценариев используются самописные клиенты на базе известной и простой в использовании библиотеки requests [9].
В обоих случаях тела запросов и ответов строго типизированы с помощью моделей swagger-codegen [7] либо pydantic [10]-моделей. При автогенерации модели создаются автоматически на основе схем из OpenAPI, для кастомных клиентов модели создаются вручную.
Использование моделей swagger-codegen и pydantic-моделей дает сразу несколько ключевых преимуществ для написания автотестов:
Значительно сокращает объем кода, за счет отсутствия многочисленных проверок значений, по типу assert 'id' in response.json().
Благодаря четкому разделению зоны ответственности сокращает время отладки автотеста, по сравнению с legacy-подходом, в 5-7 раз.
При изменении контракта API достаточно обновить одну pydantic-модель, а не десятки зависимых от нее тестов.
По сравнению с другими подходами к валидации и парсингу данных (нативное использование dataclasses [11] или громоздкой альтернативы в виде библиотеки marshmallow [12]) pydantic полезен тем что:
Обладает высокой читаемостью и самодокументируемостью, например, в отличии от того же marshmallow, где схемы описываются отдельно от данных и часто требуют избыточного синтаксиса, а поля не видны напрямую в IDE как атрибуты объекта.
Автоматически валидирует типы и структуру данных на основе аннотаций python.
По-умолчанию уже поддерживает вложенные модели, опциональные поля, валидаторы и алиасы.
Сгенерированные (или ручные) модели HTTP-запросов тесно интегрированы с системой генерации тестовых данных на основе библиотеки faker [13] и паттерна Builder [14].
Каждый билдер создает валидное тело запроса без ручной настройки благодаря автоматическому заполнению обязательных полей осмысленными синтетическими данными, близкими к реальным, с добавленной кастомной возможностью подтягивать актуальные значения из базы данных (БД) продукта.
Такой подход позволяет инженерам по автоматизированному тестированию сосредоточиться на логике теста, а не на подготовке данных. Сценарии становятся короче, чище и нагляднее.
Ниже представлен пример автотеста, объемом кода всего в несколько строк:
def test_create_layout_with_valid_data(layout_api_client):
# Валидные данные по умолчанию + кастомизация
body = CreateLayoutBuilder()
.set_name("Test Layout")
.set_zone_id(42)
.set_items_count(5)
.build()
response = layout_api_client.v1_base.create_layout(body=body)
assert response.status_code == 201
LayoutResponseSchema.model_validate(response.json())
Для тестирования API часто требуется получать или проверять актуальные боевые данные напрямую из базы данных. Для этого в проектном фреймворке используется DSL (Domain Specific Language) [15] для построения SQL-запросов. Такой подход исключает ручную конкатенацию строк, предотвращает SQL-инъекции и делает запросы читаемыми и поддерживаемыми.
Среди существующих инструментов для работы с SQL в python-коде можно выделить несколько подходов:
Полноценные ORM [16], такие как SQLAlchemy [17], Django ORM [18] или Tortoise ORM [19]. Они предлагают удобное объектное представление таблиц, но требуют описания моделей, управления сессиями и часто неявно генерируют сложные запросы, что снижает прозрачность в тестах.
Ручная сборка запросов через f-строки [20] или шаблоны [21]. Такой подход максимально гибкий, но небезопасный и подвержен ошибкам.
DSL-подход предполагает для работы с SQL использовать специализированные библиотеки для программного построения SQL-запросов без привязки к объектам или состоянию. Такой подход безопасен, сохраняет читаемость и дает полный контроль над итоговым запросом.
Исходя из требований к тестовому фреймворку (надежность, скорость, масштабируемость, самодокументируемость и чистота кода) был выбран именно DSL-подход. Он обеспечивает предсказуемое поведение, минимальные накладные расходы и прозрачную связь между кодом и выполняемым SQL.
В качестве реализации DSL была выбрана библиотека pypika [22], поскольку эта библиотека лучше всего соответствует этим принципам:
Надежность обеспечивается за счет автоматической параметризации всех значений, что полностью исключает риск SQL-инъекций [23].
Скорость достигается благодаря отсутствию необходимости описывать модели, управлять сессиями или загружать метаданные, формирование и выполнение запросов происходит с минимальными накладными расходами.
Масштабируемость поддерживается за счет композиции, когда общие части запросов, такие как фильтрация по окружению или статусу, легко выносятся в переиспользуемые компоненты без дублирования логики.
Самодокументируемость проявляется в том, что благодаря близкому к SQL синтаксису pypika из самого кода сразу понятно, какие данные запрашиваются и по каким условиям.
Чистота кода обеспечивается цепочечным стилем, который позволяет выразить даже сложный запрос в одну–две логически цельные строки, избегая вложенных условий, шаблонов и сложных конструкций.
В фреймворке расширен механизм проверок в автотестах за счет удобного и выразительного инструмента для написания читаемых, цепочечных и самодокументируемых проверок библиотеки assertpy [24]. Она не заменяет стандартный встроенный в python assert, а дополняет и расширяет возможности проверок, цепочечный синтаксиса позволяет выполнять несколько проверок подряд без дублирования переменных и без потери ясности, библиотека особенно полезна при проверки данных в бизнес-логики теста.
Пример цепочечного синтаксиса:
assert_that(order.total).is_greater_than(0).is_less_than(10000)
Для документирования тест-кейсов (ТК), планирования прогонов и визуализации результатов выполнения был выбран Allure TestOps [25]. На нем уже были созданы ТК, которые требовали автоматизации.
Чтобы связать автоматизированные тесты с существующими тест-кейсами в Allure и обеспечить стабильную синхронизацию между кодом и документацией, в фреймворк был интегрирован плагин allure-pytest [26]. Этот плагин перехватывает события выполнения тестов в реальном времени и автоматически генерирует структурированные данные, которые после прогона отправляются в Allure TestOps и формируют детализированный отчет.
Отправка данных автотестов в Allure TestOps осуществляется через кастомную invoke [27]-таску. Invoke [27] – это python-библиотека и CLI-утилита для определения и запуска повторяемых задач через декларативный синтаксис.
В отличии от bash-скриптов, invoke позволяет писать задачи на python, использовать аргументы с типизацией, валидацией и подсказками.
Для обеспечения воспроизводимости и стабильности результатов все автотесты запускаются в изолированном и идентичном окружении docker [28]-контейнера. Использование контейнеризации гарантирует, что зависимости, версии python, переменные окружения и сетевые настройки остаются одинаковыми независимо от места запуска, локально у инженера или в CI/CD-пайплайне.
Весь процесс выполнения автотестов интегрирован в GitLab CI/CD [29]. Хотя существуют альтернативные CI/CD-платформы (Jenkins [30], TeamCity [31], GitHub Actions [32]), выбор пал именно на GitLab CI/CD, поскольку в компании принят GitLab [33] как основная платформа для хранения кода и управления разработкой. Это позволяет использовать встроенный и нативно интегрированный механизм пайплайнов без необходимости разворачивать, настраивать и поддерживать отдельный CI-сервер.
Создание собственного проектного фреймворка автотестирования API – это стратегическое решение, направленное на обеспечение масштабируемости, удобства сопровождения и высокой эффективности тестов как средства проверки корректности API. Готовые инструменты вроде Postman отлично подходят для разведочного тестирования и быстрой проверки эндпоинтов, но не отвечают требованиям зрелой автоматизации в условиях CI/CD, командной разработки и сложной логики.
Выбор в пользу собственного фреймворка обусловлен необходимостью полного контроля над архитектурой, гибкостью в работе с данными, окружением, инфраструктурой и стремлением к устойчивой и воспроизводимости тестов. Принципы (надежность, скорость, масштабируемость, самодокументируемость и чистота кода) легли в основу проектного фреймворка и определили выбор технологий, архитектурных подходов и инструментов.
В следующей части статьи мы последовательно раскроем внутреннее устройство фреймворка. Рассмотрим архитектурные слои, генерацию клиентов на основе OpenAPI-спецификаций, подходы к обеспечению качества и читаемости кода, жизненный цикл тестового прогона, а также методы повышения производительности и масштабирования автотестов.
Автор: Jaldsky
Источник [34]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/440784
Ссылки в тексте:
[1] Postman: https://www.postman.com/
[2] Bruno: https://bruno-docs.vercel.app/get-started/bruno-basics/download
[3] pytest: https://pypi.org/project/pytest/
[4] pytest-xdist : https://pypi.org/project/pytest-xdist/
[5] pytest-rerunfailures: https://pypi.org/project/pytest-rerunfailures/
[6] pytest-dependency: https://pypi.org/project/pytest-dependency/
[7] автогенерация клиентского кода: https://github.com/swagger-api/swagger-codegen
[8] urllib3: https://pypi.org/project/urllib3/
[9] requests: https://pypi.org/project/requests/
[10] pydantic: https://pypi.org/project/pydantic/
[11] dataclasses: https://docs.python.org/3/library/dataclasses.html
[12] marshmallow: https://pypi.org/project/marshmallow/
[13] faker: https://pypi.org/project/Faker/
[14] паттерна Builder: https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D1%80%D0%BE%D0%B8%D1%82%D0%B5%D0%BB%D1%8C_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)
[15] DSL (Domain Specific Language): https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%B4%D0%BC%D0%B5%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D1%8F%D0%B7%D1%8B%D0%BA
[16] ORM: https://ru.wikipedia.org/wiki/ORM
[17] SQLAlchemy: https://pypi.org/project/SQLAlchemy/
[18] Django ORM: https://pypi.org/project/django-orm/
[19] Tortoise ORM: https://pypi.org/project/tortoise-orm/
[20] f-строки: https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals
[21] шаблоны: https://docs.python.org/3/library/string.html#custom-string-formatting
[22] pypika: https://pypi.org/project/PyPika/
[23] SQL-инъекций: https://ru.wikipedia.org/wiki/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_SQL-%D0%BA%D0%BE%D0%B4%D0%B0
[24] assertpy: https://pypi.org/project/assertpy/
[25] Allure TestOps: https://qameta.io/
[26] allure-pytest: https://pypi.org/project/allure-pytest/
[27] invoke: https://pypi.org/project/invoke/
[28] docker: https://www.docker.com/
[29] GitLab CI/CD: https://docs.gitlab.com/ci/
[30] Jenkins: https://www.jenkins.io/
[31] TeamCity: https://www.jetbrains.com/teamcity/
[32] GitHub Actions: https://docs.github.com/en/actions
[33] GitLab: https://about.gitlab.com/
[34] Источник: https://habr.com/ru/articles/982624/?utm_source=habrahabr&utm_medium=rss&utm_campaign=982624
Нажмите здесь для печати.