JavaRush /Курси /Java Server /Negative-path запити в Postman

Negative-path запити в Postman

Java Server
Рівень 13, Лекція 2
Відкрита

1. Negative-path як частина контракту

Якщо чесно, happy-path — це найпопулярніший спосіб мислення початківця-розробника. Приблизно так: «Я відправив запит, отримав дані, усе гарно — значить, API працює». Проблема в тому, що в реальному житті користувачі, мережа й зовнішні сервіси дуже рідко поводяться так само дисципліновано, як ваш демонстраційний запит у Postman.

Negative-path — це усвідомлена перевірка меж контракту. Ми навмисно робимо запит «не так»: передаємо порожній параметр, неіснуючий ідентифікатор, дивний ліміт, неправильний формат. І дивимося, що робить сервіс. Не для того, щоб «зловити його на помилці», а щоб зрозуміти: як сервіс повідомляє про проблему і який формат відповіді можна вважати частиною договору.

Є одна важлива психологічна пастка, від якої хочеться одразу позбутися. У звичайному мовленні слово «помилка» означає «усе погано». У контрактному мисленні «помилка» часто означає «очікувана гілка». Наприклад, запит «деталі книги за неіснуючим ID» майже зобов’язаний повернути 404 Not Found або щось еквівалентне за змістом. Це не «зламалося», а «сервіс чесно сказав: такого ресурсу немає».

Чому це важливо для нашого проєкту ReadLater Starter саме зараз, ще на етапі Postman? Тому що згодом, не сьогодні, ви будете писати код клієнта, а код має розуміти щонайменше три різні наслідки:

  1. усе гаразд і дані є;
  2. усе гаразд, але даних немає (порожній результат);
  3. сервіс повідомляє, що запит некоректний, ресурсу немає або стався конфлікт, тобто статус і/або тіло відповіді вказують на проблему.

Якщо ви заздалегідь не побачили ці гілки через Postman, то в коді будете навмання вгадувати, що означає «порожня відповідь» і чому іноді прилітає 404, а іноді 200 із порожнім списком.

2. Negative-path і мережеві збої

Одна з найчастіших пасток початківця — вважати negative-path «будь-яким невдалим результатом». У підсумку людина вимикає Wi‑Fi, бачить у Postman Could not get any response і радісно каже: «Ось він, негативний сценарій!». Це корисний життєвий досвід, але це не перевірка контракту, а перевірка того, що ви вмієте страждати.

Negative-path — це те, що можна відтворити передбачувано і пояснити двома фразами: «Я відправив такий-то запит із таким-то входом, сервіс повернув такий-то статус і таку-то форму відповіді». Мережевий збій — це окрема категорія транспортних проблем, а ми зараз говоримо про межі HTTP/JSON-договору.

Щоб не плутатися, тримайте в голові просту схему. Вона не академічна, а практична: допомагає швидко зрозуміти, що саме ви спостерігаєте в Postman.

flowchart TD
    A["Відправили запит"] --> B{"Отримали HTTP-відповідь?"}
    B -- ні --> N["Мережевий збій: timeout / DNS / немає зʼєднання"]
    B -- так --> C{"Status 2xx?"}
    C -- так --> D{"Дані \"за змістом\" є?"}
    D -- так --> HP["Happy-path: успіх + корисні дані"]
    D -- ні --> EP["Граничний сценарій: успіх + порожній результат"]
    C -- ні --> ER["Negative-path: 4xx/5xx як частина контракту"]

Зверніть увагу на гілку «успіх + порожній результат». Вона часто ламає мозок. За HTTP усе чудово (200 OK), а за змістом — нічого не знайдено. І це нормально. Це не обов’язково помилка; інколи це цілком коректна контрактна відповідь. Особливо для пошуку.

Щоб зробити різницю зовсім «в лоб», ось невелика таблиця, яку зручно тримати поруч, поки ви навчаєтесь:

Що сталося Як виглядає в Postman Це про контракт? Що ми робимо зараз
Сервіс не відповів Could not get any response, timeout, помилка з’єднання Скоріше ні Не змішуємо з negative-path, фіксуємо як окремий операційний факт
Сервіс відповів 4xx/5xx Є код статусу і заголовки, часто є body Так Вивчаємо статус і форму відповіді як частину договору
Сервіс відповів 2xx, але даних немає 200 OK, body порожній за змістом (наприклад, items: []) Так Фіксуємо поведінку як «граничний сценарій», не називаємо це «зламалося»

3. Відтворювані negative-path сценарії

Negative-path легко перетворити на хаос: можна нагенерувати сто запитів, половина з яких «інколи працює». Нам так не треба. Нам потрібні сценарії, які ви зможете запустити через тиждень і отримати приблизно таку саму поведінку, без шаманства й ворожіння на кавовій гущі. А каву, як відомо, краще пити, ніж ворожити на ній.

Головні критерії хорошого negative-path сценарію для зовнішнього API прості. Він має бути відтворюваним, безпечним для провайдера, без DDoS і без «ліміту 1 000 000», а ще — перевіряти конкретну межу: «що буде, коли запит порожній», «що буде, коли ресурсу немає», «що буде, коли параметр невалідний».

Нижче ми розберемо negative-path для двох базових сценаріїв каталогу: пошук і деталі. Ми спеціально говоритимемо не про те, «як правильно за REST», а про те, «як реально побачити поведінку зовнішнього API й зафіксувати її».

Negative-path для search: порожнеча й межі

Пошук — ідеальне місце для негативних сценаріїв, тому що він природно допускає ситуацію «нічого не знайдено». Саме тому важливо розділяти «помилку запиту» і «порожній результат». Якщо ви робите q=asdkjasdkjasd, сервіс може чесно відповісти 200 OK і порожнім списком — це не помилка, а відсутність збігів.

Ось кілька сценаріїв, які зазвичай добре працюють і не залежать від того, які саме книги є у провайдера. Формально це мінінабір, але вже він дає вам майже все потрібне розуміння.

Сценарій Приклад запиту Що ми очікуємо побачити
Порожній запит {{baseUrl}}/search?q=&limit={{limit}} Часто 400, інколи 200 із порожніми даними — залежить від провайдера
Запит із пробілів q= Часто трактується як порожній
«Сміттєвий рядок» q=asdkjasdkjasd Зазвичай 200 + порожній список/count=0
Граничний ліміт limit=0 або
limit=-1
Часто 400, інколи значення «спадає» до типового ліміту
Надто великий ліміт limit=9999 Або 400, або сервіс сам уріже ліміт

Тут важлива думка: ми не намагаємося заздалегідь вгадати правильну відповідь, ми намагаємося з’ясувати контракт. Якщо сервіс на порожній q= повертає 200 OK і порожній список — це дивно, але це факт. Для вас, як для майбутнього автора клієнта, це означає: «порожній q не призводить до 400, отже клієнт має валідувати це самостійно, інакше буде беззмістовний запит».

Negative-path для details: неіснуючий ID і неправильний формат

З деталями зазвичай простіше: у нас є один ресурс — книга — і її ідентифікатор. Negative-path тут часто виглядає як «цього ресурсу немає» або «ідентифікатор невалідний». Головне — вибрати ID так, щоб він точно не існував. Не «мені здається, що не існує», а «я вибрав завідомий маркер».

Найпрактичніший підхід — завести окрему змінну середовища, наприклад missingBookId, і покласти туди очевидний маркер:

# Змінна середовища для сценарію "книги точно немає"
missingBookId = DOES_NOT_EXIST

Тоді ваш negative-запит виглядає так:

{{baseUrl}}/books/{{missingBookId}}

Чому так краще, ніж «вручну в URL»? Тому що змінна відразу робить сенс сценарію очевидним. Коли через тиждень ви відкриєте колекцію, вам не доведеться згадувати, чому там був ID BK-000000. За назвою змінної все зрозуміло: це ID, якого немає.

Є ще один цікавий тип negative-path для details: невалідний формат. Наприклад, якщо провайдер очікує буквено-цифровий ID, можна перевірити щось на кшталт !!! або 123%. Деякі сервіси відповідають 400 Bad Request, деякі — 404 Not Found (бо їм простіше сказати «немає такого ресурсу», ніж пояснювати формат). І це теж частина контракту: інколи сервіс не розкриває, чи валідний формат, щоб не давати зайвої інформації.

І ще один важливий нюанс саме для нашої зв’язки «пошук → деталі». Якщо в середовищі є bookId, ви легко можете випадково обманути самі себе: негативний пошук нічого не знайшов, але bookId залишився від попереднього успішного пошуку, і деталі далі «ніби працюють». Це виглядає так, ніби chained-сценарій стабільний, хоча насправді він просто використовує старе значення.

Якщо ви вже використовуєте Tests у search-запиті, то найпростіший прийом — явно очищати bookId перед тим, як намагатися зберегти новий. Тоді «порожній пошук» не залишить у середовищі старий ID.

// Важливо: не даємо старому bookId "протекти" в наступний запуск сценарію
pm.environment.unset("bookId"); // прибираємо старе значення

const body = pm.response.json(); // читаємо JSON-відповідь

// Якщо щось знайшлося — зберігаємо id першої книги для chained-сценарію
if (body.items && body.items.length > 0) {
  pm.environment.set("bookId", body.items[0].id);
}

Цей шматок коду маленький, але він робить chained-сценарії чеснішими: якщо пошук нічого не повернув, ви це побачите не тільки очима, а й за тим, що bookId більше не існує.

4. Колекція без test2/test3

Коли колекція росте, вона легко перетворюється на музей випадкових експериментів: new request, new request (2), test, bad, bad2-final. Це нормально для перших 10 хвилин, але жахливо як робочий артефакт курсу. Negative-path особливо любить розмножуватися, тому тут дисципліна потрібна одразу.

Хороша колекція — це коли за назвами папок і запитів можна відновити сценарій, навіть не відкриваючи URL. Ми не намагаємося зробити ідеальний каталог тестів, ми просто хочемо, щоб ваша колекція була читабельною, як зміст: що перевіряємо і навіщо.

Зручний компромісний формат для нашого каталогу може виглядати так:

Каталог/
├─ Пошук/
│  ├─ happy-path / за запитом
│  ├─ negative / порожній-запит
│  ├─ negative / сміттєвий-запит
│  └─ edge / limit-zero
└─ Деталі/
   ├─ happy-path / за-id (використовує {{bookId}})
   ├─ negative / відсутній-id (використовує {{missingBookId}})
   └─ edge / невалідний-формат-id

Зверніть увагу на слово edge. Це не обов’язково, але часто допомагає відокремити явно поганий запит (negative) від граничного значення (edge). limit=0 — це не завжди помилка користувача, це інколи дивне, але можливе значення. У підсумку ви швидше розумієте, що саме перевіряли.

Ще одна практична порада: не використовуйте в negative-запитах змінні, які потрібні для happy-потоку, якщо це може ламати сценарій. Наприклад, {{bookId}} — це змінна, яку ми витягуємо з успішного пошуку. Для negative details значно безпечніше використовувати окрему {{missingBookId}}, щоб ви випадково не перезаписали «робочий» ID і не зламали собі ж smoke-набір.

5. Що порівнювати у відповідях negative-path

Є спокуса дивитися лише на body, бо «там же JSON». Але в HTTP контракт — це три речі одночасно: status, headers, body. І в negative-path це особливо важливо, бо body інколи взагалі може бути не JSON. Так, деякі сервіси люблять повертати HTML-сторінку помилки, і це теж реальність, навіть якщо нам це не подобається.

Коли ви фіксуєте negative-path, вам потрібно навчитися ставити собі однакові запитання. Не «що за дурниця прилетіла», а «як сервіс повідомляє про проблему». Для цього зручно тримати маленьку таблицю: що дивитися залежно від сценарію.

Тип negative/edge сценарію На що дивимося у відповіді Чому це важливо
Некоректний запит (наприклад, порожній q) status (часто 400), Content-Type, чи є body і яка його форма У коді клієнта ви маєте розрізняти «погане введення» і «нічого не знайдено»
Відсутній ресурс (неіснуючий ID) status (часто 404), чи є корисна помилка в body Клієнт має вміти сказати «книги немає», а не «усе зламалося»
Порожній результат пошуку 200, форма body (items/count), порожній список проти відсутнього поля Це нормальна гілка роботи пошуку, а не помилка транспорту
Граничні параметри (limit=0/-1/9999) status (400 або 200), чи не відбувається «тихе виправлення» параметра Ви дізнаєтесь, наскільки сервіс суворий і чи потрібна валідація на боці клієнта
Внутрішня помилка сервісу status (500/503), може бути текстова помилка Це вже ближче до операційного світу, але статус усе одно частина контракту

Давайте окремо проговоримо «порожній результат», бо він найчастіше викликає непорозуміння. Для пошуку «нічого не знайдено» — це цілком коректна відповідь, і найчастіше її роблять через 200 OK і порожній список. Приблизний shape може бути таким:

{
  "items": [],
  "count": 0
}

Це не означає, що запит поганий. Це означає, що запит коректний, просто збігів немає. І це важливо відрізняти від сценарію «запит невалідний», де частіше очікується 400 Bad Request.

Для details, навпаки, «нічого не знайдено» зазвичай означає, що ресурсу не існує, і тоді логічніше побачити 404 Not Found. Але зовнішні сервіси інколи обирають інші підходи. Ваше завдання на цьому етапі — не сперечатися з реальністю, а зафіксувати, яка реальність у провайдера.

6. Мінімальні перевірки в Postman Tests

Зараз важливо не скотитися в окремий курс про тестування API через Postman. Нам потрібен дуже легкий шар автоматичної перевірки, щоб negative-path був не «я колись бачив, що там було 404», а «я запускаю — і Postman підтверджує, що контракт не змінився». Це особливо корисно під час самостійного навчання: сьогодні ви подивилися, завтра забули, післязавтра провайдер змінився — і ви не розумієте, ви помилилися чи змінився світ.

У negative-path логіка проста: ми очікуємо певний статус і саме його перевіряємо. Не «щоб тест був зеленим», а щоб зафіксувати домовленість. Наприклад, запит із порожнім q (якщо ваш провайдер відповідає 400) можна оформити так:

pm.test("Порожній запит повертає 400", () => {
  // Фіксуємо контракт: саме для цього сценарію очікуємо 400
  pm.response.to.have.status(400);
});

Якщо ваш провайдер замість 400 повертає 200 і порожній список, тоді перевірка буде іншою, і це нормально:

// Дістаємо тіло відповіді, щоб перевірити форму результату
const body = pm.response.json();

pm.test("Пошук повертає порожні items", () => {
  // Важливо: перевіряємо структуру, а не точні тексти повідомлень
  pm.expect(body.items).to.be.an("array");
  pm.expect(body.items.length).to.eql(0);
});

Зверніть увагу: ми не перевіряємо точний текст повідомлень, бо це часто найнестабільніша частина. Ми перевіряємо форму: масив є, він порожній, отже «нічого не знайдено» виражено саме так.

Ще один невеликий, але корисний чек — Content-Type. Іноді на помилках сервіс зривається і віддає HTML. Якщо ви очікуєте JSON, це варто знати.

pm.test("Відповідь — JSON", () => {
  // Content-Type може бути відсутній, тому підстраховуємося порожнім рядком
  const ct = pm.response.headers.get("Content-Type") || "";

  // Фіксуємо очікування: хочемо JSON навіть у помилкових сценаріях (якщо провайдер так уміє)
  pm.expect(ct).to.include("application/json");
});

Якщо цей тест раптово падає на 500 і text/html, ви хоча б розумієте: «ага, на внутрішніх помилках сервіс віддає не JSON, отже в клієнті це потрібно враховувати». Не сьогодні, не в коді, а просто як факт контракту.

І, нарешті, повертаємося до chained-сценарію. Якщо ви використовуєте bookId із пошуку, намагайтеся не залишати в середовищі старі значення, інакше negative-path стане фальшивим. Ми вже показували прийом unset перед установленням. Він простий, але рятує від ілюзії «усе працює».

7. Типові помилки negative-path у Postman

Помилка №1: вважати negative-path «будь-яким червоним екраном» і змішувати його з мережевими збоями.
Якщо Postman пише Could not get any response, це майже завжди не про контракт, а про те, що ви не отримали HTTP-відповідь. Це важливо вміти спостерігати, але це інша категорія проблем. Negative-path починається там, де є status code і хоч якась передбачувана поведінка API.

Помилка №2: обирати негативні значення, які «інколи існують».
Наприклад, ви берете ID навмання і сподіваєтеся, що його немає. А потім він раптом з’являється або провайдер змінює дані, і ваш negative-case раптово стає happy-case. Набагато краще завести missingBookId = DOES_NOT_EXIST або інший очевидний маркер і використовувати його як завідомо відсутній.

Помилка №3: перевіряти тільки body й ігнорувати status/headers.
Зовнішній сервіс може повернути той самий JSON, але з різними статусами, або повернути помилку взагалі без JSON. Якщо дивитися тільки на body, ви втрачаєте половину контракту. У negative-path насамперед дивіться на статус, потім Content-Type, і лише потім на форму body.

Помилка №4: ламати happy-path змінні negative-запитами.
Якщо ви перезаписуєте bookId у середовищі «для перевірки missing-id», ви потім самі собі створюєте головоломку: чому ланцюжок «пошук → деталі» перестав працювати. Розділяйте змінні за змістом: bookId — для happy chained flow, missingBookId — для negative details.

Помилка №5: перетворювати negative-path на архів разових експериментів.
Запит «порожній q» має сенс лише тоді, коли він оформлений як сценарій, названий за змістом і лежить поруч із happy-path. Інакше це не частина колекції, а просто ваш особистий слід на піску, який зникне за кілька днів.

1
Задача
Java Server, 13 рівень, 2 лекція
Недоступна
Негативний сценарій для відсутнього ресурсу
Негативний сценарій для відсутнього ресурсу
1
Задача
Java Server, 13 рівень, 2 лекція
Недоступна
Пограничний сценарій пошуку без збігів
Пограничний сценарій пошуку без збігів
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ