vyvchy
    Теми розділу

    04 · API-тестування

    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-файлу інструменти автоматично породжують цілу низку артефактів — і всі вони гарантовано узгоджені між собою, бо мають спільне джерело:

    openapi.yaml
    специфікація в репозиторії

    Swagger UI
    інтерактивна документація

    Mock-сервер
    відповіді за схемами

    Кодогенерація
    клієнти, типи, серверні стаби

    Валідація відповідей
    у тестах

    Контракт
    між командами

    openapi.yaml
    специфікація в репозиторії

    Swagger UI
    інтерактивна документація

    Mock-сервер
    відповіді за схемами

    Кодогенерація
    клієнти, типи, серверні стаби

    Валідація відповідей
    у тестах

    Контракт
    між командами

    Для тестувальника з цього списку два пункти особливо смачні. По-перше, зі специфікації можна підняти 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

    Структура специфікації: 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: специфікація очима людини

    Від специфікації до тест-кейсів

    • OpenAPI Specificationpaths як мапа операцій і responses як перелік задокументованих статусів — готовий каркас покриття.
    • JSON Schema Validation, draft 2020-12 — кожне обмеження схеми дає межу: maxLength порівнює «менша або рівна», тож 50 валідне, а 51 — ні; required стежить лише за наявністю імені.

    Коли дока бреше: розбіжності специфікації й реальності

    • OpenAPI Specificationresponses не зобовʼязаний покривати всі можливі коди: описаними мають бути успішна відповідь і відомі помилки.
    • 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-12required перевіряє лише наявність імені властивості, а format типово нічого не стверджує.

    Пояснення

    «Поясни» працює з власним API-ключем Anthropic: запит іде з вашого браузера прямо до Anthropic.

    Свій API-ключ (BYOK)

    Вставте власний ключ Anthropic — пояснення працюватиме на реальній моделі. Ключ зберігається лише у вашому браузері (localStorage), нікуди не надсилається, крім api.anthropic.com, і не логується.