Артем

Head of QA

Привет, Хабр! В этой статье поделюсь личным опытом создания и эксплуатации проектного фреймворка автотестирования API. Это моя первая статья, надеюсь, получилось достаточно структурировано и понятно.

Статья получилась объемной и будет разделена на три части.

В первой части рассматриваются причины создания собственного проектного фреймворка, формулируются его ключевые принципы и приводится как краткий обзор технологического стека, так и развернутое обоснование выбора каждого компонента.

Во второй части описывается архитектура фреймворка. Рассматривается слоистая структура, подходы к генерации API-клиентов, стандарты качества кода, управление жизненным циклом прогона, стратегии обеспечения производительности и масштабируемости тестовых сценариев.

В третьей, заключительной части раскрываются аспекты эксплуатации. Рассказывается об организации воспроизводимого окружения через Docker, проектировании CI/CD-пайплайна, настройке отчетности с использованием Allure TestOps, руководстве по добавлению новых тестов, приводятся практические кейсы, построенные вокруг реальных задач и типовых трудностей.

Оглавление

  • Введение

  • Зачем создавать собственный проектный фреймворк

  • Принципы проектного фреймворка

  • Технологический стек проектного фреймворка в кратком обзоре

  • Расширенное описание и обоснование выбора технологий проектного фреймворка

  • Заключение

Введение

Немного расскажу о контексте и предыстории. Когда я пришел в известную компанию-маркетплейс, автотестирование в отделе только зарождалось – мне дали четкую задачу: создать быстрый, унифицированный, масштабируемый фреймворк для REST API-тестов и перенести на него все legacy-автотесты. Legacy-тесты были типичными для первого уровня зрелости модели автоматизации тестирования (Level 1 – Ad-hoc), когда тесты создаются разово, без единой архитектуры, переиспользования и учета дальнейшей поддержки.

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

В этой статье будут описаны выбранные технологии, подходы, устройство фреймворка и опыт его эксплуатации с описанием проблем, с которыми пришлось столкнуться.

Статья техническая и адресована инженерам-программистам в тестировании (SDET) и инженерам по автоматизации тестирования, которые перешли от написания обособленных тестов к осознанному проектированию тестового фреймворка или только начинают этот переход.

Зачем создавать собственный проектный фреймворк

Зачем создавать собственный проектный фреймворк для API-автотестов, если есть коллекции автотестов Postman?

На первый взгляд, инструменты вроде Postman или Bruno выглядят удобными: они позволяют быстро создавать коллекции запросов, параметризовать их, писать простые проверки и даже запускать тесты в CI/CD. Однако при переходе от единичных проверок к полноценной автоматизированной системе тестирования API эти инструменты начинают демонстрировать серьезные ограничения.

К таким ограничениям можно отнести:

  1. Отсутствие гибкой логики и повторного использования кода.

    В Postman/Bruno сложно реализовать сложную логику, например, динамическую генерацию данных, кастомные retry-механизмы или условные ветвления, без дублирования кода. В проектном фреймворке можно легко выносить общую логику в функции, классы и модули.

  2. Прямая работа с базой данных для подготовки и верификации состояния.

    Во многих сценариях недостаточно проверять только HTTP-ответы, требуется убедиться, что данные корректно сохранились, изменились или удалились в БД. Postman не предоставляет удобных средств подключения к внутренним базам, выполнения запросов и транзакций. В проектном фреймворке можно напрямую взаимодействовать с БД: создавать предусловия, проверять постусловия, откатывать изменения.

  3. Ограниченная поддержка типов и статической проверки.

    В инструментах вроде Postman/Bruno структура запросов и ответов описывается неявно, в виде сырых JSON-объектов или переменных. Это делает крайне затруднительной проверку полноты и корректности полей, можно легко пропустить обязательное поле, ошибиться в его типе или не заметить, что API изменило формат ответа.

  4. Сложности с управлением тестовыми данными и окружением.

    В Postman управление данными часто сводится к CSV/JSON-файлам и глобальным переменным, что затрудняет реализацию стратегий вроде data factories, фейковых сущностей или очистки состояния после тестов.

  5. Плохая интеграция с инфраструктурой разработки.

    Хотя Postman умеет запускаться в CI, он плохо сочетается с артефактами сборки, сикретами и внутренними библиотеками и не позволяет с ними гибко работать.

  6. Отсутствие контроля над архитектурой и расширяемостью.

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

  7. Трудности с поддержкой и масштабированием.

    Когда тестов становится сотни или тысячи, коллекции в 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 подошел лучше всего

Проектный фреймворк базируется на python версии 3.12. Выбор именно python был обусловлен практическими соображениями и особенностями внедрения автоматизированного тестирования в отделе, когда стояла острая необходимость максимально быстро и с минимальными затратами покрыть автотестами значительный объем API-функционала.

Благодаря низкому порогу входа, высокой скорости разработки и богатой экосистеме готовых, хорошо оптимизированных решений python как нельзя лучше подошел для наших целей. Выбор python позволил не только упростить адаптацию инженеров по автоматизированному тестированию, пришедших к нам с других ЯП, но и стал катализатором роста для инженеров по ручному тестированию, позволив им в короткие сроки (3-4 месяца) освоить написание автотестов и начать создавать и поддерживать автотесты уже самостоятельно.

Pytest как ядро проектного фреймворка

В основе тестового движка лежит фреймворк-ориентированная библиотека pytest. Pytest органично вписался в стратегию развития автотестирования, полностью отвечая заложенным в фреймворк принципам.

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

В проектном фреймворке для расширения возможностей pytest используется ряд специализированных решений, которые усиливают фреймворк в ключевых направлениях:

  • pytest-xdist ускоряет прогон автотестов за счет параллельного запуска, сокращая время получения обратной связи.

  • pytest-rerunfailures повышает стабильность, автоматически перезапуская упавшие тесты и фильтруя временные сбои или flaky-падения.

  • pytest-dependency позволяет выстраивать сквозные сценарии, явно задавать зависимости между тестами и избегать бессмысленных запусков, если предварительный шаг заведомо не пройден, последующие тесты автоматически пропускаются.

Подходы к работе с API-клиентами и валидацией моделей

Взаимодействие автотестов с API в проектном фреймворке основывается на двух подходах:

  • Для стандартных сценариев используется автогенерация клиентского кода на основе OpenAPI-спецификаций, где по умолчанию в качестве HTTP-транспорта задействована библиотека urllib3.

  • Для нестандартных сценариев используются самописные клиенты на базе известной и простой в использовании библиотеки requests.

В обоих случаях тела запросов и ответов строго типизированы с помощью моделей swagger-codegen либо pydantic-моделей. При автогенерации модели создаются автоматически на основе схем из OpenAPI, для кастомных клиентов модели создаются вручную.

Использование моделей swagger-codegen и pydantic-моделей дает сразу несколько ключевых преимуществ для написания автотестов:

  • Значительно сокращает объем кода, за счет отсутствия многочисленных проверок значений, по типу assert 'id' in response.json().

  • Благодаря четкому разделению зоны ответственности сокращает время отладки автотеста, по сравнению с legacy-подходом, в 5-7 раз.

  • При изменении контракта API достаточно обновить одну pydantic-модель, а не десятки зависимых от нее тестов.

По сравнению с другими подходами к валидации и парсингу данных (нативное использование dataclasses или громоздкой альтернативы в виде библиотеки marshmallow) pydantic полезен тем что:

  1. Обладает высокой читаемостью и самодокументируемостью, например, в отличии от того же marshmallow, где схемы описываются отдельно от данных и часто требуют избыточного синтаксиса, а поля не видны напрямую в IDE как атрибуты объекта.

  2. Автоматически валидирует типы и структуру данных на основе аннотаций python.

  3. По-умолчанию уже поддерживает вложенные модели, опциональные поля, валидаторы и алиасы.

Генерация тестовых данных

Сгенерированные (или ручные) модели HTTP-запросов тесно интегрированы с системой генерации тестовых данных на основе библиотеки faker и паттерна Builder.

Каждый билдер создает валидное тело запроса без ручной настройки благодаря автоматическому заполнению обязательных полей осмысленными синтетическими данными, близкими к реальным, с добавленной кастомной возможностью подтягивать актуальные значения из базы данных (БД) продукта. 

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

Ниже представлен пример автотеста, объемом кода всего в несколько строк:

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) для построения SQL-запросов. Такой подход исключает ручную конкатенацию строк, предотвращает SQL-инъекции и делает запросы читаемыми и поддерживаемыми.

Среди существующих инструментов для работы с SQL в python-коде можно выделить несколько подходов:

  • Полноценные ORM, такие как SQLAlchemy, Django ORM или Tortoise ORM. Они предлагают удобное объектное представление таблиц, но требуют описания моделей, управления сессиями и часто неявно генерируют сложные запросы, что снижает прозрачность в тестах.

  • Ручная сборка запросов через f-строки или шаблоны. Такой подход максимально гибкий, но небезопасный и подвержен ошибкам.

  • DSL-подход предполагает для работы с SQL использовать специализированные библиотеки для программного построения SQL-запросов без привязки к объектам или состоянию. Такой подход безопасен, сохраняет читаемость и дает полный контроль над итоговым запросом.

Исходя из требований к тестовому фреймворку (надежность, скорость, масштабируемость, самодокументируемость и чистота кода) был выбран именно DSL-подход. Он обеспечивает предсказуемое поведение, минимальные накладные расходы и прозрачную связь между кодом и выполняемым SQL.

В качестве реализации DSL была выбрана библиотека pypika, поскольку эта библиотека лучше всего соответствует этим принципам:

  • Надежность обеспечивается за счет автоматической параметризации всех значений, что полностью исключает риск SQL-инъекций.

  • Скорость достигается благодаря отсутствию необходимости описывать модели, управлять сессиями или загружать метаданные, формирование и выполнение запросов происходит с минимальными накладными расходами.

  • Масштабируемость поддерживается за счет композиции, когда общие части запросов, такие как фильтрация по окружению или статусу, легко выносятся в переиспользуемые компоненты без дублирования логики.

  • Самодокументируемость проявляется в том, что благодаря близкому к SQL синтаксису pypika из самого кода сразу понятно, какие данные запрашиваются и по каким условиям.

  • Чистота кода обеспечивается цепочечным стилем, который позволяет выразить даже сложный запрос в одну–две логически цельные строки, избегая вложенных условий, шаблонов и сложных конструкций.

Расширение возможностей assert

В фреймворке расширен механизм проверок в автотестах за счет удобного и выразительного инструмента для написания читаемых, цепочечных и самодокументируемых проверок библиотеки assertpy. Она не заменяет стандартный встроенный в python assert, а дополняет и расширяет возможности проверок, цепочечный синтаксиса позволяет выполнять несколько проверок подряд без дублирования переменных и без потери ясности, библиотека особенно полезна при проверки данных в бизнес-логики теста.

Пример цепочечного синтаксиса:

assert_that(order.total).is_greater_than(0).is_less_than(10000)

Предоставление результатов прогона автотестов

Для документирования тест-кейсов (ТК), планирования прогонов и визуализации результатов выполнения был выбран Allure TestOps. На нем уже были созданы ТК, которые требовали автоматизации.

Чтобы связать автоматизированные тесты с существующими тест-кейсами в Allure и обеспечить стабильную синхронизацию между кодом и документацией, в фреймворк был интегрирован плагин allure-pytest. Этот плагин перехватывает события выполнения тестов в реальном времени и автоматически генерирует структурированные данные, которые после прогона отправляются в Allure TestOps и формируют детализированный отчет.

Запуск подзадач

Отправка данных автотестов в Allure TestOps осуществляется через кастомную invoke-таску. Invoke – это python-библиотека и CLI-утилита для определения и запуска повторяемых задач через декларативный синтаксис.

В отличии от bash-скриптов, invoke позволяет писать задачи на python, использовать аргументы с типизацией, валидацией и подсказками.

Изоляция окружения и запуск автотестов

Для обеспечения воспроизводимости и стабильности результатов все автотесты запускаются в изолированном и идентичном окружении docker-контейнера. Использование контейнеризации гарантирует, что зависимости, версии python, переменные окружения и сетевые настройки остаются одинаковыми независимо от места запуска, локально у инженера или в CI/CD-пайплайне.

Весь процесс выполнения автотестов интегрирован в GitLab CI/CD. Хотя существуют альтернативные CI/CD-платформы (Jenkins, TeamCity, GitHub Actions), выбор пал именно на GitLab CI/CD, поскольку в компании принят GitLab как основная платформа для хранения кода и управления разработкой. Это позволяет использовать встроенный и нативно интегрированный механизм пайплайнов без необходимости разворачивать, настраивать и поддерживать отдельный CI-сервер.

Заключение

Создание собственного проектного фреймворка автотестирования API – это стратегическое решение, направленное на обеспечение масштабируемости, удобства сопровождения и высокой эффективности тестов как средства проверки корректности API. Готовые инструменты вроде Postman отлично подходят для разведочного тестирования и быстрой проверки эндпоинтов, но не отвечают требованиям зрелой автоматизации в условиях CI/CD, командной разработки и сложной логики.

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

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

Комментарии (6)


  1. cupraer
    03.01.2026 17:54

    • Надежность обеспечивается за счет автоматической параметризации всех значений, что полностью исключает риск SQL-инъекций.

    Да, защититься от SQL-ниъекций в собственных тестах — это первоочередная задача.

    2026 год на дворе. Я бы на вашем месте посмотрел, что делают соседи. Property-based тесты затащили в Go (в кастрированном видек под названием fuzzying) лет 8 назад уже. А вы все фейкером данные генерируете.


  1. rpc1
    03.01.2026 17:54

    Для тестирования API часто требуется получать или проверять актуальные боевые данные напрямую из базы данных

    Зачем для терстирования проверять боевые данные? Тестирование, по идее, должно проводиться в тестовой среде. И для E2E тестов не очень хорошо смотреть во внутренности сервиса (например в базу данных), API сервиса лучше тестировать как "черный ящик", для тестирования внутрянки используются другие типы тестов


    1. Jaldsky Автор
      03.01.2026 17:54

      Спасибо за оставленный комментарий! Это первая статья, тщательного ревью, а тем более рецензирование, со стороны кого-либо не проводилось.

      Зачем для терстирования проверять боевые данные

      В данной формулировке я имел ввиду актуальные тестовые данные из stage-контура, ни в коем случае данные с прода.

      Тестирование, по идее, должно проводиться в тестовой среде

      Полностью согласен с проведением тестирования в изолированной тестовой среде, по сути уменьшенной копией прода.

      И для E2E тестов не очень хорошо смотреть...

      В данном проектном фреймворке есть как E2E тесты, так и более скромные контрактные тесты, как раз для контрактных тестов и нужен функционал по получению данных из тестовой БД, например, для проверки что ручка действительно создает нужные данные в БД.


  1. dyadyaSerezha
    03.01.2026 17:54

    Кроме Postman и Bruno смотрели еще что-нибудь? Как-то мало предварительный исследований на тему "что есть сейчас".


    1. Jaldsky Автор
      03.01.2026 17:54

      Кроме Postman и Bruno также посмотрели в сторону Insomnia, Pororoca и Hoppscotch.

      Все эти инструменты не достаточно гибки для наших задач, например, сложно встраивать запуск автотестов в CI или тянуть данные с БД.

      Если интересно почитать об альтернативах Postman и Bruno, то советую ознакомиться со статьей на habr "Отказаться от Postman, перейти на Bruno и жить счастливо"


  1. conopus
    03.01.2026 17:54

    В питоне с этим все хорошо: сразу после этой идет статья про Schematesis. Инструмент живёт и успешно развивается автором.