Когда разработчик говорит «я написал API», это ещё не значит, что он работает. Точнее, работает — но как именно? Что вернёт эндпоинт при некорректных данных? Выдержит ли нагрузку? Не сломается ли авторизация?

Тестирование API — это не просто «запустил, получил 200, всё ок». Это системная проверка того, что сервис ведёт себя предсказуемо в любых условиях. Разберём, чем тестировать, как структурировать проверки и на что смотреть в первую очередь.

Зачем вообще тестировать API отдельно

UI-тесты не заменяют API-тесты. Если вы кликаете по кнопке в браузере и проверяете, что данные появились на странице — вы тестируете стек целиком. Это полезно, но медленно и нестабильно.

API-тесты работают на уровне HTTP-запросов: напрямую, без браузера, без фронтенда. Они быстрее, стабильнее и точнее говорят, где именно сломалось.

Кроме того, многие API живут отдельно от UI — это внутренние сервисы, микросервисы, интеграции. Там вообще нет фронтенда, который можно потыкать.

curl — минимальный инструмент, который всегда под рукой

Прежде чем открывать Postman, стоит знать curl. Это консольная утилита, которая есть в любой Unix-системе и доступна на Windows. Она отправляет HTTP-запросы и показывает ответ.

Простой GET-запрос:

curl https://api.example.com/users

POST с JSON-телом и заголовком авторизации:

curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGc..." \
  -d '{"name": "Ivan", "email": "ivan@example.com"}'

Флаг -v покажет полные заголовки запроса и ответа — удобно для отладки. -I вернёт только заголовки без тела.

curl — это инструмент для быстрой проверки «а вообще что-то отвечает?». Для систематического тестирования нужно что-то удобнее.

Postman — стандарт индустрии

Postman — графический инструмент для работы с API. Бесплатная версия покрывает большинство задач.

Что умеет Postman:

  • Сохранять запросы в коллекции и организовывать их по папкам
  • Использовать переменные окружения (dev/staging/prod с разными базовыми URL)
  • Писать тесты на JavaScript прямо внутри запроса
  • Запускать коллекцию как набор тестов через Collection Runner
  • Генерировать документацию по коллекции

Пример теста в Postman — вкладка Tests:

pm.test("Статус 200", function () {
  pm.response.to.have.status(200);
});

pm.test("Ответ — массив", function () {
  const body = pm.response.json();
  pm.expect(body).to.be.an('array');
});

pm.test("Первый пользователь имеет email", function () {
  const body = pm.response.json();
  pm.expect(body[0]).to.have.property('email');
});

Эти тесты запускаются после каждого запроса и показывают, прошли они или нет. Collection Runner запускает всю коллекцию последовательно — это уже похоже на настоящий тест-сьют.

Postman также поддерживает pre-request скрипты — код, который выполняется перед запросом. Удобно для получения токена перед авторизованными запросами.

Insomnia — альтернатива для тех, кому Postman тяжеловат

Insomnia — более лёгкий и быстрый инструмент. Интерфейс проще, меньше функций, но для большинства задач хватает. Хорошо работает с GraphQL и gRPC, где у Postman исторически были проблемы.

Если вы работаете с REST и вам нужна простая коллекция запросов — выбор между Postman и Insomnia это вопрос вкуса.

HTTPie — curl для людей

HTTPie — консольный инструмент, синтаксис которого значительно удобнее curl:

http POST api.example.com/users name=Ivan email=ivan@example.com Authorization:"Bearer token123"

Без кавычек вокруг JSON, без -H для каждого заголовка, с подсветкой синтаксиса в выводе. Если вы часто работаете в терминале, HTTPie ускоряет рутину.

Автоматизация: когда ручных запросов уже недостаточно

Ручное тестирование хорошо для отладки, но оно не масштабируется. Нельзя каждый деплой проверять руками — это медленно и ненадёжно.

Для автоматизации API-тестов чаще всего используют:

Jest + Supertest (Node.js). Supertest — библиотека для тестирования HTTP-сервисов. Работает поверх Jest или Mocha.

const request = require('supertest');
const app = require('../app');

describe('GET /users', () => {
  it('возвращает список пользователей', async () => {
    const res = await request(app).get('/users');
    expect(res.statusCode).toBe(200);
    expect(Array.isArray(res.body)).toBe(true);
  });

  it('возвращает 401 без токена', async () => {
    const res = await request(app).get('/users/me');
    expect(res.statusCode).toBe(401);
  });
});

Pytest + httpx (Python). httpx — асинхронный HTTP-клиент с удобным API для тестирования.

import httpx

def test_get_users():
    response = httpx.get('https://api.example.com/users')
    assert response.status_code == 200
    assert isinstance(response.json(), list)

Newman — CLI-runner для Postman-коллекций. Позволяет запускать коллекцию в CI/CD:

newman run collection.json -e environment.json --reporters cli,junit

JUnit-репорт понимают большинство CI-систем — GitLab CI, GitHub Actions, Jenkins.

Что именно проверять

Новички часто ограничиваются проверкой статус-кода. Это необходимо, но недостаточно. Вот что стоит проверять:

Статус-коды. Не только 200, но и граничные случаи: 400 при невалидных данных, 401 без токена, 403 при недостатке прав, 404 для несуществующего ресурса, 422 при нарушении бизнес-логики.

Структура ответа. Есть ли нужные поля? Правильные ли типы данных? Не пришёл ли null вместо массива?

Содержимое данных. Создали пользователя — вернулся ли он с правильным email? Обновили поле — отразилось ли изменение?

Заголовки. Content-Type должен быть application/json для JSON-API. Если есть кеширование — Cache-Control. Для CORS — нужные заголовки при preflight-запросе.

Время ответа. Медленный API — плохой API. Если эндпоинт отвечает дольше 500 мс при нормальной нагрузке, это повод разобраться.

Пагинация. Если API возвращает списки — проверяйте пагинацию: корректные meta.total, правильная навигация по страницам, работа с limit/offset или cursor.

Нагрузочное тестирование: выдержит ли API реальную нагрузку

Функциональные тесты проверяют корректность. Нагрузочные — производительность под стрессом.

k6 — современный инструмент для нагрузочного тестирования, скрипты на JavaScript:

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 100,        // 100 виртуальных пользователей
  duration: '30s', // 30 секунд теста
};

export default function () {
  const res = http.get('https://api.example.com/users');
  check(res, {
    'статус 200': (r) => r.status === 200,
    'быстрее 500ms': (r) => r.timings.duration < 500,
  });
  sleep(1);
}

k6 покажет p95, p99 времени ответа, количество ошибок, RPS. Запускать его стоит не в продакшн, а на стейджинге.

Apache Benchmark (ab) — проще, встроен в большинство систем:

ab -n 1000 -c 50 https://api.example.com/users

1000 запросов, 50 одновременных соединений. Быстрый способ понять, как сервер справляется с нагрузкой.

Тестирование авторизации и безопасности

Это отдельная область, которую часто игнорируют. Минимальный чек-лист:

  • Запрос без токена возвращает 401
  • Токен другого пользователя не даёт доступ к чужим данным (проверяйте IDOR — Insecure Direct Object Reference)
  • Истёкший токен отклоняется
  • SQL-инъекции в параметрах: передайте ' OR 1=1 -- в строковое поле и убедитесь, что API не ломается
  • Слишком длинные строки не роняют сервер
  • Вложенные объекты большой глубины не вызывают переполнение стека

Для автоматизации безопасного тестирования используют OWASP ZAP или Burp Suite, но это уже отдельная тема.

Contract testing: когда микросервисов много

Если у вас монолит с одним API — достаточно функциональных тестов. Но если несколько команд разрабатывают микросервисы, которые общаются друг с другом, возникает проблема: команда А изменила формат ответа, команда Б об этом не знала — всё сломалось.

Contract testing решает это: каждый сервис фиксирует «контракт» — что он ожидает получить и что обязуется вернуть. При изменении контракта тесты падают до того, как изменение попадёт в продакшн.

Pact — самый популярный инструмент для contract testing. Поддерживает JavaScript, Python, Go, Java.

Для большинства небольших проектов contract testing избыточен. Он нужен, когда у вас 5+ микросервисов и несколько команд.

Как встроить тесты в CI/CD

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

  1. Функциональные тесты запускаются при каждом пуше в ветку
  2. Нагрузочные тесты — по расписанию или перед релизом
  3. Упавшие тесты блокируют мёрж в main

Пример для GitHub Actions:

name: API Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm install
      - run: npm test

Для Newman:

- name: Run API tests
  run: newman run collection.json -e env.json

Мониторинг API в продакшне

Тесты проверяют API до деплоя. Мониторинг — после. Это разные задачи.

Синтетический мониторинг — запускает реальные HTTP-запросы к продакшн-API по расписанию и алертит, если что-то пошло не так. Инструменты: Checkly, UptimeRobot, Grafana с HTTP-зондами.

АПИ может работать, но медленно. Настройте алерты не только на ошибки, но и на деградацию времени ответа.

Как организовать тесты в проекте

Несколько правил, которые упрощают жизнь:

Один тест — одна проверка. Не мешайте в один тест проверку статуса, структуры и бизнес-логики. Если что-то падает, сразу понятно, что именно.

Независимые тесты. Тест не должен зависеть от результата предыдущего. Если нужен созданный пользователь — создайте его в setup, удалите в teardown.

Реалистичные данные. Используйте фикстуры, похожие на боевые данные. Тест с name: "test" и email: "a@b.c" менее надёжен, чем с полноценными данными.

Покрывайте негативные сценарии. 80% багов в API — это граничные случаи и некорректный ввод. Тестируйте не только «счастливый путь».


Если вы заказываете разработку API — убедитесь, что подрядчик поставляет тесты вместе с кодом. В REEXY (r3xy.ru) интеграция сервисов и разработка API включают проверку ключевых сценариев — так проект приходит с доказательством, что он работает, а не просто выглядит работающим.

Начать можно просто: curl или Postman, несколько ручных запросов, понимание структуры ответов. Потом — тесты на Jest или Pytest, запуск в CI. Нагрузка и безопасность — следующий уровень, когда базовое покрытие есть.

Главное: тест, написанный сегодня, завтра поймает регрессию, на которую иначе потратите час отладки в продакшне.