Newman и pytest для автоматизации API
Updated Jul 2026
Postman отлично подходит для исследования. Для интеграции с CI/CD и масштабируемых тестовых наборов нужны инструменты командной строки: Newman для запуска коллекций Postman и pytest с requests для полного программного контроля.
Newman: коллекции Postman в CI
Newman запускает коллекции Postman из командной строки, что делает их подходящими для CI-пайплайнов.
Базовое использование
# Run a collection with an environment
newman run collection.json --environment staging.json --reporters cli,junit
# Run with specific folder
newman run collection.json --folder "Auth" --environment staging.json
# Run with data file (data-driven testing)
newman run collection.json --iteration-data test_data.csv --reporters cli,htmlextra
# Set environment variables from command line
newman run collection.json -e staging.json --env-var "auth_token=abc123"
Newman в GitHub Actions
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm install -g newman newman-reporter-htmlextra
- run: |
newman run tests/collection.json \
--environment tests/staging.json \
--reporters cli,junit,htmlextra \
--reporter-junit-export results.xml \
--reporter-htmlextra-export report.html
- uses: actions/upload-artifact@v4
if: always()
with:
name: api-test-report
path: |
results.xml
report.html
pytest + requests: полный контроль
Для продакшен-наборов API-тестов pytest с библиотекой requests обеспечивает гибкость, поддерживаемость и мощь, которых не хватает Newman.
Структура проекта
tests/
api/
conftest.py # Shared fixtures
test_auth.py # Authentication tests
test_users.py # User CRUD tests
test_orders.py # Order tests
test_error_handling.py
data/
test_users.csv # Test data files
pytest.ini # Configuration
Общие фикстуры (conftest.py)
import os
import pytest
import requests
@pytest.fixture(scope="session")
def base_url():
return os.environ.get("API_BASE_URL", "http://localhost:3000/api/v1")
@pytest.fixture(scope="session")
def auth_headers(base_url):
"""Authenticate once per test session."""
r = requests.post(f"{base_url}/auth/login", json={
"email": "test@example.com",
"password": "testpass123"
})
assert r.status_code == 200, f"Auth failed: {r.text}"
token = r.json()["access_token"]
return {"Authorization": f"Bearer {token}"}
@pytest.fixture
def api(base_url, auth_headers):
"""Pre-configured API session."""
session = requests.Session()
session.headers.update(auth_headers)
session.headers.update({"Content-Type": "application/json"})
# Store base_url on session for convenience
session._base_url = base_url
original_request = session.request
def patched_request(method, url, **kwargs):
if url.startswith("/"):
url = f"{base_url}{url}"
return original_request(method, url, **kwargs)
session.request = patched_request
return session
@pytest.fixture
def create_user(api):
"""Factory fixture for creating test users with cleanup."""
created_ids = []
def _create(name="Test User", email=None, role="viewer"):
import uuid
email = email or f"test-{uuid.uuid4().hex[:8]}@test.com"
r = api.post("/users", json={"name": name, "email": email, "role": role})
assert r.status_code == 201
user = r.json()
created_ids.append(user["id"])
return user
yield _create
# Cleanup: delete all created users
for uid in created_ids:
api.delete(f"/users/{uid}")
Примеры тестов
# test_users.py
def test_create_user(api, create_user):
user = create_user(name="Alice", role="admin")
assert user["name"] == "Alice"
assert user["role"] == "admin"
assert "id" in user
def test_get_user(api, create_user):
user = create_user(name="Bob")
r = api.get(f"/users/{user['id']}")
assert r.status_code == 200
assert r.json()["name"] == "Bob"
def test_update_user(api, create_user):
user = create_user(name="Original")
r = api.put(f"/users/{user['id']}", json={
"name": "Updated",
"email": user["email"],
"role": user["role"]
})
assert r.status_code == 200
assert r.json()["name"] == "Updated"
def test_delete_user(api, create_user):
user = create_user()
r = api.delete(f"/users/{user['id']}")
assert r.status_code == 204
# Verify deletion
r = api.get(f"/users/{user['id']}")
assert r.status_code == 404
@pytest.mark.parametrize("email,expected", [
("valid@test.com", 201),
("", 400),
("not-an-email", 422),
("a" * 300 + "@test.com", 422),
])
def test_create_user_email_validation(api, email, expected):
r = api.post("/users", json={"name": "Test", "email": email, "role": "viewer"})
assert r.status_code == expected
Newman vs pytest: когда что использовать
| Аспект | Newman | pytest + requests |
|---|---|---|
| Кривая обучения | Низкая (если используете Postman) | Средняя (требуется Python) |
| Гибкость | Ограничена скриптингом Postman | Полная экосистема Python |
| Тестирование на основе данных | CSV/JSON файлы данных | pytest parametrize, фикстуры |
| Поддержка при масштабировании | Коллекции становятся неуправляемыми | Стандартный код с модулями и импортами |
| Отладка | Консоль Postman | Python debugger, логирование |
| Переиспользуемые утилиты | Ограниченные | Полный Python: пользовательские утверждения, хелперы, библиотеки |
| Контроль версий | JSON-экспорт (сложно сравнивать) | Python-файлы (легко сравнивать и ревьюить) |
| Командная работа | Рабочие области Postman | Git (стандартный код-ревью) |
Гибридный подход
Многие команды используют оба инструмента:
- Postman + Newman для smoke-тестов и быстрых проверок эндпоинтов
- pytest для комплексных тестовых наборов со сложной логикой, подготовкой данных и очисткой
Запуск тестов в CI
# Run all API tests
API_BASE_URL=https://api.staging.example.com pytest tests/api/ -v --junitxml=results.xml
# Run only smoke tests
API_BASE_URL=https://api.staging.example.com pytest tests/api/ -m smoke -v
# Run with parallel execution
API_BASE_URL=https://api.staging.example.com pytest tests/api/ -n 4 -v
# Run against production (read-only tests only)
API_BASE_URL=https://api.example.com pytest tests/api/ -m "readonly" -v
Практическое упражнение
- Создайте проект pytest с conftest.py, содержащим фикстуры base_url, auth и API-сессии
- Напишите CRUD-тесты для ресурса (создание, чтение, обновление, удаление)
- Добавьте параметризованные тесты валидации для хотя бы одного эндпоинта
- Добавьте фабричную фикстуру, создающую тестовые данные и очищающую их после теста
- Запустите набор тестов против локального или публичного API
Ключевые выводы
- Newman запускает коллекции Postman в CI — быстро настраивается, ограничен в гибкости
- pytest + requests обеспечивает полный программный контроль для продакшен-тестовых наборов
- Используйте фикстуры для общей настройки: авторизация, API-сессии, фабрики тестовых данных
- Фабричные фикстуры с очисткой предотвращают накопление тестовых данных
- Параметризуйте для тестирования на основе данных вместо дублирования тестовых функций
- Гибридный подход (Postman для исследования, pytest для CI) хорошо работает для многих команд