QA Interview Hub · База знань · Тести
API
Документація й контракти: OpenAPI, JSON Schema, contract testing
Swagger/OpenAPI у тестуванні
OpenAPI — специфікація опису REST API, Swagger — набір інструментів навколо неї, зокрема інтерактивний Swagger UI. Специфікація описує endpoints, методи, параметри, схеми запитів і відповідей, коди відповідей та авторизацію. Для QA це головне джерело вимог до API.
Через Swagger UI можна швидко виконати запит прямо з браузера, авторизуватися й подивитися реальну відповідь. Специфікацію можна імпортувати в Postman, щоб автоматично отримати колекцію всіх запитів. Також з неї беру схеми для валідації відповідей в автотестах.
Під час тестування перевіряю, чи реальна поведінка API відповідає документації: типи полів, обов'язковість, коди помилок. Розбіжності — це або баг у коді, або баг у документації, і обидва варто заводити. На основі специфікації будують contract testing та генерують API-клієнти.
OpenAPI як джерело тестів
OpenAPI-специфікація описує ендпоінти, параметри, схеми тіл, коди відповідей і авторизацію. Для мене це не лише документація, а сировина для автотестів.
Як я її використовую на практиці:
- Беру схеми з
components/schemas і валідую ними відповіді в автотестах, замість того щоб переписувати структуру вручну. - Генерую TypeScript-типи зі специфікації (наприклад,
openapi-typescript), і зміна контракту ламає компіляцію тестів. - Проганяю лінтер специфікації в CI, щоб ловити неповні описи: відсутні коди помилок, поля без типу.
- Порівнюю специфікацію між версіями, щоб побачити зміни, що ламають клієнтів.
Типові розбіжності, які знаходжу: поле позначене обов'язковим, але приходить null; задокументовано 400, а реально 422; у специфікації немає опису помилок взагалі; enum у документації не збігається з реальними значеннями.
Transfer:
type: object
required: [id, status, amount]
properties:
id: { type: string }
status: { type: string, enum: [pending, completed, failed] }
amount: { type: number }
На співбесіді: OpenAPI для мене — джерело правди і сировина для тестів: зі схем я валідую відповіді й генерую типи. Будь-яку розбіжність між специфікацією і реальною поведінкою заводжу як баг — або в коді, або в документації.
Валідація схем у коді: ajv і zod
У Postman схему перевіряє jsonSchema, а в TypeScript-автотестах зазвичай використовують ajv (JSON Schema валідатор) або zod (схеми прямо в TypeScript з виведенням типів).
import { test, expect } from '@playwright/test';
import { z } from 'zod';
const Transfer = z.object({
id: z.string(),
status: z.enum(['pending', 'completed', 'failed']),
amount: z.number().positive(),
currency: z.string().regex(/^[A-Z]{3}$/),
}).strict();
test('create transfer matches contract', async ({ request }) => {
const res = await request.post('/v1/transfers', {
data: { toAccount: 'UA21', amount: 150, currency: 'UAH' },
});
expect(res.status()).toBe(201);
const parsed = Transfer.safeParse(await res.json());
expect(parsed.success, JSON.stringify(parsed.error?.issues)).toBe(true);
});
.strict() у zod працює як additionalProperties: false — зайве поле валить тест. Вибір інструмента простий: якщо схеми вже є в OpenAPI чи JSON Schema, беру ajv і не дублюю їх; якщо схем немає, zod зручніший, бо одночасно дає і перевірку, і тип.
На співбесіді: У коді я валідую відповіді через ajv, якщо є готові JSON-схеми з OpenAPI, або через zod, якщо описую контракт сам у TypeScript. Строгий режим ловить зайві поля, а повідомлення про помилку одразу показує, яке поле не збіглося.
Consumer-driven contracts і breaking changes
Contract testing перевіряє, що два сервіси розуміють одне одного, без підняття всієї системи. У підході consumer-driven contracts (інструмент Pact) споживач, наприклад фронтенд чи інший мікросервіс, описує в тесті, які запити він робить і які поля очікує. З цього генерується контракт, він публікується в Pact Broker, і провайдер у своєму CI перевіряє, що реальний API цей контракт виконує.
Переваги: швидко, ізольовано, і провайдер дізнається, що зламав конкретного споживача, ще до деплою. Pact перевіряє лише те, що споживач реально використовує, тому це не заміна функціональних тестів провайдера.
Breaking vs non-breaking changes:
| Зміна | Тип |
|---|
| Додати нове необов'язкове поле у відповідь | Non-breaking |
| Додати новий ендпоінт | Non-breaking |
| Додати необов'язковий параметр у запит | Non-breaking |
| Видалити або перейменувати поле відповіді | Breaking |
| Змінити тип поля, наприклад число на рядок | Breaking |
| Зробити необов'язкове поле запиту обов'язковим | Breaking |
| Змінити статус-код чи формат помилки | Breaking |
Нюанс: нове значення в enum формально «додавання», але може зламати клієнта, який обробляє лише відомі значення.
На співбесіді: У consumer-driven contracts споживач фіксує свої очікування в контракті через Pact, а провайдер перевіряє його у своєму CI до деплою. Додавання полів зазвичай не ламає клієнтів, а видалення, перейменування чи зміна типу — breaking change, яке потребує нової версії API.