Lesson 5 of 26

Урок 05 — Тестирование API с Postman и SoapUI. GET vs POST

Название: Инструменты QA-инженера для тестирования API: Postman и SoapUI
Описание: Изучаем работу с Postman для тестирования REST API и SoapUI для SOAP-сервисов. Разбираем подробно разницу между GET и POST, учимся писать автотесты для API и организовывать коллекции запросов.
Почему это важно для QA: Postman — это один из первых инструментов, который осваивает QA-инженер. Без умения тестировать API невозможно работать в современных командах. Понимание разницы между GET и POST — обязательное знание для любого технического интервью.


1. Зачем тестировать API отдельно от UI

Когда пользователь нажимает кнопку «Сохранить», происходит следующее:

Пользователь → UI (форма) → JavaScript → HTTP-запрос → API → База данных

Можно ли тестировать только через UI? Технически — да. Но это неэффективно:

СравнениеUI-тестированиеAPI-тестирование
СкоростьМедленно (загрузка страниц)Быстро (только запрос/ответ)
СтабильностьЗависит от версткиСтабильнее
Что проверяетИнтерфейс + логикаТолько логика
Когда запускатьБлиже к релизуС первого дня
Граничные значенияСложно воспроизвестиЛегко

Главный аргумент: API готово раньше UI

В Agile-командах бэкенд часто готов до того, как UI реализован. QA может начать тестировать бизнес-логику через API, не дожидаясь фронтенда.

Спринт 1: Разработчик написал API /api/orders
           QA: уже тестирует логику создания заказов через Postman

Спринт 2: Дизайнер сделал форму заказа
           QA: дополнительно тестирует UI

2. Postman — главный инструмент для REST

Postman — это графическое приложение для работы с HTTP-запросами. Позволяет:

  • Отправлять запросы к любому API
  • Организовывать запросы в коллекции
  • Писать автоматические проверки (тесты)
  • Управлять переменными окружения
  • Запускать коллекции в CI/CD

Установка и первый запрос

  1. Скачай Postman: postman.com/downloads
  2. Создай аккаунт (бесплатно)
  3. Нажми «New» → «HTTP»

Анатомия запроса в Postman

ОбластьСодержимое
Строка запросаМетод [GET ▼], URL, кнопка [Send]
ParamsQuery-параметры: Key: id, Value: 1
HeadersContent-Type: application/json
BodyДля GET — пустое
AuthorizationBearer Token: eyJhbGci...
TestsJavaScript-код для автопроверок
ОтветBody, Cookies, Headers, Test Results
Статус ответа200 OK, Time: 124 ms, Size: 508 B

Пример тела ответа:

jsonjson
{
  "id": 1,
  "name": "Leanne Graham",
  "email": "Sincere@april.biz"
}

3. GET vs POST — подробный разбор

Это самый популярный вопрос на интервью для Junior QA. Разберём досконально.

GET — получить данные

Цель: Запросить данные, не изменяя их на сервере.

GET /api/users?role=admin&page=1&limit=20

Параметры:
  role=admin   → фильтр по роли
  page=1       → номер страницы
  limit=20     → элементов на странице

Характеристики GET:

Параметры:    в URL (?key=value)
Тело:         нет
Кэшируемость: да (браузер кэширует)
Идемпотентность: да (можно повторять)
Безопасность: да (не меняет данные)
Закладки:     можно сохранить URL
Логи:         URL виден в логах сервера

ВАЖНО: Никогда не передавай секретные данные в GET-параметрах:

❌ Плохо:
GET /login?username=alina&password=secret123
  → Пароль виден в логах сервера
  → Пароль сохраняется в истории браузера
  → Пароль виден в адресной строке

✓ Правильно:
POST /login
Body: {"username": "alina", "password": "secret123"}
  → Данные передаются в теле, не видны в URL

POST — отправить данные на сервер

Цель: Создать новый ресурс или отправить данные для обработки.

POST /api/orders
Content-Type: application/json

{
  "productId": 123,
  "quantity": 2,
  "deliveryAddress": "Москва, Ленина 10"
}

Характеристики POST:

Параметры:    в теле запроса (Body)
Тело:         есть
Кэшируемость: нет
Идемпотентность: нет (каждый раз создаёт новое)
Безопасность: нет (меняет данные)
Закладки:     невозможно
Логи:         тело не попадает в стандартные логи URL

Полное сравнение в одной таблице

ХарактеристикаGETPOST
НазначениеПолучить данныеСоздать / отправить данные
ПараметрыВ URLВ теле запроса
Тело запросаНетЕсть
КэшДаНет
ИдемпотентенДаНет
БезопасенДа (readonly)Нет (изменяет данные)
ЗакладкиМожноНельзя
Видимость данныхВ URL (небезопасно для секретов)В теле (скрыто от URL)
Ограничение размера~2000 символов URLОграничен только настройками сервера
Типичный ответ200 OK201 Created
Повторный запросТот же результатСоздаёт ещё один объект

Практический пример: поиск vs создание

# Поиск книг (GET — не создаём ничего нового)
GET /api/books?title=playwright&author=smith&year=2024
→ Возвращает список найденных книг

# Создание заказа (POST — создаём новую запись)
POST /api/orders
{
  "bookId": 42,
  "quantity": 1,
  "customerId": 7
}
→ Создаёт новый заказ, возвращает его ID

4. Работа с Postman: пошаговые примеры

Пример 1: GET-запрос с параметрами

Задача: Получить список постов пользователя с ID 1.

1. Метод: GET
2. URL: https://jsonplaceholder.typicode.com/posts
3. Вкладка Params:
   Key: userId   Value: 1
   (Postman автоматически добавит ?userId=1 к URL)
4. Нажать Send

Ожидаемый результат:

jsonjson
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat...",
    "body": "quia et suscipit..."
  },
  ...
]

Быстрые проверки:

  • Status code: 200
  • Ответ — массив
  • Все объекты имеют поле userId: 1

Пример 2: POST-запрос с телом

Задача: Создать нового пользователя.

1. Метод: POST
2. URL: https://jsonplaceholder.typicode.com/users
3. Вкладка Body → raw → JSON:
jsonjson
{
  "name": "Алина Иванова",
  "username": "alina_iv",
  "email": "alina@test.com",
  "phone": "+7-999-123-45-67"
}
4. Нажать Send

Ожидаемый результат:

jsonjson
{
  "name": "Алина Иванова",
  "username": "alina_iv",
  "email": "alina@test.com",
  "phone": "+7-999-123-45-67",
  "id": 11
}

Быстрые проверки:

  • Status code: 201
  • В ответе есть id (сервер присвоил)
  • Все переданные поля сохранились

Пример 3: Авторизованный запрос

Задача: Получить данные с Bearer Token.

1. Метод: GET
2. URL: https://api.example.com/profile
3. Вкладка Authorization:
   Type: Bearer Token
   Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
4. Нажать Send

Postman автоматически добавит заголовок:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

5. Переменные в Postman

Переменные позволяют не повторять одни и те же значения в каждом запросе.

Типы переменных

Глобальные   → доступны везде в Postman
Окружения    → доступны в выбранном окружении (dev, staging, prod)
Коллекции    → доступны в рамках коллекции
Локальные    → только в текущем запросе/тесте

Создание переменных окружения

Создай окружение «Development»:

BASE_URL   = https://api.dev.example.com
AUTH_TOKEN = eyJhbGciOiJIUzI1NiJ9.dev_token...
USER_ID    = 42

Используй в запросах:

URL: {{BASE_URL}}/api/users/{{USER_ID}}
Authorization: Bearer {{AUTH_TOKEN}}

Теперь переключись на окружение «Staging» — все запросы автоматически пойдут на staging сервер.

Передача данных между запросами

Первый запрос получает токен, второй использует его:

javascriptjavascript
// В Tests первого запроса (POST /login):
const token = pm.response.json().access_token;
pm.environment.set("AUTH_TOKEN", token);

// Второй запрос автоматически использует {{AUTH_TOKEN}}

6. Написание тестов в Postman

Вкладка Tests в Postman позволяет писать JavaScript-код для автоматической проверки ответа.

Основные проверки

javascriptjavascript
// Проверка кода статуса
pm.test("Статус 200", function () {
  pm.response.to.have.status(200);
});

// Проверка времени ответа
pm.test("Ответ быстрый (меньше 1 секунды)", function () {
  pm.expect(pm.response.responseTime).to.be.below(1000);
});

// Проверка наличия поля в ответе
pm.test("Ответ содержит поле id", function () {
  const body = pm.response.json();
  pm.expect(body).to.have.property("id");
});

// Проверка значения поля
pm.test("Имя пользователя корректное", function () {
  const body = pm.response.json();
  pm.expect(body.name).to.equal("Алина Иванова");
});

// Проверка типа данных
pm.test("Поле id — число", function () {
  const body = pm.response.json();
  pm.expect(body.id).to.be.a("number");
});

// Проверка заголовка ответа
pm.test("Ответ в формате JSON", function () {
  pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");
});

Реальный набор тестов для POST /users

javascriptjavascript
// Тест 1: Код ответа
pm.test("Статус 201 Created", function () {
  pm.response.to.have.status(201);
});

// Тест 2: Время ответа
pm.test("Ответ за разумное время", function () {
  pm.expect(pm.response.responseTime).to.be.below(2000);
});

// Тест 3: Наличие ID в ответе
pm.test("Новый пользователь получил ID", function () {
  const body = pm.response.json();
  pm.expect(body).to.have.property("id");
  pm.expect(body.id).to.be.a("number");
  pm.expect(body.id).to.be.above(0);
});

// Тест 4: Поля сохранились
pm.test("Данные пользователя сохранены корректно", function () {
  const requestBody = JSON.parse(pm.request.body.raw);
  const responseBody = pm.response.json();

  pm.expect(responseBody.name).to.equal(requestBody.name);
  pm.expect(responseBody.email).to.equal(requestBody.email);
});

// Тест 5: Сохраняем ID для следующих тестов
const newUserId = pm.response.json().id;
pm.environment.set("CREATED_USER_ID", newUserId);

7. Коллекции и организация запросов

Коллекция — это папка с запросами, которую можно запустить целиком (регрессия) или отдельно.

Структура коллекции

📁 Users API Tests
  ├── 📁 Happy Path
  │   ├── GET /users — получить список
  │   ├── GET /users/:id — получить по ID
  │   ├── POST /users — создать пользователя
  │   ├── PATCH /users/:id — обновить поля
  │   └── DELETE /users/:id — удалить
  │
  ├── 📁 Authentication
  │   ├── POST /login — успешный вход
  │   ├── POST /login — неверный пароль
  │   └── GET /profile — без токена
  │
  └── 📁 Validation
      ├── POST /users — пустое имя
      ├── POST /users — невалидный email
      └── POST /users — дублирующийся email

Pre-request Script — подготовка перед запросом

javascriptjavascript
// Генерируем уникальный email для каждого теста
const uniqueEmail = `test+${Date.now()}@example.com`;
pm.environment.set("TEST_EMAIL", uniqueEmail);

// Вычисляем дату (например, для фильтрации)
const today = new Date().toISOString().split("T")[0];
pm.environment.set("TODAY", today);

Запуск коллекции (Collection Runner)

  1. Нажми на коллекцию → Run collection
  2. Выбери окружение
  3. Настрой количество итераций
  4. Нажми Run

Postman покажет результаты: сколько тестов прошло, сколько упало.

Newman — запуск из командной строки

Newman — это CLI-инструмент для запуска Postman-коллекций в CI/CD:

bashbash
# Установка
npm install -g newman

# Запуск коллекции
newman run Users_API.postman_collection.json \
  --environment Development.postman_environment.json \
  --reporters cli,htmlextra \
  --reporter-htmlextra-export report.html

Это позволяет запускать API-тесты в GitHub Actions, GitLab CI и других системах.


8. SoapUI — инструмент для SOAP и REST

SoapUI — это специализированный инструмент для тестирования веб-сервисов. Лучше всего подходит для SOAP, но умеет и REST.

Основные возможности SoapUI

  • Импорт WSDL-файла и автоматическая генерация запросов
  • Отправка SOAP-запросов и просмотр XML-ответов
  • Написание Groovy-скриптов для проверок
  • Функциональное тестирование (Test Suite, Test Case, Test Step)
  • Нагрузочное тестирование
  • Mock-сервисы (имитация сервера)

Создание SOAP-проекта в SoapUI

Шаг 1: Создай проект из WSDL

File → New SOAP Project
Project Name: BankService
Initial WSDL: https://api.bank.com/service?wsdl
✓ Create Requests

SoapUI автоматически создаст все операции и сгенерирует XML-шаблоны.

Шаг 2: Заполни запрос

xmlxml
<soap:Envelope xmlns:soap="..." xmlns:bank="...">
  <soap:Body>
    <bank:GetBalanceRequest>
      <bank:AccountId>ACC-001234</bank:AccountId>
    </bank:GetBalanceRequest>
  </soap:Body>
</soap:Envelope>

Шаг 3: Отправь и проверь ответ

SoapUI разворачивает XML-ответ в удобное дерево. Можно видеть и сырой XML, и структурированный вид.

Assertions (проверки) в SoapUI

В SoapUI можно добавить автоматические проверки прямо к запросу:

Response SLA Assertion    →  Время ответа < 2000ms
HTTP Status Codes         →  Ожидаем 200
Contains Assertion        →  Ответ содержит "BalanceResponse"
XPath Match               →  //bank:Balance/text() = "150000.00"
Not Contains              →  Ответ не содержит "Fault"
Schema Compliance         →  XML соответствует XSD-схеме

XPath в SoapUI — пример

Assertion: XPath Match
Expression: //bank:Balance/text()
Expected:   150000.00

Это проверяет, что в ответе поле Balance равно 150000.00.

Функциональный тест (Test Suite)

SoapUI позволяет создавать тест-сьюты — цепочки запросов с проверками:

Test Suite: Полный цикл работы с аккаунтом
  Test Case 1: Авторизация
    Step 1: POST /login
    Step 2: Извлечь токен → сохранить в переменную
  
  Test Case 2: Просмотр баланса
    Step 1: GET /balance с токеном из переменной
    Step 2: Проверить, что баланс > 0
  
  Test Case 3: Перевод средств
    Step 1: POST /transfer
    Step 2: Проверить новый баланс

9. Postman vs SoapUI — что когда использовать

КритерийPostmanSoapUI
Тип APIREST (основное)SOAP (основное), REST
ИнтерфейсСовременный, интуитивныйФункциональный, перегруженный
Порог входаНизкийСредний
WSDL-импортОграниченныйОтличный
XML-работаБазоваяПродвинутая (XPath, XQuery)
СкриптыJavaScriptGroovy
Нагрузочное тестированиеНетДа (LoadUI)
CI/CD-интеграцияNewman (CLI)SoapUI Runner (CLI)
ЦенаБесплатный базовыйБесплатный базовый
Кому подходитQA для REST, разработчикиQA для SOAP, Enterprise QA

Вывод:

  • Работаешь с REST? → Postman
  • Работаешь с SOAP? → SoapUI
  • Работаешь с обоими? → знай оба инструмента

10. Типичные ошибки при тестировании API

Ошибка 1: Тестирование только позитивных сценариев

❌ Только это:
  POST /users с корректными данными → 201

✓ Полное тестирование:
  POST /users с корректными данными → 201
  POST /users без обязательного поля → 400/422
  POST /users с невалидным email → 400/422
  POST /users с уже существующим email → 409
  POST /users без токена → 401
  POST /users с токеном другого пользователя → 403

Ошибка 2: Не проверять структуру ответа

javascriptjavascript
// ❌ Только код статуса:
pm.test("OK", () => pm.response.to.have.status(200));

// ✓ Код + структура + значения:
pm.test("Пользователь создан корректно", function () {
  pm.response.to.have.status(201);
  const body = pm.response.json();
  pm.expect(body).to.have.property("id").that.is.a("number");
  pm.expect(body.email).to.equal(pm.environment.get("TEST_EMAIL"));
  pm.expect(body).to.not.have.property("password");  // Пароль не возвращается!
});

Ошибка 3: Зависимые тесты

❌ Тест 2 зависит от результата Теста 1:
  Тест 1: Создать пользователя с ID 5
  Тест 2: GET /users/5 (предполагает, что Тест 1 прошёл)

✓ Каждый тест самодостаточен:
  Тест 2: В Pre-request script создаёт собственного пользователя
          и сохраняет его ID в переменную

Ошибка 4: Захардкоженные данные

javascriptjavascript
// ❌ Плохо: email дублируется при повторном запуске
pm.environment.set("email", "test@example.com");

// ✓ Хорошо: уникальный email каждый раз
pm.environment.set("email", `test+${Date.now()}@example.com`);

11. Полный пример: тестируем API пользователей

Используем бесплатный тестовый API: https://jsonplaceholder.typicode.com

Тест 1: Получить список пользователей

Method: GET
URL: https://jsonplaceholder.typicode.com/users
javascriptjavascript
pm.test("Статус 200", () => pm.response.to.have.status(200));

pm.test("Ответ — непустой массив", function () {
  const users = pm.response.json();
  pm.expect(users).to.be.an("array");
  pm.expect(users.length).to.be.above(0);
});

pm.test("Каждый пользователь имеет обязательные поля", function () {
  const users = pm.response.json();
  users.forEach(function (user) {
    pm.expect(user).to.have.property("id");
    pm.expect(user).to.have.property("name");
    pm.expect(user).to.have.property("email");
  });
});

Тест 2: Получить конкретного пользователя

Method: GET
URL: https://jsonplaceholder.typicode.com/users/1
javascriptjavascript
pm.test("Статус 200", () => pm.response.to.have.status(200));

pm.test("Правильный пользователь", function () {
  const user = pm.response.json();
  pm.expect(user.id).to.equal(1);
  pm.expect(user.name).to.be.a("string");
  pm.expect(user.email).to.include("@");
});

// Сохраняем данные для следующих запросов
pm.environment.set("FETCHED_USER_ID", pm.response.json().id);

Тест 3: Создать пользователя (POST)

Method: POST
URL: https://jsonplaceholder.typicode.com/users
Body (raw JSON):
{
  "name": "{{TEST_NAME}}",
  "email": "{{TEST_EMAIL}}",
  "username": "test_user"
}

Pre-request Script:

javascriptjavascript
pm.environment.set("TEST_NAME", "Алина Иванова");
pm.environment.set("TEST_EMAIL", `alina+${Date.now()}@example.com`);

Tests:

javascriptjavascript
pm.test("Статус 201 Created", () => pm.response.to.have.status(201));

pm.test("Создан объект с ID", function () {
  const body = pm.response.json();
  pm.expect(body.id).to.be.a("number");
  pm.expect(body.name).to.equal(pm.environment.get("TEST_NAME"));
  pm.expect(body.email).to.equal(pm.environment.get("TEST_EMAIL"));
});

pm.environment.set("NEW_USER_ID", pm.response.json().id);

Тест 4: Несуществующий пользователь

Method: GET
URL: https://jsonplaceholder.typicode.com/users/99999
javascriptjavascript
pm.test("Статус 404 для несуществующего пользователя", function () {
  pm.response.to.have.status(404);
});

pm.test("Тело ответа пустое или содержит ошибку", function () {
  const body = pm.response.json();
  pm.expect(body).to.deep.equal({});
});

12. Чеклист готового QA по API-тестированию

Junior QA должен уметь

  • Отправить GET-запрос и интерпретировать ответ
  • Отправить POST-запрос с JSON-телом
  • Добавить заголовок Authorization
  • Читать HTTP-коды ответа и понимать их значение
  • Написать базовые тесты в Postman (статус, наличие поля)
  • Организовать запросы в коллекции
  • Использовать переменные окружения

Middle QA должен уметь

  • Всё из Junior +
  • Передавать данные между запросами (Pre-request / Tests)
  • Писать полные наборы тестов для CRUD
  • Покрывать негативные сценарии и граничные значения
  • Запускать коллекцию через Newman в CI/CD
  • Работать с SoapUI для SOAP-сервисов
  • Читать OpenAPI/Swagger документацию
  • Составлять чеклист тестирования API по спецификации

Итоги урока

ТемаЧто запомнить
Зачем тестировать APIБыстрее UI, стабильнее, доступно до UI
GETПолучить данные. Параметры в URL. Кэшируется
POSTСоздать. Данные в теле. Не идемпотентен
PostmanГлавный инструмент для REST. Коллекции, переменные, тесты
SoapUIГлавный инструмент для SOAP. XPath-проверки, WSDL-импорт
NewmanЗапуск Postman-коллекций в CI/CD
Тесты в PostmanJavaScript в вкладке Tests
ПеременныеОкружения для dev/staging/prod

Практические задания

  1. Первые шаги в Postman: Установи Postman. Отправь GET-запрос к https://jsonplaceholder.typicode.com/posts. Добавь параметр userId=1. Изучи ответ: сколько постов? Какова структура каждого поста?

  2. POST с телом: Отправь POST-запрос к https://jsonplaceholder.typicode.com/posts с телом:

    jsonjson
    {"title": "Мой первый пост", "body": "Содержимое поста", "userId": 1}

    Проверь: какой код ответа? Какой ID получил новый пост?

  3. Написание тестов: К запросу из задания 2 добавь тесты:

    • Статус 201
    • Ответ содержит поле id
    • Поле title совпадает с переданным
  4. Переменные окружения: Создай окружение с переменной BASE_URL = https://jsonplaceholder.typicode.com. Замени все запросы в коллекции на {{BASE_URL}}/posts. Убедись, что всё работает.

  5. Полная коллекция: Создай коллекцию для тестирования /posts:

    • GET /posts (список)
    • GET /posts/1 (конкретный пост)
    • POST /posts (создание)
    • PATCH /posts/1 (обновление)
    • DELETE /posts/1 (удаление)

    Для каждого запроса напиши минимум 3 теста. Включи негативный сценарий (GET /posts/99999).

  6. Newman в CI: Экспортируй свою коллекцию и окружение из Postman. Установи Newman. Запусти коллекцию из командной строки. Что ты видишь в выводе? Сколько тестов прошло?


Ты прошёл все уроки блока «Веб-тестирование для QA». Теперь у тебя есть понимание:

  • Как работает клиент-серверная архитектура
  • Как устроены HTTP-протокол и модель TCP/IP
  • Что такое URL, IP, DNS, кэш и куки
  • В чём разница между SOAP/XML и REST/JSON
  • Как профессионально тестировать API с Postman и SoapUI