OpenAPI/Swagger: специфікація як джерело істини
Зміст
Уяви: тобі дали новий сервіс і сказали «протестуй API». Перше питання — де взяти правду про те, як цей API має працювати? Сторінка у вікі бреше, бо її писали пів року тому. Розробник відповідає «подивись у код». Postman-колекція колеги покриває третину . Саме цю проблему розв'язує OpenAPI — формальний машиночитний опис API, який лежить у репозиторії поруч із кодом і слугує єдиним джерелом істини (source of truth) для документації, тестів, і клієнтського коду.
Ця глава недарма стоїть перед тест-дизайном для API: специфікація — це матеріал, з якого тест-дизайн робить кейси. Вміння читати її заощаджує години перепитувань: тест-кейси стають системними («покрив усе, що в контракті»), а не «що згадав — те й перевірив».
Що таке OpenAPI і до чого тут Swagger
OpenAPI Specification (OAS) — це стандарт опису HTTP API у форматі YAML або JSON. Один файл (часто openapi.yaml) перелічує всі ендпоінти, їхні параметри, формати запитів і відповідей, коди статусів і правила авторизації. Про сам REST і формати даних ми говорили в розділі про веб — специфікація просто записує все це у строгій, придатній для програмної обробки формі.
З назвами історична плутанина. Спочатку формат називався Swagger; 2015 року SmartBear передала специфікацію під крило OpenAPI Initiative (Linux Foundation), і з версії 3.0 стандарт зветься OpenAPI. Слово «Swagger» лишилося за екосистемою інструментів SmartBear: Swagger UI, Swagger Editor, Swagger Codegen. Тобто коректно так: OpenAPI — специфікація (стандарт), Swagger — інструменти навколо неї. У побуті «подивись у Swagger» майже завжди означає «відкрий Swagger UI з нашою специфікацією».
Навіщо опис саме машиночитний? Текст у вікі вміє читати тільки людина. А з формального YAML-файлу інструменти автоматично породжують цілу низку артефактів — і всі вони гарантовано узгоджені між собою, бо мають спільне джерело:
Для тестувальника з цього списку два пункти особливо смачні. По-перше, зі специфікації можна підняти mock-сервер, який віддаватиме відповіді за описаними схемами ще до того, як бекенд написаний, — механіку розбираємо в главі про мокання залежностей. По-друге, реальні відповіді API можна автоматично звіряти зі схемами — про це нижче.
Актуальна стандарту — 3.x: найпоширеніші на практиці OpenAPI 3.0 (2017) і 3.1 (2021), у 2025-му вийшла версія 3.2. Стара версія Swagger 2.0 формально називається OpenAPI 2.0 і досі трапляється на легасі-проєктах.
Структура специфікації: paths, parameters, schemas, responses, $ref
Ось мінімальна, але цілком робоча специфікація одного ресурсу:
openapi: 3.0.3
info:
title: Users API
version: 1.0.0
paths:
/users/{id}:
get:
summary: Отримати користувача за id
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Користувача знайдено
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: Користувача з таким id не існує
components:
schemas:
User:
type: object
required: [id, email]
properties:
id:
type: integer
email:
type: string
format: email
name:
type: string
maxLength: 50
Розберімо блоки, з якими працює QA:
paths — серце специфікації: перелік усіх шляхів і HTTP-методів на кожному. Пара «шлях × метод» називається операцією (operation). Важливий нюанс: стандарт нічого не стверджує про операції, яких в описі немає, — частину API свідомо лишають поза описом, якщо її не виходить описати засобами OAS. Тож «немає в paths — не існує» є трактуванням команди, а не висновком зі стандарту.
parameters — усе, що приходить поза тілом запиту. Поле in каже, звідки береться параметр, і розташувань пʼять: path (сегменти шляху, як {id} вище), query (фільтри, ), querystring, header і cookie. Параметр унікальний за парою «імʼя + розташування», тож id у шляху і id у query — два різні параметри з окремими перевірками. У кожного — тип, ознака required, іноді обмеження: enum з допустимими значеннями, minimum/maximum, pattern.
responses — задокументовані коди статусів і схема тіла для кожного. Це готовий перелік очікуваних результатів: якщо у специфікації для операції описані 200 і 404, обидва сценарії мають бути відтворювані (самі статус-коди — у розділі про веб).
components/schemas — словник моделей даних. Схема описує форму обʼєкта: які поля, яких типів, які обовʼязкові (required), які обмеження (format, maxLength, enum). Синтаксис схем — це діалект JSON Schema; в OpenAPI 3.1 сумісність із JSON Schema повна, у 3.0 є дрібні відмінності (наприклад, «поле може бути null» у 3.0 позначається окремим ключовим словом nullable: true). Детально про перевірки за JSON Schema — у главі про перевірки відповіді.
$ref — посилання на вже описаний фрагмент замість копіювання. Рядок $ref: '#/components/schemas/User' каже: «тут та сама схема User». Один опис моделі перевикористовується в десятках операцій, і зміна в одному місці міняє контракт скрізь. Для QA це підказка: якщо схему User поправили, зачеплені всі ендпоінти, які на неї посилаються, — ось і обсяг .
Swagger UI: специфікація очима людини
Swagger UI — вебсторінка, яка рендерить специфікацію в інтерактивну документацію: список операцій, розгорнуті схеми, приклади запитів. На багатьох проєктах вона доступна прямо на тестовому стенді за адресою на кшталт /swagger або /api-docs.
Найкорисніша кнопка — Try it out: заповнюєш параметри, тиснеш Execute — і Swagger UI надсилає справжній запит на середовище й показує справжню відповідь. Це найшвидший спосіб помацати незнайомий API без Postman і curl. Два застереження. Перше: запити справжні — DELETE через Try it out на спільному стенді видалить дані так само, як будь-який інший клієнт. Друге: те, що показує UI (схеми, приклади), — це вміст специфікації, а не поведінка сервера; правдивість цих обіцянок ще треба перевірити.
Swagger UI зручний для розвідки й разових перевірок; системну роботу з запитами краще вести в Postman або в коді.
Від специфікації до тест-кейсів
Головна практична навичка цієї глави: специфікація — це готовий каркас тест-аналізу. Кожен блок відповідає на своє тестове питання:
| Блок | Що там записано | Які тести з цього народжуються |
|---|---|---|
paths | усі операції API | мапа покриття: кожна пара «шлях × метод» викликана хоча б раз |
parameters | типи, required, enum, межі | позитив із валідними значеннями; негатив: пропущений обовʼязковий, чужий тип, значення поза enum |
requestBody + schemas | обовʼязкові поля, формати, довжини | граничні значення, відсутні required-поля, невалідні формати |
responses | задокументовані статуси | окремий сценарій на кожен статус: як добитися 200, 404, 422 |
securitySchemes | схеми авторизації | запит без токена і з невалідним токеном (докладно — у главі про авторизацію в API) |
Подивись ще раз на схему User вище — вона сама диктує перевірки. email має format: email — отже, потрібен негативний тест із рядком без «собачки». name має maxLength: 50 — класична пара граничних значень: 50 символів проходить, 51 — ні. id та email у required — а name ні, отже, відповідь без name валідна, і клієнт зобовʼязаний таке переживати. Кожне обмеження у схемі — це межа, а межі — улюблене місце проживання багів. Як системно застосовувати техніки еквівалентності й граничних значень до параметрів — у главі про тест-дизайн для API; самі техніки — в розділі про тест-дизайн.
Бонус такого підходу — вимірність. «Я протестував API» — твердження ні про що; «покрито всі операції з paths, для кожної — всі задокументовані статуси плюс негативи за обмеженнями схем» — уже осмислена заявка на , яку можна показати команді.
Коли дока бреше: розбіжності специфікації й реальності
Специфікація — джерело істини за задумом, але не за фактом: вона теж написана людьми і теж відстає від коду. Типові розбіжності, які варто шукати свідомо:
- у реальній відповіді є поля, яких немає у схемі (або навпаки — обіцяне поле не приходить);
- сервер повертає не той статус, що описаний у
responses: у доці400, у житті500; - тип не збігається: у схемі
id: integer, а приходить рядок"42"; - поле в
required, а насправді буваєnullчи зникає; - параметр позначений
required: true, але запит без нього чомусь проходить.
Майже кожна така знахідка — дефект; питання лише, чий: коду чи специфікації. Один виняток варто знати: сам по собі код, якого немає в responses, порушенням стандарту не є — OAS прямо визнає, що перелік відповідей не зобовʼязаний покривати всі можливі коди, бо наперед вони можуть бути невідомі. Обовʼязково описаними мають бути успішна відповідь і відомі помилки, тож розбіжність тут — це розходження з контрактом конкретного API, а не з OAS. Відповідь залежить від того, як команда працює з контрактом. У підході design-first специфікацію пишуть до коду, вона і є узгодженим контрактом — тоді розбіжність майже завжди баг реалізації. У підході code-first специфікація генерується з анотацій у коді — тоді вона автоматично встигає за кодом, але сумлінно документує і його помилки, а «істина» розмивається. Тестувальнику не треба вирішувати цю суперечку самотужки — треба зафіксувати розбіжність, показати обидві сторони (що обіцяно і що прийшло) і винести на команду. Мовчки підлаштувати тест під фактичну поведінку — найгірший варіант: контракт продовжить брехати наступному, хто його прочитає.
Розбіжність, яку зловили не тести, а інша команда у своєму продакшн-коді, ми називаємо (schema drift) — і це вже територія контрактного тестування.
Валідація відповідей проти схеми
Очима дрейф не ловиться: поле, що зникло з відповіді, у JSON на пів екрана просто не помітиш. Тому звірку відповідей зі схемами автоматизують — кожен автотест, окрім своїх функціональних перевірок, проганяє тіло відповіді через . У JavaScript/TypeScript стандартний інструмент — Ajv, валідатор JSON Schema:
import { test, expect } from '@playwright/test';
import Ajv from 'ajv';
// Схема — з components/schemas специфікації
const userSchema = {
type: 'object',
required: ['id', 'email'],
properties: {
id: { type: 'integer' },
email: { type: 'string' },
name: { type: 'string', maxLength: 50 },
},
additionalProperties: false,
};
test('GET /users/1 відповідає схемі User', async ({ request }) => {
const response = await request.get('/api/users/1');
expect(response.status()).toBe(200);
const ajv = new Ajv();
const valid = ajv.validate(userSchema, await response.json());
expect(valid, JSON.stringify(ajv.errors)).toBe(true);
});
Зверни увагу на additionalProperties: false — без нього зайві, не описані у схемі поля пройшли б валідацію мовчки, бо за замовчуванням JSON Schema додаткові поля дозволяє. Саме так непомічені поля з персональними даними випливають у публічних відповідях.
І тверезе застереження: схема перевіряє форму, а не зміст. Відповідь, де email — синтаксично валідна адреса іншого користувача, схему пройде бездоганно. Валідація проти схеми — це від дрейфу контракту, а не заміна функціональних перевірок. Як вбудувати її в структуру автотестів — у главі про API-автотести в коді.
Кодогенерація типів зі специфікації
Валідація проти схеми ловить розбіжність у момент прогону: тест уже сходив у мережу, дочекався відповіді — і аж тоді сказав «поля немає». Є друга лінія захисту, яка спрацьовує до першого запиту. Це той самий пункт «Кодогенерація» зі схеми на початку глави: зі специфікації породжують код — клієнтів, серверні або, що найкорисніше для тестів, типи мови програмування (кодогенерація типів, type generation). У JavaScript/TypeScript цим займається openapi-typescript: він перетворює схеми OpenAPI 3.0 і 3.1 на TypeScript, без Java і без запущеного OpenAPI-сервера.
Потік лінійний: специфікація → генератор → файл із типами → компілятор. Одна команда — npx openapi-typescript ./openapi.yaml -o ./tests/api/types.ts — читає YAML або JSON і кладе поруч файл, з якого імпортують paths (операції) і components (схеми). Далі в тестах замість написаного на віру інтерфейсу стоїть тип, витягнутий із самої специфікації:
import type { paths, components } from './types'; // згенеровано openapi-typescript
type User = components['schemas']['User'];
type UserResponse =
paths['/users/{id}']['get']['responses'][200]['content']['application/json']['schema'];
Типи безрантаймні (runtime-free types) — у прогоні їх немає взагалі. Але поки код компілюється, вони працюють, і в цьому вся вигода. Перейменували в специфікації email на contactEmail, перегенерували типи — і кожне звертання user.email у тестах стає помилкою компіляції: не одного тесту на одному оточенні, а всіх файлів одразу, ще до того, як хоч один запит пішов у мережу. Ajv покаже той самий дрейф схеми теж, але червоним тестом і вже по факту. Саме тому офіційна дока проєкту радить не покладатися на лінтер чи команду збірки, а тримати перевірку типів окремим npm-скриптом (tsc --noEmit) і ганяти його в CI.
Уся конструкція тримається на свіжості згенерованого файлу: якщо типи старі, компілятор упевнено підтвердить учорашню картину світу. Тому генератор уміє брати не лише локальний файл, а й URL специфікації — тобто саме ту версію, яку зараз віддає сервіс. А --check (типово вимкнений) робить зворотну перевірку: чи згенеровані типи ще актуальні.
Наша практика (не канон). Далі — те, як цей крок вбудовують команди, з якими ми працювали; окремого стандарту чи специфікації під нього немає. Генерацію вішають на pre-скрипт перед прогоном (pretest), щоб типи бралися зі свіжої специфікації, а не з того, що лежало в теці з минулого тижня. Сам згенерований файл заводять у .gitignore: він похідний, у діфі шумить і ревʼю не піддається. Протилежний варіант — тримати файл у репозиторії й перевіряти його свіжість у CI — теж робочий; вибір між ними команда робить сама, і головне тут не спосіб, а те, щоб типи не могли мовчки відстати від специфікації.
Типові помилки
- Виглядає як «Swagger — це наша документація», а насправді Swagger UI — лише рендер файлу специфікації. Джерело — YAML/JSON у репозиторії; якщо на стенд задеплоєна стара версія, красива сторінка впевнено бреше.
- Виглядає як «у специфікації описаний
200— значить, ендпоінт працює», а насправді специфікація описує намір, а не факт. Це список обіцянок для перевірки, а не результат перевірки. - Виглядає як «поле не в
required, тестувати нічого», а насправді опціональність — це окрема гілка поведінки: відповідь без цього поля валідна, і все, що її споживає, мусить це переживати. Опціональні поля — класичне гніздо null-багів. - Виглядає як «схема пройшла — відповідь правильна», а насправді схема перевіряє типи і структуру, а не бізнес-логіку. Чужі дані правильної форми валідатор пропустить.
- Виглядає як «додаткових полів у відповіді не буде, у схемі ж їх немає», а насправді без явного
additionalProperties: falseзайві поля дозволені й валідацію проходять. - Виглядає як «дока застаріла, орієнтуюсь на фактичну поведінку», а насправді так контракт остаточно втрачає сенс. Розбіжність — це дефект, який треба зафіксувати, а не обійти.
Підсумок
- Специфікація OpenAPI — машиночитний контракт API і єдине джерело істини; Swagger UI — лише її людиночитний рендер.
- Кожен блок специфікації — готовий чек-лист:
pathsдає мапу покриття,parametersіschemas— межі й негативи,responses— перелік статусів, кожен з яких треба вміти відтворити. - Розбіжність специфікації й реальності — майже завжди дефект (сам факт коду поза переліком
responsesпорушенням OAS не є); чий саме (коду чи доки) — вирішує команда залежно від підходу design-first чи code-first, але фіксувати її — робота QA. - Валідація відповіді проти схеми ловить дрейф контракту, який не видно очима, — але перевіряє форму, а не зміст.
$refзвʼязує схеми в граф: зміна однієї моделі зачіпає всі операції, що на неї посилаються, — це готовий обсяг регресії.
Можливі питання
- «Чим OpenAPI відрізняється від Swagger?» — перевіряють охайність термінології: OpenAPI — стандарт специфікації, Swagger — екосистема інструментів навколо нього (UI, Editor, Codegen). Плутанина пробачна, але точна відповідь одразу додає балів.
- «Як ти використовуєш специфікацію при тестуванні API?» — чекають системності, а не «дивлюсь у Swagger, що там є»: покриття з
paths, негативи з обмежень параметрів і схем, сценарій на кожен задокументований статус. - «Реальна відповідь не збігається з докою. Твої дії?» — дивляться на процесне мислення: зафіксувати обидві сторони розбіжності, зʼясувати, що в команді є джерелом істини, завести дефект — а не мовчки підігнати тест під факт.
- «Як перевірити, що відповідь відповідає схемі?» — хочуть почути про автоматичну валідацію (JSON Schema, Ajv чи аналог) і про її межу: форма — так, бізнес-логіка — ні.
- «Навіщо машиночитна специфікація, якщо є сторінка у вікі?» — перевіряють розуміння ідеї: з формального опису генеруються узгоджені між собою дока, моки, типи й валідація; вікі ж дрейфує мовчки і нічого не породжує.
Джерела
Що таке OpenAPI і до чого тут Swagger
- OpenAPI Specification — OAS — мовно-незалежний опис інтерфейсу, з якого видно можливості сервісу без доступу до коду й без розглядання трафіку.
- Learn OpenAPI — Introduction (OpenAPI Initiative) — історія назви: OAS базувався на Swagger 2.0, переданій SmartBear у 2015-му, і відтоді ведеться OpenAPI Initiative.
- Swagger UI — офіційна сторінка інструмента (SmartBear) — офіційна сторінка сама фіксує співвідношення назв: сторінка генерується з «your OpenAPI (formerly known as Swagger) Specification».
Структура специфікації: paths, parameters, schemas, responses, $ref
- OpenAPI Specification — операція — це пара «шлях × метод»; параметр унікальний за парою «імʼя + розташування», розташувань пʼять, а
responsesперелічує очікувані відповіді. - Learn OpenAPI — Structure of an OpenAPI Description — опис може складатися з кількох документів, звʼязаних посиланнями; точка входу — документ із кореневим обʼєктом.
- JSON Schema Core, draft 2020-12 —
$refзастосовує ту саму схему, і результат перевірки є результатом схеми, на яку вказано, — звідси обсяг регресії. - JSON Schema Validation, draft 2020-12 — словник обмежень схеми:
type,required,maxLength,format— усе, з чого потім беруться межі.
Swagger UI: специфікація очима людини
- Swagger UI — офіційна сторінка інструмента (SmartBear) — рендер дає побачити ресурси API й повзаємодіяти з ними навіть без наявної реалізації, але показує вміст опису, а не поведінку сервера.
- Swagger UI — Configuration (docs/usage) —
Try it outшле справжні запити (їх перехоплюєrequestInterceptor, а тривалість міряєdisplayRequestDuration), і дефолтнийsupportedSubmitMethodsвключаєdelete.
Від специфікації до тест-кейсів
- OpenAPI Specification —
pathsяк мапа операцій іresponsesяк перелік задокументованих статусів — готовий каркас покриття. - JSON Schema Validation, draft 2020-12 — кожне обмеження схеми дає межу:
maxLengthпорівнює «менша або рівна», тож50валідне, а51— ні;requiredстежить лише за наявністю імені.
Коли дока бреше: розбіжності специфікації й реальності
- OpenAPI Specification —
responsesне зобовʼязаний покривати всі можливі коди: описаними мають бути успішна відповідь і відомі помилки. - Prism (Stoplight) — README репозиторію — валідаційний пропускає трафік до реальної реалізації й шукає розбіжності з документом OpenAPI.
- Pact — офіційна документація (Introduction) — провайдерське звіряє фактичну поведінку з задокументованим контрактом — але нічого не знає про очікування .
Валідація відповідей проти схеми
- Ajv — Why use Ajv (офіційна документація) — валідатор перетворює схему на швидку функцію перевірки й підтримує чернетки JSON Schema, включно з 2020-12.
- JSON Schema Core, draft 2020-12 — пропуск
additionalPropertiesдорівнює порожній схемі — зайві поля проходять валідацію мовчки. - JSON Schema Validation, draft 2020-12 — схема стереже форму:
formatза замовчуванням є анотацією, тож синтаксично валідне значення ще нічого не доводить про зміст.
Кодогенерація типів зі специфікації
- openapi-typescript — Introduction — перетворює схеми OpenAPI 3.0/3.1 на TypeScript без Java і запущеного сервера; безрантаймні типи; YAML/JSON локально або віддалено; імпорт
pathsіcomponents. - openapi-typescript — CLI — CLI розбирає локальні й віддалені схеми; прапорець
--check(типовоfalse) перевіряє актуальність згенерованих типів. - openapi-fetch — Getting Started — рекомендація тримати
tsc --noEmitокремим npm-скриптом і ганяти в CI, не покладаючись на лінтер чи команду збірки.
- Swagger UI — офіційна сторінка інструмента (SmartBear) — сторінка генерується автоматично з опису, тож показує вміст специфікації, а не поведінку сервера.
- OpenAPI Specification — перелік
responsesне зобовʼязаний покривати всі коди — сам факт незадокументованого коду порушенням стандарту не є. - JSON Schema Core, draft 2020-12 — без явного
additionalProperties: falseзайві поля дозволені й валідацію проходять. - JSON Schema Validation, draft 2020-12 —
requiredперевіряє лише наявність імені властивості, аformatтипово нічого не стверджує.
Що таке OpenAPI і навіщо він потрібен тестувальнику?
OpenAPI Specification (OAS) — це формальний машиночитний опис HTTP API у форматі YAML або JSON: у ньому зібрані всі операції, їхні параметри, схеми запитів і відповідей, й правила авторизації. Файл живе в репозиторії разом із кодом і виконує роль єдиного джерела істини (source of truth). Для QA це не «ще одна дока», а готовий каркас тест-аналізу: контракт показує повний обсяг операцій та обмежень, тож будується від нього, а не від памʼяті — «перевірив усе, що обіцяно», замість «перевірив те, що згадалось». Плюс зі специфікації інструменти автоматично піднімають mock-сервер і валідують реальні відповіді проти схем. Практичний наслідок: замість перепитувань у розробника «а що цей ендпоінт має повертати» ти читаєш контракт і одразу бачиш обсяг перевірок.
Чим OpenAPI відрізняється від Swagger?
OpenAPI — це стандарт (сама специфікація), а Swagger — екосистема інструментів навколо нього: Swagger UI, Swagger Editor, Swagger Codegen. Плутанина історична: формат народився під іменем Swagger, у 2015-му SmartBear віддала специфікацію OpenAPI Initiative під Linux Foundation, і починаючи з 3.0 стандарт носить імʼя OpenAPI, а бренд Swagger лишився на інструментах SmartBear. Тож коли колега каже «глянь у Swagger», ідеться зазвичай про Swagger UI, який рендерить вашу специфікацію. На співбесіді цим питанням перевіряють охайність термінології — точна відповідь одразу додає балів.
Яка зараз актуальна версія стандарту і що не так зі старим Swagger 2.0?
Актуальна — 3.x: у роботі найчастіше зустрінеш OpenAPI 3.0 (2017) і 3.1 (2021); восени 2025-го опубліковано 3.2. Swagger 2.0 — та сама специфікація до перейменування (офіційно OpenAPI 2.0), і на легасі-проєктах вона досі жива. Знати номер версії корисно не для педантизму: між 3.0 і 3.1 є практичні відмінності, які впливають на читання схем. Наприклад, у 3.0 «поле може бути null» позначається окремим ключовим словом nullable: true, а в 3.1 сумісність із JSON Schema повна, і null описується інакше. Тому перше, на що варто глянути у файлі, — рядок openapi: з версією.
З яких основних блоків складається специфікація?
Ключові блоки, з якими працює QA, — paths, parameters, responses, components/schemas і посилання $ref. paths — центральний блок: у ньому всі шляхи API і HTTP-методи на кожному з них. parameters описує все, що приходить поза тілом запиту: параметри шляху, query-параметри, заголовки. responses перелічує задокументовані коди статусів і схему тіла для кожного. components/schemas — словник моделей даних, а $ref дозволяє посилатися на вже описаний фрагмент замість копіювання. Верхньорівнево є ще блок info (назва, версія API), а схеми авторизації описують у components/securitySchemes. Читаючи незнайому специфікацію, зазвичай ідуть згори вниз: спочатку paths — щоб побачити мапу API, потім у кожну операцію — за параметрами, тілом і відповідями.
Що таке операція (operation) у термінах OpenAPI?
Операція — це пара «шлях × метод»: наприклад, GET /users/{id} і DELETE /users/{id} — це дві різні операції на одному шляху. У специфікації вони живуть у блоці paths: під кожним шляхом перелічені HTTP-методи, а під кожним методом — його параметри, тіло запиту й відповіді. Ключова думка для тестування: paths дає готову мапу покриття — кожна пара «шлях × метод» має бути викликана хоча б раз. А от звичне «чого немає в paths, того не існує» — це домовленість команди: сам стандарт про операції поза описом нічого не стверджує й прямо дозволяє лишати частини API неописаними. Коли команді треба показати покриття API, рахують саме операції, а не абстрактні «фічі».
Що описує блок parameters і які бувають типи параметрів?
parameters описує все, що приходить у запит поза його тілом, і в OpenAPI кожен параметр має поле in, яке каже, звідки він береться. Розташувань пʼять: path — сегменти шляху на кшталт {id} у /users/{id}, query — усе після знака питання в URL (фільтри, сторінки ), querystring, header — заголовки запиту, і cookie. Параметр унікальний за парою «імʼя + розташування», тож id у шляху й id у query — два різні параметри з окремими перевірками. У кожного параметра є тип, ознака required і часто обмеження: enum із допустимими значеннями, minimum/maximum, pattern. Для QA це прямий генератор тестів: на кожен параметр — позитив із валідним значенням і негативи (пропущений обовʼязковий, чужий тип, значення поза enum, вихід за межі). Обмеження в описі параметра — це фактично готовий список меж, які треба перевірити.
Навіщо у специфікації responses, якщо є код застосунку?
responses — це задокументований перелік очікуваних результатів операції: які коди статусів вона може повернути і яка схема тіла в кожного. Це важливо саме тому, що описує намір, а не факт: якщо для операції задокументовані 200, 404 і 422, то кожен із цих сценаріїв має бути відтворюваним, і завдання QA — знайти вхідні дані, які дадуть кожен код. Тобто responses перетворюється на список тест-сценаріїв: як добитися 200, як спровокувати 404, як отримати 422. Дзеркальний бік: якщо замість задокументованого коду приходить інший (у доці 400, у житті 500) — це вже знахідка. При цьому сам перелік у responses не зобовʼязаний покривати всі можливі коди — стандарт вимагає описати успішну відповідь і відомі помилки, а не все на світі. Код застосунку відповість на питання «як воно поводиться», а специфікація — «як воно має поводитися», і різниця між ними і є дефект.
Що таке components/schemas і як вони пов'язані з JSON Schema?
components/schemas — це словник моделей даних: кожна схема описує форму обʼєкта (які поля, яких типів, які обовʼязкові, які обмеження). Синтаксис схем — це діалект JSON Schema, стандарту опису структури даних. В OpenAPI 3.1 сумісність із JSON Schema повна, а в 3.0 є дрібні відмінності — найпомітніша та сама історія з nullable: true замість звичного для JSON Schema способу описати null. Для QA схема — це готовий набір перевірок: поле в required має бути присутнім, format: email вимагає негативу з невалідною адресою, maxLength: 50 дає пару граничних значень. Ба більше, ту саму схему можна згодувати валідатору (наприклад, Ajv) і автоматично звіряти з нею реальні відповіді.
Що робить $ref і чому він важливий для оцінки обсягу регресії?
$ref — це посилання на вже описаний фрагмент замість його копіювання: рядок $ref: '#/components/schemas/User' каже «тут та сама схема User, дивись у components». Сенс — не дублювати опис моделі: одну модель підключають у десятках операцій, тож правка в одному місці розходиться по всьому контракту. Саме тому $ref — важливий сигнал для оцінки : після правки схеми User під перевірку потрапляє кожен , що на неї посилається, — це і є обсяг перетестування. По суті $ref звʼязує схеми в , і читання цього графа економить час: не треба вгадувати, що ще могло зламатися. Тому при зміні спільної моделі перше питання QA — «хто на неї посилається».
Що таке Swagger UI і чим корисна (та небезпечна) кнопка Try it out?
Swagger UI — це вебінтерфейс поверх файлу специфікації: він перетворює YAML/JSON на інтерактивну сторінку з переліком операцій, схемами й прикладами; на стендах його зазвичай шукають за шляхами типу /swagger чи /api-docs. Найцінніше в ньому — Try it out: вводиш параметри, натискаєш Execute — і UI шле реальний запит на стенд та показує живу відповідь; це найшвидший спосіб познайомитися з невідомим API, не відкриваючи Postman чи curl. Але є два застереження. Перше: запити не іграшкові — деструктивний DELETE зі спільного стенда зітре дані так само, як з будь-якого іншого клієнта. Друге: схеми і приклади на сторінці беруться з файлу специфікації, а не з поведінки сервера, тож їхню правдивість ще належить перевірити. Тобто Swagger UI чудовий для розвідки й разових перевірок, але системну роботу краще вести в Postman або в коді.
Як зі специфікації системно народжуються тест-кейси?
Кожен блок специфікації відповідає на своє тестове питання, тож специфікація читається як готовий чек-лист. paths дає мапу покриття: кожна операція має бути викликана хоча б раз. parameters дає позитиви (валідні значення) і негативи (пропущений обовʼязковий, чужий тип, значення поза enum, вихід за межі). requestBody разом зі схемами дає , відсутні обовʼязкові поля й невалідні формати. responses дає окремий сценарій на кожен задокументований статус. securitySchemes дає запит без токена і з невалідним токеном. Бонус такого підходу — вимірність: «кожна операція з paths викликана, кожен задокументований статус відтворений, негативи за обмеженнями схем пройдені» — таку заявку на покриття не соромно показати команді, на відміну від безпредметного «я протестував API».
Схема User має email з format:email і name з maxLength:50. Які тести це диктує?
Схема сама диктує перевірки, і кожне обмеження — це межа, де живуть баги. email із format: email вимагає негативного тесту з рядком без «собачки» — валідатор має його відхилити. name із maxLength: 50 — підручникова пара меж: рядок із 50 символів сервер приймає, з 51 — відхиляє. Якщо id та email стоять у required, а name ні — контракт дозволяє відповідь без name, і мусить бути до цього готовий, тож потрібен і тест на присутність обовʼязкових полів, і сценарій із відсутнім опціональним. Тобто з трьох рядків схеми вже вимальовується позитив, негатив на формат, пара граничних значень і перевірка обовʼязковості. Саме тому читати схему вигідно до написання тестів — вона економить вигадування кейсів.
Поле не в required. Це означає, що тестувати там нічого?
Навпаки — опціональність це окрема гілка поведінки, яку треба перевірити свідомо. Якщо поле не в required, то відповідь без нього валідна за контрактом, а отже, весь код, який це поле споживає, мусить коректно переживати його відсутність. Опціональні поля — класичний розсадник null-багів: фронтенд чи інший сервіс очікує рядок, а отримує undefined/null і падає. Тому на опціональне поле варто мати як мінімум два сценарії: коли воно є і коли його немає. Помилка «поле не обовʼязкове, тестувати нічого» — типова пастка джуна; сильний кандидат бачить тут окрему поведінку, а не порожнє місце.
Навіщо машиночитна специфікація, якщо вже є сторінка у вікі?
Тому що вікі вміє читати тільки людина, а машиночитний файл породжує узгоджені між собою артефакти. З одного YAML/JSON інструменти автоматично генерують Swagger UI (документацію), mock-сервер (відповіді за схемами ще до готового бекенда), клієнтський код і типи, а також валідацію відповідей у тестах — і всі вони гарантовано узгоджені, бо мають спільне джерело. Вікі ж нічого не породжує і дрейфує мовчки: її написали пів року тому, і ніщо не сигналить, що вона розійшлася з реальністю. Специфікація теж може відставати, але вона хоча б формальна: розбіжність зі схемою можна зловити автоматично. На співбесіді цим питанням перевіряють, чи розумієш ти саму ідею source of truth, а не просто «дока в новому форматі».
Реальна відповідь API не збігається зі специфікацією. Твої дії?
Це процесне питання, і від тебе чекають не «підлаштую тест», а дисципліни фіксації. Спочатку зафіксувати обидві сторони розбіжності: що обіцяно у специфікації і що реально прийшло (код, поле, тип). Потім зʼясувати, що в команді вважається джерелом істини — специфікація чи код, бо від цього залежить, чий це баг. Далі завести дефект із доказами обох сторін і винести на команду. Найгірший варіант — мовчки переписати під факт: тоді специфікація й далі обіцятиме неправду кожному наступному читачеві, і сенс контракту втрачається. Тобто розбіжність — це завжди дефект, питання лише чий (коду чи доки), і розвʼязувати цю суперечку тестувальник не мусить самотужки — його робота зафіксувати й показати.
Чим відрізняються підходи design-first і code-first і як це впливає на те, чий баг розбіжність?
У підході design-first специфікацію пишуть до коду — вона і є узгодженим контрактом, за яким потім реалізують API. Тоді розбіжність між специфікацією і реальністю майже завжди баг реалізації: код не дотягнув до контракту. У підході code-first специфікацію породжують з анотацій просто в коді: відставати вона не може, зате чесно фіксує й помилки реалізації, тож «істина» розмивається — файл описує те, що є, а не те, що мало б бути. Практичний наслідок для QA: знаючи підхід команди, ти одразу розумієш, куди дивитися. При design-first довіряєш специфікації як контракту й заводиш баг на код; при code-first специфікація — слабший аргумент, і розбіжність частіше означає, що треба уточнювати очікувану поведінку. Але в обох випадках фіксувати розбіжність — робота тестувальника.
Що таке дрейф схеми (schema drift)?
— це поступове розходження між задокументованою схемою і реальними відповідями API, яке накопичується непомітно: додали поле в код і забули в специфікацію, змінили тип, прибрали поле з відповіді. Особливо підступно те, що вручну дрейф не ловиться: зникле поле серед десятків рядків JSON око просто не вихоплює. Класичний сценарій — коли дрейф ловлять не тести, а інша команда у своєму продакшн-коді: вона покладалася на поле, якого вже немає. Саме тому звірку відповідей зі схемами автоматизують, а системна боротьба з дрейфом між командами — це вже територія . Для QA дрейф — головний аргумент на користь автоматичної валідації проти схеми в кожному тесті.
Як автоматично перевірити, що відповідь API відповідає схемі?
Реальну відповідь проганяють через валідатор JSON Schema, і в JavaScript/TypeScript стандартний інструмент — Ajv. Схему беруть із components/schemas специфікації, а в тесті, окрім звичних функціональних перевірок, віддають тіло відповіді валідатору й асертять, що воно валідне. У Playwright це виглядає так: отримав відповідь, ajv.validate(userSchema, await response.json()), і expect(valid).toBe(true), а в повідомлення асерту зручно покласти ajv.errors, щоб одразу бачити, яке саме поле не збіглося. Сенс у тому, що така перевірка ловить дрейф контракту, який не видно очима, — зниклі поля, змінені типи, порушені формати. Це , яку додають до кожного API-тесту майже даром, бо схема вже описана у специфікації.
Навіщо у схемі валідації потрібен additionalProperties: false?
Тому що дефолтна поведінка JSON Schema — пропускати поля, яких у схемі немає, без жодного попередження. additionalProperties: false вмикає сувору перевірку — будь-яке поле, якого немає в схемі, робить відповідь невалідною. Для QA це критично: саме через відсутність цієї опції у відповідях непомітно випливають зайві поля, зокрема з персональними даними, які туди потрапили випадково. Класичний баг безпеки — коли ендпоінт починає віддавати внутрішнє поле (хеш пароля, службовий ), а тест цього не ловить, бо схема без additionalProperties: false на зайве поле не реагує. Тобто ця опція перетворює валідацію зі «все обовʼязкове на місці?» на «і нічого зайвого немає?». Ставити її треба свідомо там, де контракт справді закритий.
Схема пройшла — чи означає це, що відповідь правильна?
Ні: схема перевіряє форму, а не зміст. Якщо API повернув дані іншого користувача, але email там синтаксично коректний, валідатор не заперечить: тип правильний, формат правильний, поле присутнє — а дані чужі. Схемна валідація страхує від дрейфу контракту (зниклі поля, змінені типи), але функціональних перевірок бізнес-логіки не замінює. Тому в тесті поруч зі схемною валідацією мають жити перевірки конкретних значень: що повернувся саме той користувач, якого запитали, з очікуваними даними. Плутати ці два рівні небезпечно: «схема зелена» заспокоює, але не гарантує, що API повернув правильну відповідь по суті. Сильний кандидат на співбесіді цю межу проговорює одразу.
Три кейси, які проходить тестувальник із живою специфікацією: перетворити фрагмент OpenAPI на матрицю тест-кейсів, автоматично звірити реальну відповідь зі схемою через Ajv (і зловити зайве поле) та коректно оформити розбіжність між докою й реальністю. Скрізь — що дивитися і чому.
Кейс 1. Від фрагмента специфікації до матриці тест-кейсів
Тобі дали новий і фрагмент специфікації. Замість «повикликаю та подивлюсь» читаємо контракт і виводимо перевірки по блоках.
paths:
/users:
post:
summary: Створити користувача
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NewUser'
responses:
'201': { description: Створено }
'409': { description: email уже зайнятий }
'422': { description: Не пройшло валідацію }
components:
schemas:
NewUser:
type: object
required: [email, role]
properties:
email: { type: string, format: email }
role: { type: string, enum: [admin, user, guest] }
name: { type: string, maxLength: 50 }
Кожен рядок схеми — це готовий рядок тест-плану. required: [email, role], format: email, enum на role, maxLength: 50 на name і три задокументовані статуси перетворюються на матрицю:
| Джерело у специфікації | Тест-кейс | Очікування |
|---|---|---|
responses: 201 | валідне тіло з email + role | 201, у відповіді новий ресурс |
required: [email] | тіло без email | 422 |
required: [role] | тіло без role | 422 |
format: email | email без «собачки» ("petro.example.com") | 422 |
enum на role | role: "superadmin" (поза списком) | 422 |
maxLength: 50 | name рівно 50 символів | 201 |
maxLength: 50 | name 51 символ | 422 |
responses: 409 | email, який уже існує | 409 |
опціональність name | тіло без name | 201 (поле не обовʼязкове) |
Що дивитися і чому:
requiredіenumдають негативи майже даром. Не треба вигадувати кейси — специфікація сама каже, де межа: пропущене обовʼязкове поле, значення позаenum, перевищена довжина.maxLength: 50— це пара граничних значень, а не одне. 50 проходить, 51 — ні; перевіряти треба обидва боки межі, бо баги живуть саме на переході.- Опціональне
name— окремий рядок плану. Тіло без нього має дати201; це перевірка, що API справді вважає поле необовʼязковим, а не «забули згадати в required». - Кожен статус із
responses— окремий сценарій.409не зʼявиться сам — його треба спровокувати дублемemail; якщо відтворити не вдається, це вже питання до контракту.
Кейс 2. Playwright + Ajv: звірити відповідь зі схемою і зловити зайве поле
Функціональний перевіряє значення, схемна валідація — форму. Разом вони ловлять і логічні баги, і дрейф контракту. Схему беремо з components/schemas, а additionalProperties: false додаємо свідомо, щоб зловити поля, яких у контракті бути не має.
import { test, expect } from '@playwright/test';
import Ajv from 'ajv';
const userSchema = {
type: 'object',
required: ['id', 'email'],
properties: {
id: { type: 'integer' },
email: { type: 'string' },
name: { type: 'string', maxLength: 50 },
},
additionalProperties: false, // жодних не описаних у контракті полів
};
test('GET /users/1 — і форма, і зміст', async ({ request }) => {
const res = await request.get('/api/users/1');
expect(res.status()).toBe(200);
const body = await res.json();
// 1) форма: відповідь відповідає схемі
const ajv = new Ajv();
const valid = ajv.validate(userSchema, body);
expect(valid, JSON.stringify(ajv.errors)).toBe(true);
// 2) зміст: повернувся саме той користувач (схема цього не перевіряє)
expect(body.id).toBe(1);
});
Уяви, що бекенд випадково почав віддавати службове поле passwordHash. Функціональний асерт body.id === 1 цього не помітить — id правильний. А Ajv через additionalProperties: false впаде з ajv.errors, де прямо вказано зайве поле:
[{ "keyword": "additionalProperties",
"params": { "additionalProperty": "passwordHash" },
"message": "must NOT have additional properties" }]
Що дивитися і чому:
- Без
additionalProperties: falseвитік поля пройшов би мовчки. За замовчуванням JSON Schema дозволяє додаткові поля; саме так персональні чи службові дані непомітно випливають у публічні відповіді. ajv.errorsу повідомленні асерту — це діагноз замість «щось не так». Одразу видно, яке поле зайве або який тип не збігся, без ручного порівняння JSON.- Схема і функціональний асерт перевіряють різне. Прибереш
expect(body.id).toBe(1)— і чужий користувач правильної форми пройде; прибереш Ajv — і дрейф контракту (зниклий/зайвий/перейменований ключ) залишиться непоміченим. - Не вішай
additionalProperties: falseнаосліп на все. На відкритих, розширюваних контрактах зайве поле може бути легітимним; сувору перевірку ставлять там, де контракт справді закритий.
Кейс 3. Дока бреше: як оформити розбіжність
Тест на POST /users із дубльованим email очікує 409 (так у responses), а сервер повертає 500 зі стектрейсом. Спокуса — переписати асерт на 500 і йти далі. Це найгірший варіант: контракт продовжить брехати наступному.
Правильний хід — зафіксувати обидві сторони й винести на команду:
Ендпоінт: POST /users
Сценарій: email, який уже існує в базі
Очікувано: 409 (responses у openapi.yaml)
Фактично: 500 Internal Server Error, unhandled exception
Стектрейс: UniqueConstraintError у UsersController#create
Далі рішення залежить від того, як команда працює з контрактом:
| Підхід | Що є джерелом істини | Куди дефект |
|---|---|---|
| design-first | специфікація — узгоджений контракт до коду | баг реалізації: код має віддавати 409, а не падати |
| code-first | специфікація генерується з коду | усе одно баг: 500 на дубль — пропущена обробка, але й контракт варто оновити |
Що дивитися і чому:
500на передбачуваний конфлікт — щонайменше півбаг бекенда. Добре спроєктований сервер відповідає свідомим409/422, а не падає з необробленим винятком; тригер (дубльemail) прийшов від клієнта, але реакція сервера — окремий дефект.- Фіксуй обидві сторони, а не одну. «Очікувано
409за специфікацією, фактично500зі стектрейсом» — це доказ; «ендпоінт падає» — ні. - Не вирішуй суперечку контракту самотужки. Твоя робота — показати, що обіцяно і що прийшло; чиє це джерело істини (специфікація чи код) — рішення команди, але без твоєї фіксації розбіжність просто тихо доживе до продакшену.
- Мовчки підігнати тест під факт = знищити контракт. Асерт на
500зробить тест зеленим, але узаконить баг і залишить наступного читача специфікації з брехливою обіцянкою409.
OpenAPI vs Swagger і навіщо специфікація
- Можу розвести терміни: OpenAPI — стандарт (сама специфікація), Swagger — екосистема інструментів (UI, Editor, Codegen); формат починався як Swagger, 2015 року перейшов до OpenAPI Initiative, з версії 3.0 зветься OpenAPI.
- Розумію, що специфікація — машиночитний контракт у YAML/JSON у репозиторії як єдине джерело істини (source of truth); з неї генеруються Swagger UI, mock-сервер, клієнти й типи, валідація — усі узгоджені, бо джерело спільне.
- Знаю актуальні версії: 3.0 (2017), 3.1 (2021), 3.2 (2025); Swagger 2.0 = OpenAPI 2.0 — легасі.
Структура специфікації
- Розумію, що операція (operation) — це пара «шлях × метод», і що стандарт нічого не стверджує про операції, яких в описі немає: «немає в
paths— не існує» — трактування команди, а не висновок зі стандарту. - Знаю, що
parametersописує все поза тілом запиту через полеin, і що розташувань пʼять (path,query,querystring,header,cookie), а параметр унікальний за парою «імʼя + розташування»; обмеження, що диктують негативи, —required,enum,minimum/maximum,pattern,format,maxLength. - Розумію, що
responses— задокументовані коди статусів зі схемою тіла, тобто список сценаріїв, кожен з яких треба вміти відтворити. - Знаю, що
components/schemas— словник моделей на діалекті JSON Schema; у 3.1 сумісність повна, у 3.0 null позначається окремимnullable: true. - Можу пояснити
$ref: посилання замість копіювання; зміна однієї схеми зачіпає всі операції, що на неї посилаються, — готовий обсяг .
Swagger UI
- Розумію, що Swagger UI — лише рендер файлу специфікації, часто доступний на стенді за
/swaggerабо/api-docs; системну роботу краще вести в Postman або в коді. - Знаю про кнопку Try it out і два застереження: запити справжні (
DELETEреально видаляє), а показані схеми — це вміст специфікації, а не поведінка сервера.
Від специфікації до тест-кейсів
- Можу побудувати мапу з
paths: кожна операція викликана хоча б раз. - Виводжу негативи з обмежень: пропущений обовʼязковий параметр, чужий тип, значення поза
enum, вихід за межі, неваліднийformat. - Роблю окремий сценарій на кожен задокументований статус у
responses(200,404,422), а зsecuritySchemes— запит без токена і з невалідним. - Розумію, що опціональне поле (не в
required) — це окрема поведінки й гніздо null-багів; покриття формулюю вимірно («усі операції + усі статуси + негативи за обмеженнями»), а не «я протестував API».
Коли дока розходиться з реальністю
- Знаю типові розбіжності: зайве/зникле поле, не той статус, що описаний, розбіжність типу,
required-поле буваєnull,required: trueне спрацьовує — майже кожна це дефект, питання лише чий; при цьому перелікresponsesне зобовʼязаний покривати всі можливі коди, тож обовʼязковими є успішна відповідь і відомі помилки. - Можу пояснити різницю design-first (специфікація — контракт, розбіжність = баг коду) і code-first (специфікація генерується з коду й документує його ж помилки).
- Знаю правильний процес: зафіксувати обидві сторони, зʼясувати джерело істини в команді, завести дефект — а не мовчки підганяти тест під факт.
- Розумію, що таке (schema drift) і чому очима він не ловиться.
Валідація відповідей проти схеми
- Можу пояснити, навіщо кожен автотест проганяє тіло відповіді через валідатор JSON Schema (в JS/TS — Ajv), і навіщо
additionalProperties: false: без нього зайві поля (зокрема з персональними даними) проходять валідацію мовчки. - Розумію межу валідації: вона перевіряє форму (типи, структуру, обовʼязковість), а не зміст — чужі дані правильної форми пройдуть; тому вона доповнює, а не замінює функціональні перевірки значень.
Квіз
Перед стартом
- Питань: 12
- Поріг «зараховано»: ≥70% правильних відповідей.
- Результат впливає на прогрес; завалені питання підуть у чергу повторення.
- Квіз впливає на компліт теми: тема стає «пройдено», лише коли прочитано теорію І квіз складено на ≥70%.
Питання
OpenAPI vs Swagger — у чому різниця?


