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: дополнительно тестирует UI2. Postman — главный инструмент для REST
Postman — это графическое приложение для работы с HTTP-запросами. Позволяет:
- Отправлять запросы к любому API
- Организовывать запросы в коллекции
- Писать автоматические проверки (тесты)
- Управлять переменными окружения
- Запускать коллекции в CI/CD
Установка и первый запрос
- Скачай Postman: postman.com/downloads
- Создай аккаунт (бесплатно)
- Нажми «New» → «HTTP»
Анатомия запроса в Postman
| Область | Содержимое |
|---|---|
| Строка запроса | Метод [GET ▼], URL, кнопка [Send] |
| Params | Query-параметры: Key: id, Value: 1 |
| Headers | Content-Type: application/json |
| Body | Для GET — пустое |
| Authorization | Bearer Token: eyJhbGci... |
| Tests | JavaScript-код для автопроверок |
| Ответ | Body, Cookies, Headers, Test Results |
| Статус ответа | 200 OK, Time: 124 ms, Size: 508 B |
Пример тела ответа:
{
"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"}
→ Данные передаются в теле, не видны в URLPOST — отправить данные на сервер
Цель: Создать новый ресурс или отправить данные для обработки.
POST /api/orders
Content-Type: application/json
{
"productId": 123,
"quantity": 2,
"deliveryAddress": "Москва, Ленина 10"
}Характеристики POST:
Параметры: в теле запроса (Body)
Тело: есть
Кэшируемость: нет
Идемпотентность: нет (каждый раз создаёт новое)
Безопасность: нет (меняет данные)
Закладки: невозможно
Логи: тело не попадает в стандартные логи URLПолное сравнение в одной таблице
| Характеристика | GET | POST |
|---|---|---|
| Назначение | Получить данные | Создать / отправить данные |
| Параметры | В URL | В теле запроса |
| Тело запроса | Нет | Есть |
| Кэш | Да | Нет |
| Идемпотентен | Да | Нет |
| Безопасен | Да (readonly) | Нет (изменяет данные) |
| Закладки | Можно | Нельзя |
| Видимость данных | В URL (небезопасно для секретов) | В теле (скрыто от URL) |
| Ограничение размера | ~2000 символов URL | Ограничен только настройками сервера |
| Типичный ответ | 200 OK | 201 Created |
| Повторный запрос | Тот же результат | Создаёт ещё один объект |
Практический пример: поиск vs создание
# Поиск книг (GET — не создаём ничего нового)
GET /api/books?title=playwright&author=smith&year=2024
→ Возвращает список найденных книг
# Создание заказа (POST — создаём новую запись)
POST /api/orders
{
"bookId": 42,
"quantity": 1,
"customerId": 7
}
→ Создаёт новый заказ, возвращает его ID4. Работа с 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Ожидаемый результат:
[
{
"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:{
"name": "Алина Иванова",
"username": "alina_iv",
"email": "alina@test.com",
"phone": "+7-999-123-45-67"
}4. Нажать Send
Ожидаемый результат:
{
"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. Нажать SendPostman автоматически добавит заголовок:
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 сервер.
Передача данных между запросами
Первый запрос получает токен, второй использует его:
// В Tests первого запроса (POST /login):
const token = pm.response.json().access_token;
pm.environment.set("AUTH_TOKEN", token);
// Второй запрос автоматически использует {{AUTH_TOKEN}}
6. Написание тестов в Postman
Вкладка Tests в Postman позволяет писать JavaScript-код для автоматической проверки ответа.
Основные проверки
// Проверка кода статуса
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
// Тест 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 — дублирующийся emailPre-request Script — подготовка перед запросом
// Генерируем уникальный 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)
- Нажми на коллекцию → Run collection
- Выбери окружение
- Настрой количество итераций
- Нажми Run
Postman покажет результаты: сколько тестов прошло, сколько упало.
Newman — запуск из командной строки
Newman — это CLI-инструмент для запуска Postman-коллекций в CI/CD:
# Установка
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 RequestsSoapUI автоматически создаст все операции и сгенерирует XML-шаблоны.
Шаг 2: Заполни запрос
<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 — что когда использовать
| Критерий | Postman | SoapUI |
|---|---|---|
| Тип API | REST (основное) | SOAP (основное), REST |
| Интерфейс | Современный, интуитивный | Функциональный, перегруженный |
| Порог входа | Низкий | Средний |
| WSDL-импорт | Ограниченный | Отличный |
| XML-работа | Базовая | Продвинутая (XPath, XQuery) |
| Скрипты | JavaScript | Groovy |
| Нагрузочное тестирование | Нет | Да (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: Не проверять структуру ответа
// ❌ Только код статуса:
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: Захардкоженные данные
// ❌ Плохо: 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/userspm.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/1pm.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:
pm.environment.set("TEST_NAME", "Алина Иванова");
pm.environment.set("TEST_EMAIL", `alina+${Date.now()}@example.com`);
Tests:
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/99999pm.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 |
| Тесты в Postman | JavaScript в вкладке Tests |
| Переменные | Окружения для dev/staging/prod |
Практические задания
-
Первые шаги в Postman: Установи Postman. Отправь GET-запрос к
https://jsonplaceholder.typicode.com/posts. Добавь параметрuserId=1. Изучи ответ: сколько постов? Какова структура каждого поста? -
POST с телом: Отправь POST-запрос к
https://jsonplaceholder.typicode.com/postsс телом:jsonjson {"title": "Мой первый пост", "body": "Содержимое поста", "userId": 1}Проверь: какой код ответа? Какой ID получил новый пост?
-
Написание тестов: К запросу из задания 2 добавь тесты:
- Статус 201
- Ответ содержит поле
id - Поле
titleсовпадает с переданным
-
Переменные окружения: Создай окружение с переменной
BASE_URL = https://jsonplaceholder.typicode.com. Замени все запросы в коллекции на{{BASE_URL}}/posts. Убедись, что всё работает. -
Полная коллекция: Создай коллекцию для тестирования
/posts:- GET /posts (список)
- GET /posts/1 (конкретный пост)
- POST /posts (создание)
- PATCH /posts/1 (обновление)
- DELETE /posts/1 (удаление)
Для каждого запроса напиши минимум 3 теста. Включи негативный сценарий (GET /posts/99999).
-
Newman в CI: Экспортируй свою коллекцию и окружение из Postman. Установи Newman. Запусти коллекцию из командной строки. Что ты видишь в выводе? Сколько тестов прошло?
Ты прошёл все уроки блока «Веб-тестирование для QA». Теперь у тебя есть понимание:
- Как работает клиент-серверная архитектура
- Как устроены HTTP-протокол и модель TCP/IP
- Что такое URL, IP, DNS, кэш и куки
- В чём разница между SOAP/XML и REST/JSON
- Как профессионально тестировать API с Postman и SoapUI