Перейти к основному содержимому

Интеграционное тестирование

Тестировщику Разработчику Аналитику

Интеграционное тестирование

Что такое интеграционный тест?

Интеграция - это когда две или больше штуковины (модули, сервисы, системы) начинают между собой общаться.

  • модуль А считает налог;
  • модуль Б отправляет данные в налоговую;
  • интеграция - А передаёт результат Б, а Б его принимает.

А и Б могут по отдельности работать, а вместе нет. Например, данные не приходят, что-то не записывается, или искажается.

Интеграционный тест как раз проверка того, что разговор между компонентами работает правильно. Он отвечает на вопрос "Когда А вызвал Б с такими-то данными, то Б ответил как надо и ничего там не сломалось?".

Например, тест проверяет, что POST /api/orders отправляет заказ в CRM, а CRM возвращает подтверждение. Если подтверждение пришло — тест зелёный.

Здесь мы ловим ошибки:

  • формат данных не совпал (один сервис шлёт user_id, другой ждёт userId);
  • порядок вызовов нарушен (вызвали pay до checkCart);
  • сервис упал, а второй не умеет обрабатывать ошибки;
  • токен протух, а обновлять никто не догадался;
  • база вернула null, а код этого не ждал.

Допустим, А система обладает данными, а Б запрашивает данные из первой. Тогда от владельцев системы А требуется обеспечить возможность передавать данные (выполнять запрос в БД по команде, форматировать и отправлять) и получать запросы. Система Б же должна корректно отправлять запросы в систему А по адресу, и ожидать ответ.


Смысл, цели и место в системе обеспечения качества

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

Задача — выявить дефекты, которые принципиально невозможны при тестировании компонентов по отдельности. Такие дефекты часто связаны с несогласованностью контрактов (например, ожидаемый формат даты в одном модуле — ISO 8601, а в другом — dd.MM.yyyy), неявными зависимостями (например, модуль A полагается на побочный эффект выполнения модуля B, не зафиксированный в спецификации), нарушениями порядка вызовов или утечками состояния между интеграционными точками.

Типичные классы ошибок, обнаруживаемых на этом уровне:

  • Несоответствие формата данных между отправителем и получателем (например, JSON-структура содержит поле userId, а потребитель ожидает user_id);
  • Нарушение последовательности вызовов (например, вызов метода finalizeOrder() без предварительного validateCart());
  • Проблемы с сериализацией/десериализацией (например, null в числовом поле при отсутствии обработки в десериализаторе);
  • Некорректная обработка временных зависимостей (например, race condition при одновременном обращении к разделяемому ресурсу);
  • Отсутствие или неправильная реализация механизмов восстановления при сбое (retry, circuit breaker, fallback);
  • Несоблюдение соглашений по HTTP (например, возврат статуса 200 OK при фактической бизнес-ошибке вместо 4xx/5xx).

Интеграционное тестирование применяется как к внутренним связям (между сервисами в микросервисной архитектуре, между слоями в monolith, между классами в рамках одного модуля), так и к внешним — при взаимодействии с third-party API, базами данных, message broker’ами, legacy-системами и прочими внешними зависимостями. Именно здесь проявляется ключевая особенность интеграционных проверок: они требуют фактического выполнения кода в смонтированной конфигурации, пусть даже частичной и с подменой некоторых компонентов (stubs, mocks, test doubles).


Подходы к организации интеграционного тестирования

Существует несколько стратегий построения интеграционных проверок, различающихся по направлению «сборки» системы и уровню изоляции:

Пошаговая (incremental) интеграция

Компоненты подключаются по одному к уже протестированному ядру, и на каждом шаге выполняется проверка новых интерфейсов. Это позволяет быстро локализовать источник сбоя — он почти наверняка связан с последним подключённым модулем. Варианты:

  • Снизу вверх — сначала тестируются низкоуровневые модули (например, DAL, утилиты), затем к ним добавляются более высокоуровневые (бизнес-логика, контроллеры). Преимущество — раннее выявление проблем в фундаментальных слоях. Недостаток — отсутствие «видимого» результата до завершения сборки верхних уровней.
  • Сверху вниз — начинается с высокоуровневых модулей (например, API-контроллеров), при этом нижележащие компоненты заменяются заглушками. Позволяет быстро протестировать end-to-end сценарии, но требует значительных усилий на создание и поддержку заглушек, а также рискует пропустить ошибки в «нижних» слоях до их реальной интеграции.

Big Bang интеграция

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

Sandboxing и интеграционные окружения

Для выполнения интеграционных тестов необходима специализированная среда, воспроизводящая реальные условия взаимодействия, но при этом изолированная от production и dev. Такая среда обычно включает:

  • Test-версии зависимых сервисов (часто в виде контейнеров Docker);
  • Эмуляторы внешних API (например, WireMock, Mountebank);
  • «Чистые» инстансы СУБД с предзаполненными тестовыми данными;
  • Message broker в режиме тестирования (например, embedded Kafka или RabbitMQ в Docker);
  • Сетевые ограничения (latency, packet loss), если это критично для сценариев.

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


Интеграционное тестирование API

В условиях доминирования распределённых систем (REST, gRPC, GraphQL, message-driven) подавляющее большинство интеграционных проверок сводится к тестированию сетевых интерфейсов. API становится контрактом, определяющим синтаксис обмена и семантику поведения. Поэтому интеграционное тестирование API — это комплексная проверка:

Соответствие контракту

Под контрактом понимается формальное или неформальное описание ожидаемого поведения интерфейса. Это может быть:

  • OpenAPI/Swagger-спецификация;
  • Protobuf-файл для gRPC;
  • GraphQL schema;
  • Соглашения внутри команды (например, «все ошибки возвращаются в теле JSON с полем error.code»).

Проверка контракта включает:

  • Корректность HTTP-метода (GET не должен изменять состояние, POST — создавать ресурс и т.д.);
  • Соответствие формата запроса (поля, типы, обязательность, ограничения);
  • Корректность структуры ответа (включая вложенные объекты, массивы, ссылки);
  • Валидность HTTP-статусов (201 Created при создании ресурса, 404 Not Found при отсутствии, 409 Conflict при конфликте идемпотентности);
  • Обязательные и опциональные заголовки (например, ETag, Last-Modified, Content-Type, Retry-After);
  • Поддержка версионирования (через заголовок Accept, параметр URL или path-префикс).

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

Семантическая корректность бизнес-логики

Контракт определяет как, но не что. Здесь проверяется, соответствует ли поведение API предметной области:

  • При создании заказа проверяется, что общая сумма рассчитана верно с учётом скидок и налогов;
  • При запросе истории операций возвращаются только записи, относящиеся к указанному пользователю;
  • Изменение статуса задачи возможно только в рамках допустимых переходов (например, draft → active, но не draft → archived);
  • Повторный вызов идемпотентного запроса (PUT /orders/{id}) не приводит к дублированию данных.

Такие проверки невозможно выполнить на уровне юнит-тестов одного контроллера — они требуют смонтированной цепочки: контроллер → сервис → репозиторий → БД → (возможно) внешний сервис.

Обработка ошибок и граничных условий

Интеграционные тесты должны охватывать сценарии отказа:

  • Передача некорректных данных (отрицательная сумма, строка вместо числа, слишком длинное поле);
  • Обращение к несуществующему ресурсу (неверный ID);
  • Попытка выполнить запрещённую операцию (удалить несвой заказ);
  • Превышение лимитов (rate limiting, размер payload);
  • Таймауты и отказы внешних зависимостей (например, падение payment gateway);
  • Параллельные запросы на один и тот же ресурс (конкурентное обновление).

Особое внимание уделяется предсказуемости поведения при ошибках: ответ должен быть понятен потребителю, не раскрывать внутренние детали реализации (никаких stack trace в теле ответа), и желательно — содержать код ошибки, по которому можно построить логику восстановления.

Производительность и устойчивость

Хотя нагрузочное тестирование — отдельная дисциплина, базовые проверки производительности часто встраиваются в интеграционные сценарии:

  • Время ответа на типичный запрос не превышает SLA (например, < 500 мс для 95-го перцентиля);
  • Система корректно обрабатывает пакетные запросы (bulk operations);
  • Наблюдается стабильность под длительной нагрузкой (отсутствие утечек памяти, роста latency со временем);
  • Реализованы механизмы graceful degradation (например, при падении кэша запрос уходит в БД, но медленнее, а не завершается ошибкой).

Эти проверки особенно важны, когда интеграция происходит между системами с разными SLA — например, high-load frontend и legacy backend с высокой latency.


Инструментарий интеграционного тестирования

Выбор инструмента для интеграционного тестирования определяется техническими возможностями и контекстом его применения: стадией жизненного цикла, уровнем автоматизации, требованиями к воспроизводимости, интеграцией с CI/CD, а также компетенциями команды. На практике редко используется единственный инструмент — чаще формируется стек, где каждый компонент решает свою специфическую задачу.

Условно инструменты можно разделить на три категории, хотя границы между ними размыты.

Интерактивные инструменты

Эти инструменты предназначены в первую очередь для человека: разработчика, тестировщика, аналитика — того, кто проектирует, реализует или проверяет API в процессе работы. Их ключевые качества — наглядность, скорость настройки, поддержка отладки в реальном времени и удобство документирования.

Основные функции:

  • Конструирование HTTP-запросов с визуальным редактором параметров, заголовков, тела;
  • Сохранение и группировка запросов (коллекции, папки);
  • Управление окружениями (dev/stage/prod с разными базовыми URL, токенами, параметрами);
  • Просмотр структурированного ответа (JSON, XML, HTML) с подсветкой синтаксиса и возможностью фильтрации;
  • Встроенная документация (генерация из коллекции);
  • Простые проверки (статус-код, наличие поля в ответе) — часто через GUI.

Недостатки: ограниченная выразительность логики, сложность поддержки сложных сценариев (цепочки запросов с передачей данных между ними), отсутствие контроля версий «из коробки». Однако именно эти инструменты незаменимы на ранних этапах: при проектировании API (Проектирование-first подход), при отладке интеграции «здесь и сейчас», при онбординге новых участников.

Postman — безусловный лидер в этой категории, и далее мы подробно рассмотрим его архитектуру и применение.


Фреймворки для автоматизированного тестирования

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

Характерные черты:

  • Тесты пишутся как часть кодовой базы (обычно в отдельной директории test/integration);
  • Используются стандартные механизмы сборки и запуска (JUnit, pytest, Jest и т.д.);
  • Поддерживают работу с моками и стабами (Testcontainers, WireMock, embedded databases);
  • Интегрируются в отчётность (Allure, JaCoCo, Cobertura);
  • Выполняются в CI/CD-пайплайне (GitHub Actions, GitLab CI, Jenkins) — как отдельный stage или в составе end-to-end проверок.

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

RestAssured (Java), Supertest (Node.js), Karate DSL, pytest + requests + pytest-bdd (Python) — яркие представители этого класса.


Гибридные решения

Эти инструменты пытаются совместить простоту описания (часто в виде конфигурационных файлов или DSL) с возможностями продвинутого автоматизированного тестирования: нагрузки, безопасности, анализа покрытия. Они часто используют YAML/JSON/Gherkin в качестве основного языка описания, но допускают встраивание произвольного кода при необходимости.

Тесты описываются декларативно («что нужно проверить»), а не императивно («как это сделать»). Это упрощает поддержку и позволяет вовлекать в написание тестов аналитиков или QA-инженеров с ограниченными навыками программирования.

Karate и Pact (для контрактного тестирования) — примеры гибридных подходов. SoapUI Pro и ReadyAPI также относятся к этой категории, добавляя GUI-редактор к декларативному ядру.


Postman

Postman изначально представлял собой расширение для Chrome — простой инструмент для отправки HTTP-запросов. Со временем он трансформировался в облачную платформу (Postman API Platform), охватывающую весь жизненный цикл API: проектирование, разработка, тестирование, документирование, мониторинг. Однако его ядро — коллекции и скриптовые хуки — остаётся неизменным и крайне полезным для интеграционного тестирования.

Архитектурные компоненты Postman, релевантные тестированию

Коллекции и папки

Коллекция — это именованная группа запросов, объединённых по смыслу (например, «Auth API», «Orders API»). Внутри коллекции запросы могут быть упорядочены и размещены в папках. Коллекции экспортируются в JSON, что позволяет хранить их в системе контроля версий (хотя нативная поддержка merge-конфликтов в GUI ограничена).

Переменные окружения и глобальные переменные

Postman поддерживает несколько уровней переменных:

  • Environment — набор переменных, привязанных к окружению (например, base_url = https://api.dev.example.com, auth_token = abc123). Переключение окружений — один клик.
  • Collection variables — общие для всей коллекции (например, api_version = v1);
  • Global variables — доступны во всех коллекциях (рекомендуется использовать редко);
  • Local variables (в скриптах) — временные, живут в рамках одного запроса.

Переменные используются в любом поле запроса через синтаксис {{variable_name}}.

Pre-request Script и Tests

Это два JavaScript-хука, выполняемых до отправки запроса и после получения ответа соответственно. Выполняются в изолированном окружении на базе Node.js (но без доступа к require, fs, process — только встроенные утилиты Postman: pm, atob, btoa, JSON, CryptoJS и др.).

  • Pre-request Script используется для:

    • Генерации динамических данных (timestamp, UUID, хэши);
    • Получения токенов авторизации (например, вызов /auth/login и сохранение access_token в переменную);
    • Подготовки тела запроса (например, подпись запроса по алгоритму HMAC-SHA256).
  • Tests — для валидации ответа:

    • Проверка HTTP-статуса: pm.response.to.have.status(200);
    • Проверка заголовков: pm.expect(pm.response.headers.get('Content-Type')).to.include('application/json');
    • Извлечение данных из ответа (JSON, XML) и сохранение в переменные для последующих запросов:
      const jsonData = pm.response.json();
      pm.globals.set('orderId', jsonData.id);
    • Сложные утверждения: проверка схемы JSON через tv4, сравнение значений, валидация форматов.

Библиотека pm (Postman API) предоставляет fluent-интерфейс для написания читаемых проверок.

Newman: CLI-движок для автоматизации

Newman — это утилита командной строки, позволяющая запускать коллекции Postman вне GUI. Это ключевой компонент для интеграции в CI/CD:

newman run TasksAPI.postman_collection.json \
--environment dev.postman_environment.json \
--reporters cli,html \
--reporter-html-export report.html

Newman поддерживает:

  • Параметризацию через CLI-аргументы;
  • Генерацию отчётов (HTML, JSON, JUnit XML);
  • Выполнение в Docker-контейнере;
  • Работу с Newman-специфичными опциями (timeout, delay, iteration Данные).

Таким образом, Postman позволяет реализовать гибридный подход: тесты разрабатываются и отлаживаются в удобном GUI, затем автоматизируются через Newman и встраиваются в пайплайн. Это особенно ценно на ранних этапах (shift-left), когда контракт API ещё не стабилен, и изменения происходят часто — правка в GUI быстрее, чем рефакторинг кода в RestAssured.


Интеграционное тестирование REST API управления задачами

Рассмотрим реалистичный сценарий: разрабатывается сервис Задачи API — простой CRUD для задач (task) с полями id, title, description, status (pending, in_progress, done), createdAt, updatedAt.

Шаг 1. Проектирование контракта (вне Postman, но влияет на тесты)

Согласуем минимальный контракт:

  • GET /Задачи — список задач (пагинация не требуется);
  • GET /Задачи/:id — получение задачи по ID;
  • POST /Задачи — создание задачи (требуются title, description; status по умолчанию pending);
  • PUT /Задачи/:id — полное обновление задачи;
  • PATCH /Задачи/:id — частичное обновление (только переданные поля);
  • DELETE /Задачи/:id — удаление.

Ожидаемые статусы:

  • Успех: 200 OK (GET, PUT, PATCH), 201 Created (POST), 204 No Content (DELETE);
  • Ошибки: 400 Bad Request (некорректные данные), 404 Not Found (ресурс не существует), 405 Method Not Allowed (неподдерживаемый метод), 422 Unprocessable Entity (семантическая ошибка, например, обновление несуществующего поля status на invalid_value).

Шаг 2. Создание коллекции в Postman

  1. Создаём коллекцию Задачи API.
  2. Добавляем папку Setup с запросом GET HealthcheckGET {{base_url}}/health для проверки доступности сервиса.
  3. Добавляем папку CRUD с запросами:
    • Create Task (POST)
    • Get All Задачи (GET)
    • Get Task by ID (GET)
    • Update Task (PUT)
    • Partial Update (PATCH)
    • Delete Task (DELETE)

Шаг 3. Настройка окружения

Создаём окружение Local:

  • base_url = http://localhost:3000/api
  • test_task_id = (оставляем пустым — будет заполнен динамически)

Шаг 4. Написание тестов

Запрос: Create Task

Тело запроса (raw, JSON):

{
"title": "Изучить интеграционное тестирование",
"description": "Написать главу для Вселенной IT"
}

Tests:

// Проверка статуса
pm.test("Status 201 Created", () => {
pm.response.to.have.status(201);
});

// Проверка Content-Type
pm.test("Content-Type is application/json", () => {
pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");
});

// Парсинг ответа
const response = pm.response.json();

// Проверка обязательных полей
pm.test("Response has required fields", () => {
pm.expect(response).to.have.property("id");
pm.expect(response).to.have.property("title", "Изучить интеграционное тестирование");
pm.expect(response).to.have.property("status", "pending");
pm.expect(response).to.have.property("createdAt");
pm.expect(response).to.have.property("updatedAt");
});

// Сохранение ID для последующих запросов
pm.globals.set("created_task_id", response.id);

Запрос: Get Task by ID

URL: {{base_url}}/Задачи/{{created_task_id}}

Tests:

pm.test("Status 200 OK", () => {
pm.response.to.have.status(200);
});

const task = pm.response.json();

pm.test("Task Данные matches created", () => {
pm.expect(task.title).to.eql("Изучить интеграционное тестирование");
pm.expect(task.status).to.eql("pending");
});

// Проверка, что createdAt и updatedAt — валидные ISO-строки
pm.test("Timestamps are valid ISO strings", () => {
pm.expect(new Date(task.createdAt).toString()).to.not.equal("Invalid Date");
pm.expect(new Date(task.updatedAt).toString()).to.not.equal("Invalid Date");
});

Запрос: Partial Update (PATCH)

Тело:

{
"status": "in_progress"
}

Tests:

pm.test("Status 200 OK", () => {
pm.response.to.have.status(200);
});

const updated = pm.response.json();

pm.test("Status updated to in_progress", () => {
pm.expect(updated.status).to.eql("in_progress");
});

// Проверка, что updatedAt изменился, а createdAt — нет
pm.test("updatedAt changed, createdAt preserved", () => {
pm.expect(updated.updatedAt).to.not.eql(updated.createdAt);
// (более строго: сравнить с предыдущим значением, сохранённым в переменную)
});

Запрос: Delete Task

Tests:

pm.test("Status 204 No Content", () => {
pm.response.to.have.status(204);
});

// Дополнительно: попытка получить удалённую задачу → должен быть 404
// (Здесь можно использовать pm.sendRequest для цепочки внутри одного Tests)
pm.sendRequest({
url: `${pm.environment.get('base_url')}/Задачи/${pm.globals.get('created_task_id')}`,
method: 'GET'
}, (err, res) => {
pm.test("GET after DELETE returns 404", () => {
pm.expect(res.code).to.eql(404);
});
});

Тесты на ошибки (отдельные запросы в папке Error Handling)

  • POST /Задачи с пустым title → ожидаем 400 и тело с описанием ошибки;
  • GET /Задачи/invalid-id404;
  • PATCH /Задачи/{{valid_id}} с {"status": "unknown"}422.

Для каждого — аналогичные проверки в Tests.

Шаг 5. Автоматизация через Newman

После локальной отладки коллекцию сохраняют в репозиторий (например, tests/postman/TasksAPI.postman_collection.json), окружение — tests/postman/env/local.json.

В .gitlab-ci.yml (или аналоге):

integration-tests:
stage: test
image: postman/newman:alpine
script:
- newman run tests/postman/TasksAPI.postman_collection.json
--environment tests/postman/env/local.json
--reporters cli,junit
--reporter-junit-export report.xml
artifacts:
reports:
junit: report.xml

Теперь каждый коммит, затрагивающий API, будет проходить интеграционную проверку. При неудаче — сборка падает, и разработчик сразу видит, какой контракт нарушен.


Фреймворки для автоматизированного интеграционного тестирования

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

RestAssured (Java)

RestAssured не является standalone-фреймворком — это библиотека, построенная поверх стандартных HTTP-клиентов (по умолчанию — Apache HttpClient или OkHttp). Её главная идея — позволить писать интеграционные тесты на Java так, будто язык изначально поддерживает HTTP-верификацию.

Архитектурные особенности

  • Fluent DSL: цепочки вызовов читаются как спецификация:
    given()
    .header("Authorization", "Bearer " + token)
    .body(newTask)
    .when()
    .post("/Задачи")
    .then()
    .statusCode(201)
    .header("Location", matchesPattern("/Задачи/\\d+"))
    .body("title", equalTo("Новая задача"))
    .body("status", equalTo("pending"))
    .extract().path("id");
  • Валидация JSON/XML: поддержка JsonPath и XPath «из коробки», встроенные матчеры Hamcrest, возможность подключения JSON Schema Validator.
  • Подготовка состояния: лёгкая интеграция с Testcontainers для поднятия реальной (но изолированной) СУБД, Redis, Kafka перед запуском тестов.
  • Извлечение данных: методы .extract().response(), .extract().path("id") позволяют сохранять значения между запросами без ручного парсинга.

Почему RestAssured — хороший выбор для интеграционного тестирования в Java-мире?

  • Тесты хранятся в том же репозитории, что и продукционный код — рефакторинг API (например, изменение пути эндпоинта) сразу ломает тесты, что сигнализирует о рассогласовании.
  • Полная типобезопасность при работе с DTO (если использовать ObjectMapper для десериализации тела ответа).
  • Поддержка всех этапов пайплайна: локальная отладка в IDE, запуск через Maven/Gradle, интеграция в JaCoCo для анализа покрытия интеграционных сценариев (не путать с unit-покрытием).
  • Возможность параметризации через JUnit 5 (@ParameterizedTest, @CsvSource).

Ограничения

  • Зависимость от JVM-стека (не подходит для polyglot-проектов);
  • Сложность работы с асинхронными интерфейсами (например, SSE, WebSockets) без дополнительных обёрток;
  • Не предназначен для нагрузочного тестирования — только функциональная проверка.

Supertest (Node.js)

Supertest специализируется на интеграционном тестировании HTTP-серверов, написанных на Node.js (в первую очередь — Express, но также Fastify, Koa и др.). Его ключевая особенность — прямое подключение к серверному экземпляру без запуска сетевого сокета. Это даёт два критических преимущества:

  1. Скорость: отсутствует накладная сетевая задержка, DNS-поиск, TLS-хендшейк;
  2. Изоляция: тесты не конфликтуют с другими процессами, использующими тот же порт.

Как это работает

Вместо того чтобы отправлять запрос через fetch или axios на http://localhost:3000, Supertest оборачивает Express-приложение в «виртуальный агент»:

const request = require('supertest');
const app = require('../src/app'); // ваше Express-приложение

describe('Задачи API', () => {
it('creates a task with valid Данные', async () => {
const res = await request(app)
.post('/api/Задачи')
.send({
title: 'Тестирование через Supertest',
description: 'Без запуска сервера'
})
.expect(201);

expect(res.body).toMatchObject({
id: expect.any(Number),
title: 'Тестирование через Supertest',
status: 'pending'
});
});
});

Здесь app — ссылка на объект Express. Запрос проходит через middleware, роутеры, контроллеры, но останавливается до уровня listen().

Применение в интеграционном тестировании

  • Проверка middleware (аутентификация, логирование, валидация);
  • Тестирование обработчиков ошибок (например, что next(new AppError('NOT_FOUND')) возвращает 404);
  • Интеграция с in-memory БД (например, SQLite в режиме :memory:) или mocked-репозиториями;
  • Проверка заголовков, кук, CORS-политик.

Когда Supertest не подходит

  • При тестировании взаимодействия с внешними сервисами (например, шлюзом API, обратным прокси);
  • При необходимости проверки TLS-настроек, keep-alive, HTTP/2;
  • При работе с gRPC или GraphQL (требуются специализированные клиенты).

Supertest — инструмент для внутренней интеграционной проверки. Для end-to-end тестирования того же API через реальный сетевой стек (например, через Nginx) потребуется дополнять его axios или fetch.


Karate DSL

Karate — наиболее нестандартный инструмент в списке. Он реализует идею: тест — это спецификация, а не программа. Сценарии пишутся на упрощённом диалекте Gherkin, но без необходимости реализовывать step definitions на Java — логика встроена в DSL.

Пример сценария для Задачи API

Feature: Задачи API integration

Background:
* url 'http://localhost:3000/api'
* def newTask = { title: 'Karate-тест', description: 'Декларативный подход' }

Scenario: Create and retrieve a task
Given request newTask
When method post
Then status 201
And match response ==
"""
{
id: '#number',
title: 'Karate-тест',
status: 'pending',
createdAt: '#regex \\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{3}Z',
updatedAt: '#? _ == response.createdAt'
}
"""
* def taskId = response.id

Given path 'Задачи', taskId
When method get
Then status 200
And match response.title == 'Karate-тест'

Сильные стороны Karate в контексте интеграционного тестирования

  • Самодокументируемость: сценарий читается как техническое требование;
  • Встроенные возможности:
    • JSON/XML validation (через match и #-шаблоны);
    • Поддержка multipart, form-Данные, binary payloads;
    • Генерация отчётов в формате Cucumber (HTML);
    • Встроенная поддержка параллелизма (karate.parallel());
    • Работа с SOAP, GraphQL, gRPC, Kafka, JDBC «из коробки».
  • Независимость от языка приложения: сценарии не привязаны к Java — их может писать QA-инженер без глубоких знаний JVM.

Ограничения и нюансы

  • Уход от привычной парадигмы кода: отладка сложных условий (например, циклов, рекурсии) затруднена;
  • Меньше контроля над окружением по сравнению с RestAssured (например, сложнее интегрировать Testcontainers);
  • Обучение требует переосмысления подхода к тестированию.

Karate особенно эффективен при:

  • Развитой практике BDD в команде;
  • Необходимости вовлечения нетехнических стейкхолдеров в написание проверок;
  • Тестировании legacy-систем с нестабильными или плохо документированными API.

SoapUI / ReadyAPI

SoapUI — один из старейших инструментов, изначально созданный для тестирования SOAP-сервисов (отсюда название). Сегодня он поддерживает REST, GraphQL, JDBC, JMS и даже gRPC, но его архитектура остаётся ориентированной на XML-центричность и визуальное проектирование.

Как SoapUI применяется в интеграционном тестировании

  • Импорт WSDL/XSD: автоматическая генерация запросов и схем валидации для SOAP;
  • Тестовые сюиты как дерево: каждый тест — это последовательность шагов (HTTP-запрос, Groovy-скрипт, условный переход, цикл), визуально представленная в иерархии;
  • Данные-Driven Тестирование: параметризация через внешние файлы (Excel, CSV, базы данных);
  • Property Transfer: передача значений между шагами через XPath/JsonPath (например, извлечь //orderId из ответа и подставить в следующий запрос).

Почему SoapUI всё ещё актуален

  • В госсекторе и enterprise-средах до сих пор широко используются SOAP-интерфейсы — SoapUI остаётся de facto стандартом для их верификации;
  • Готовые шаблоны для Безопасность-тестов (WS-Безопасность, OAuth 1.0), compliance-проверок (HIPAA, PCI DSS);
  • Поддержка MTOM для передачи бинарных вложений в SOAP.

Ограничения для современных REST-проектов

  • Groovy-скрипты в SoapUI — мощный, но малопопулярный язык: сложность поддержки командой;
  • Отсутствие нативной интеграции с Git (файлы проекта — бинарные .xml, конфликты при merge);
  • Высокая стоимость ReadyAPI (коммерческая версия) для расширенных функций (load Тестирование, mocking).

SoapUI оправдан при работе в смешанных средах (SOAP + REST) или при строгих требованиях к traceability (каждый шаг теста имеет идентификатор, привязанный к требованиям).


Apache JMeter

JMeter изначально проектировался как инструмент для стресс- и нагрузочного тестирования. Однако его гибкая архитектура позволяет использовать его и для функциональной верификации API — особенно в случаях, где:

  • Требуется одновременно проверить корректность и производительность;
  • Нет возможности внедрить кодовые тесты (например, black-box тестирование стороннего сервиса);
  • Необходима визуальная сборка сложных workflow (ветвления, циклы, логика извлечения данных).

Как JMeter применяется в интеграционном тестировании

  • HTTP Request Sampler: отправка GET/POST/PUT-запросов;
  • Post-Processors:
    • JSON Extractor — извлечение значений по JsonPath ($.id);
    • Regular Expression Extractor — для XML/HTML;
    • XPath2 Extractor — для строгой XML-валидации.
  • Assertions:
    • Response Assertion — проверка текста, кода, заголовков;
    • JSON Assertion — валидация по JSON Schema (требует плагина);
    • Duration Assertion — проверка времени ответа.
  • Logic Controllers:
    • If Controller — условное выполнение;
    • While Controller — циклы ожидания (например, polling статуса задачи);
    • Module Controller — повторное использование частей сценария.

Преимущества JMeter для функциональных проверок

  • Независимость от стека: запускается везде, где есть Java;
  • Поддержка протоколов помимо HTTP: FTP, JDBC, JMS, SMTP, LDAP;
  • Готовые отчёты: Aggregate Report, Response Times Over Time, etc.

Почему JMeter — не основной инструмент для интеграционного тестирования

  • Отсутствие типобезопасности: ошибки в JsonPath/XPath выявляются только при запуске;
  • Сложность отладки: нет пошагового выполнения, breakpoints, watch-выражений;
  • Проблемы с версионированием: .jmx-файлы — XML без форматирования, merge-конфликты трудно разрешать;
  • Избыточность: для простых CRUD-сценариев интерфейс JMeter излишне громоздок.

JMeter целесообразно использовать в двух случаях:

  1. Когда функциональные и нагрузочные проверки логически связаны (например, «проверить, что при 100 RPS API не возвращает 5xx»);
  2. Для black-box интеграционного тестирования, когда исходный код недоступен, а контракт — единственный ориентир.

См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).