lenec ru

← все посты

Тестирование API: pytest+httpx vs Postman, и где Postman всё-таки полезен

18K

Спор «Postman или код» в API-тестах не утихает с тех пор, как вышла первая версия Postman. Менеджер хочет видеть зелёные галочки в красивом интерфейсе, разработчик хочет писать тесты в репозитории, тестировщик посередине пытается совместить и то и другое. После семи лет в QA у меня сложилась устойчивая картина, в каких задачах работает каждый инструмент, и где они мешают друг другу.

Сразу к делу: pytest+httpx и Postman — не конкуренты. У них разные роли. Проблема в том, что команды часто выбирают один из двух, а потом удивляются, почему болит то спина, то поясница.

Что даёт pytest+httpx

Это полноценный тестовый стек на Python. Собирается на коленке за час:

pip install pytest httpx pytest-asyncio

Минимальный тест на REST-эндпоинт:

import httpx
import pytest

BASE_URL = "https://api.staging.example.com"

@pytest.fixture
def client():
    with httpx.Client(base_url=BASE_URL, timeout=10.0) as c:
        yield c

def test_health(client):
    response = client.get("/health")
    assert response.status_code == 200
    assert response.json()["status"] == "ok"

Дальше всё, что есть в Python: фикстуры, параметризация, маркеры, кастомные ассерты, моки. Тесты живут в репозитории, идут в pre-merge, отчёт собирается в Allure или JUnit XML, поверх ставится coverage.py для серверного кода (когда есть доступ).

Где pytest+httpx сильнее всего

  • Сложная подготовка данных. Сценарий «создать пользователя, выдать токен, оформить заказ, вернуть его, проверить событие в Kafka» в коде разворачивается в десяток фикстур. В Postman это превращается в гирлянду из pre-request scripts.
  • Параметризация. Одна функция, тридцать наборов данных, отдельные репорты по каждому. pytest.mark.parametrize с ids=... — и тебе видно, какой именно кейс упал.
  • Сетевая инфраструктура. httpx умеет async, прокси, кастомные транспорты, mTLS, http/2. Если нужно проверить gRPC или WebSocket рядом с REST — это всё в Python давно есть.
  • Ассерты структуры. jsonschema, pydantic, deepdiff — выбирай. Можно проверять не только значения, но и форму ответа целиком.
  • Тесты живут с продуктом. Они в Git, они в CI, они проходят ревью. Postman-коллекции тоже можно класть в репу, но это всегда «второй сорт» в глазах разработчиков.

Параметризация на практике

import pytest

@pytest.mark.parametrize(
    "payload, expected_status, expected_field",
    [
        ({"email": "ok@example.com", "password": "Hunter2!"}, 200, "token"),
        ({"email": "ok@example.com"}, 422, "detail"),
        ({"email": "not-email", "password": "Hunter2!"}, 422, "detail"),
        ({"email": "", "password": ""}, 422, "detail"),
    ],
    ids=["happy", "no-password", "bad-email", "empty"],
)
def test_signin(client, payload, expected_status, expected_field):
    response = client.post("/api/auth/signin", json=payload)
    assert response.status_code == expected_status
    assert expected_field in response.json()

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

Где Postman всё-таки полезен

Уйти от Postman целиком — соблазнительно, но не работает в реальных командах. Он закрывает задачи, которые на чистом коде делать долго и неудобно.

Исследование незнакомого API

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

Демонстрация и обмен

Аналитик прислал тебе коллекцию с примерами запросов на новый сервис. Бекендер скинул postman-коллекцию рядом с PR. На код-ревью это работает быстрее, чем «склонируй ветку, поставь зависимости, запусти конкретный тест». Коллекция — это документация, которую можно нажать.

Мокировщик и среды

Postman неплохо умеет mock servers: ставишь URL мока вместо реального бекенда и тестируешь фронт. На раннем этапе разработки это удобнее, чем поднимать свой WireMock. Хотя в зрелой команде всё-таки уезжаешь на свой mock-сервер в инфраструктуре.

Smoke-проверки от QA-руководителя

Сценарий: на проде сломалось, надо быстро прогнать пять ключевых эндпоинтов. Newman (CLI Postman) запускает коллекцию из консоли, отдаёт JUnit-репорт. Это удобнее, чем собирать pytest-окружение на ноуте, где Python другой версии. Ручник в команде поднимает Postman быстрее, чем разбирается с venv.

Что не зашло, когда пыталась смешивать

  • Postman как основной CI-стек. Ставила Newman в пайплайн, складывала коллекции в репу, гоняла на каждый PR. Через полгода поддержка превратилась в адскую работу: рефакторинг pre-request скриптов вручную, отсутствие нормального тайпчека, сложно отлаживать падения.
  • Тесты-«перевёртыши» в Postman. Это когда в скрипте теста накатываешь сложную логику с ветвлениями. JS внутри Postman не дебажится нормально. Через месяц никто, кроме автора, не понимает, что там происходит.
  • Глобальные переменные окружения как state. Прохождение коллекции зависит от порядка. Один запрос правит global, второй на него полагается. На CI флакает: запросы могут идти параллельно, переменные перетираются.
  • Postman вместо контрактных тестов. Coллекция фиксирует «как было», но не «как должно быть по контракту». Контрактные тесты у меня живут отдельно — Pact или ручная проверка OpenAPI-схемы через jsonschema.

Связка, которая работает

Делю задачи так:

  • Pytest+httpx — обязательный CI-набор. Все основные сценарии, негативные кейсы, проверка контрактов. Прогоняется на каждый PR в бекенд-репозитории, блокирует merge при падении.
  • Postman-коллекции — живая документация и smoke-набор. Хранятся в отдельной папке репы, обновляются вместе с релизом. Гонятся вручную при ресёрче и автоматически на staging после деплоя.
  • k6 — нагрузка. Не пересекается с двумя предыдущими.

Между этими тремя инструментами тестов гораздо меньше дублирования, чем кажется. Каждый отвечает за свой угол.

Конкретный пайплайн

jobs:
  api-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install -r tests/requirements.txt
      - run: pytest tests/api -v --junitxml=report.xml
      - uses: actions/upload-artifact@v4
        with:
          name: api-report
          path: report.xml

  smoke-postman:
    needs: api-tests
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - run: npm install -g newman
      - run: newman run postman/smoke.json -e postman/staging.env.json --reporters cli,junit

Pytest — всегда. Postman — только на основной ветке после деплоя. На PR коллекция не запускается: она про документацию, а не про регресс.

Что попробовать

  1. Если у тебя сейчас весь API-регресс в Postman — попробуй переписать самые часто падающие 10–20 кейсов на pytest+httpx. Сравни время разбора падений через месяц.
  2. Если у тебя только pytest — заведи Postman-коллекцию для smoke и для нового API в исследовательской фазе. Не пытайся сделать её основным регрессом.
  3. На сложных интеграциях (платёжки, SSO, внешние сервисы) считай контрактные тесты обязательными отдельно от обоих.

Финальный аргумент: Postman — это инструмент для людей, pytest — для пайплайна. Когда выбираешь, кто это запустит — человек руками или CI без участия людей — выбор очевиден.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.