Клієнт-сервер і як працює веб
Зміст
Уяви, що замовляєш каву. Ти (клієнт) кажеш бариста (сервер), чого хочеш; бариста готує й віддає результат. Ти не стоїш за стійкою і не вариш сам — лише формулюєш запит і отримуєш відповідь. Веб побудований на цій самій ідеї: одна сторона просить, інша відповідає. Для AQA це не абстракція, а щоденна робота: коли тест «не бачить» кнопку, коли запит падає з кодом 500, коли той самий сценарій зелений локально й червоний на CI — за кожним із цих випадків стоїть модель клієнт-сервер і те, як влаштований обмін даними в мережі.
Клієнт і сервер
Клієнт (client) — програма, яка ініціює запит і споживає відповідь. Найпоширеніший клієнт у вебі — браузер (browser): Chrome, Firefox, Safari. Але клієнтом може бути будь-що, що вміє надсилати мережеві запити: мобільний застосунок, утиліта curl у терміналі, Postman, твій автотест на Playwright чи API-клієнт на pytest.
Сервер (server) — програма, яка постійно «слухає» вхідні запити на певному порту й формує відповіді. Слово «сервер» має два значення, і їх часто плутають:
- фізична або віртуальна машина, де все крутиться («залізо»);
- програма-процес, яка обробляє запити (наприклад, вебсервер Nginx чи застосунок на Node.js).
Далі під «сервером» мається на увазі саме програма, якщо не сказано інакше.
Ключова властивість цієї моделі — асиметрія ролей. Клієнт знає, до кого звертатись (за адресою), а сервер зазвичай не знає наперед, хто до нього прийде. У класичній схемі «один запит — одна відповідь» сервер не ініціює обмін першим — він лише відповідає на те, що прийшло. Це важливо для тестів: якщо треба перевірити повідомлення, яке сервер «надсилає» клієнту сам (пуш, чат, живі оновлення), там уже працює інша технологія — WebSocket або Server-Sent Events, — бо в базовій моделі HTTP сервер сам заговорити не може.
| Роль | Приклади | Що робить |
|---|---|---|
| Клієнт | браузер, мобільний застосунок, curl, Postman, автотест | ініціює запит, обробляє відповідь |
| Сервер | Nginx, Apache, застосунок на Node/Python/Ruby/Java | слухає порт, обробляє запит, повертає відповідь |
Що відбувається від вводу URL до появи сторінки
Це головний «ланцюг» вебу — від натискання Enter до сторінки на екрані.
-
Розбір URL. Браузер розкладає адресу на частини. Візьмемо
https://shop.example.com/cart?id=42:https— схема (scheme), протокол обміну;shop.example.com— хост (host), доменне імʼя;/cart— шлях (path);?id=42— рядок запиту (query string).
-
DNS-резолюція (DNS lookup). Компʼютери спілкуються не за іменами, а за IP-адресами. Браузер має перетворити
shop.example.comна щось на кшталт93.184.216.34. Спершу перевіряються кеш браузера й операційної системи та файлhosts; якщо там нічого немає, запит іде до DNS-резолвера, який від імені клієнта опитує кореневі, потім TLD- (.com), а тоді авторитетні сервери домену. Це вже перша «подорож» у мережу. -
Встановлення зʼєднання (TCP handshake). Перш ніж передати дані, клієнт і сервер домовляються про зʼєднання через тристороннє рукостискання: SYN → SYN-ACK → ACK. Це окремий обмін пакетами, ще до будь-яких корисних даних.
-
Захищене зʼєднання (TLS handshake). Для
httpsдодається узгодження шифрування: сервер показує сертифікат, сторони узгоджують ключі. Це ще один обмін пакетами (у TLS 1.3 його скорочено до одного round-trip). -
HTTP-запит. Аж тепер браузер надсилає власне запит:
GET /cart?id=42 HTTP/1.1
Host: shop.example.com
Accept: text/html
User-Agent: Mozilla/5.0
-
Обробка на боці сервера. Запит може пройти через балансувальник навантаження (про нього нижче), потрапити на застосунок, той сходить у базу даних, збере дані й згенерує HTML.
-
HTTP-відповідь. Сервер повертає статус, заголовки й тіло:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 5120
<!doctype html><html>...</html>
-
Побудова сторінки. Браузер парсить HTML, будує дерево DOM (Document Object Model), знаходить у ньому посилання на CSS, JavaScript, зображення — і робить окремі запити на кожен підресурс. Далі будується CSSOM, застосовуються стилі, виконується JavaScript, відбувається рендеринг.
-
Динамічне довантаження. У сучасних застосунках (SPA — single-page application) JavaScript після завантаження часто робить ще запити до API вже без перезавантаження сторінки — і саме вони приносять дані, які бачить користувач.
Практичний висновок для AQA: коли пишеш очікування (wait), кроки 8 і 9 — головні джерела флаку (flakiness). HTML прийшов (крок 7), а даних на екрані ще немає, бо триває крок 9. Тому «сторінка завантажилась» ≠ «дані готові». Надійні очікування чіпляються за конкретний елемент чи за завершення мережевого запиту, а не за подію завантаження документа.
Модель «запит/відповідь»
Основний протокол вебу — HTTP (HyperText Transfer Protocol), його захищена версія — HTTPS. Обмін завжди має форму «один запит → одна відповідь».
Запит складається з: - методу (method) — що робимо; - шляху (path) — з чим; - заголовків (headers) — метадані; - тіла (body) — дані, які надсилаємо (не в усіх методів).
Відповідь складається з: - коду статусу (status code) — чим усе скінчилось; - заголовків; - тіла.
Наведений вище текстовий вигляд запиту й відповіді — це HTTP/1.1. HTTP/2 і HTTP/3 передають те саме у двійковому вигляді й дозволяють багато паралельних запитів в одному зʼєднанні, але семантика (метод, шлях, заголовки, статус) не змінюється — тому в DevTools ти бачиш звичні поля незалежно від версії.
Основні методи:
| Метод | Призначення | Має тіло запиту |
|---|---|---|
| GET | отримати ресурс | зазвичай ні |
| POST | створити ресурс / надіслати дані | так |
| PUT | замінити ресурс повністю | так |
| PATCH | частково оновити ресурс | так |
| DELETE | видалити ресурс | зазвичай ні |
| HEAD | як GET, але лише заголовки, без тіла | ні |
| OPTIONS | дізнатись доступні опції/методи | зазвичай ні |
Коди статусів згруповані за першою цифрою (1xx — інформаційні, 2xx — успіх, 3xx — перенаправлення, 4xx — помилка клієнта, 5xx — помилка сервера). Ось найчастіші, які реально трапляються в тестах:
| Код | Значення | Типова причина |
|---|---|---|
| 200 | OK | успіх |
| 201 | Created | створено ресурс (часто після POST) |
| 204 | No Content | успіх без тіла відповіді |
| 301 / 302 | Moved Permanently / Found | перенаправлення (redirect) |
| 304 | Not Modified | ресурс не змінився, беремо з кешу |
| 400 | Bad Request | клієнт надіслав некоректні дані |
| 401 | Unauthorized | не автентифікований (немає/невалідні креденшели) |
| 403 | Forbidden | автентифікований, але без прав |
| 404 | Not Found | ресурсу немає |
| 422 | Unprocessable Content (у старій специфікації — Unprocessable Entity) | дані синтаксично коректні, але не проходять валідацію |
| 429 | Too Many Requests | спрацював ліміт запитів (rate limiting) |
| 500 | Internal Server Error | помилка на боці сервера |
| 502 / 503 / 504 | Bad Gateway / Service Unavailable / Gateway Timeout | проблеми проксі/балансувальника чи перевантаження |
Серед перенаправлень варто памʼятати нюанс: 301/308 — постійні, 302/307 — тимчасові, а 307/308 додатково гарантують, що метод і тіло запиту збережуться при редіректі (при 301/302 деякі клієнти історично перетворюють POST на GET).
Різниця між 401 і 403 — класичне питання на співбесіді й часте джерело плутанини в баг-репортах: 401 означає «я не знаю, хто ти», 403 — «я знаю, хто ти, але тобі сюди не можна».
Нюанс. Чи віддає бекенд на валідаційні помилки саме 422 — рішення конкретного API, не універсальне правило: чимало проєктів для того самого випадку повертають 400. Звіряйся з контрактом свого бекенду.
Stateless і його наслідки
HTTP — stateless-протокол (без збереження стану). Це означає, що сервер за замовчуванням не памʼятає нічого про попередні запити. Кожен запит — чистий аркуш: сервер обробляє його так, ніби бачить клієнта вперше.
Чому так зробили? Заради масштабованості й простоти. Якщо сервер не мусить памʼятати клієнтів, то будь-який із десятка однакових серверів може обробити будь-який запит — не треба, щоб той самий клієнт завжди потрапляв на ту саму машину.
Практичні наслідки для тестів:
- Кожен запит має нести все потрібне сам. Хочеш звернутись до захищеного ендпоінта — прикладай токен чи кукі до кожного запиту, а не «один раз залогінься».
- Порядок і незалежність тестів. Оскільки протокол не тримає стану між запитами, легко зробити тести ізольованими. І навпаки: будь-який «спільний стан» у тестах ти створюєш сам (через дані в базі, кукі, глобальні фікстури), і саме він потім тече між тестами й дає флак.
- Ідемпотентність (idempotency). GET, PUT і DELETE можна безпечно повторити з тим самим результатом, а повторний POST може створити дубль. Це впливає на ретраї (retries): наївний ретрай POST-запиту в тесті здатен створити два записи.
Важливо: stateless — це про протокол, а не про застосунок. Сам застосунок майже завжди має стан (твій кошик, твій профіль). Просто цей стан живе не «в памʼяті сервера про зʼєднання», а в базі даних та в тому, що клієнт приносить із кожним запитом.
Як «памʼятається» логін між запитами
Якщо HTTP не памʼятає нічого, як тоді сайт памʼятає, що я залогінений? Відповідь: клієнт сам нагадує про себе в кожному запиті.
Найпоширеніший механізм — кукі (cookies). Схема така:
- Ти надсилаєш логін і пароль (зазвичай POST).
- Сервер перевіряє їх і у відповіді ставить кукі через заголовок
Set-Cookie:
HTTP/1.1 200 OK
Set-Cookie: session_id=a1b2c3d4; HttpOnly; Secure; SameSite=Lax; Path=/
- Браузер зберігає цю кукі й автоматично додає її до кожного наступного запиту на цей домен:
GET /account HTTP/1.1
Host: shop.example.com
Cookie: session_id=a1b2c3d4
- Сервер бачить
session_id, знаходить у себе (у базі чи сховищі сесій), кому він належить, — і «впізнає» тебе.
Атрибути кукі, важливі для тестування:
| Атрибут | Що робить | Чому важливо для AQA |
|---|---|---|
HttpOnly | забороняє доступ до кукі з JavaScript | таку кукі не витягнеш через document.cookie — керуй нею на рівні контексту браузера |
Secure | кукі шлеться лише через HTTPS | на локальному http:// така кукі може не встановитись |
SameSite | обмежує надсилання кукі в крос-сайтових запитах | ламає сценарії з редіректами між доменами й SSO |
Expires / Max-Age | час життя кукі | сесія «протухає» — джерело плаваючих падінь у довгих прогонах |
Важливий нюанс із SameSite: якщо атрибут не заданий явно, браузери на рушії Chromium (Chrome, Edge) трактують кукі як SameSite=Lax за замовчуванням, тоді як Firefox і Safari такого дефолту не застосовують. Тому в крос-доменних сценаріях не покладайся на «дефолтну» поведінку — дивись у DevTools, що саме прийшло в Set-Cookie, і за потреби задавай атрибут явно.
Альтернатива кукі — токени (tokens), найчастіше у форматі JWT, які клієнт зберігає сам (наприклад, у localStorage) і додає вручну в заголовок:
GET /api/account HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Ключова відмінність: кукі браузер підкладає автоматично, а токен із localStorage мусить додати сам код фронтенду. Тому кукі-сесія «переживає» перезавантаження сторінки без зусиль, а токен потрібно спершу дістати зі сховища.
Для автотестів ця різниця критична. Найшвидший і найстабільніший спосіб «залогінитись» у тесті — не проклацувати форму щоразу, а один раз отримати сесію (кукі чи токен) через API й підкласти її в контекст браузера. Це прибирає флак на сторінці логіну й економить час. Playwright для цього має механізм збереження стану автентифікації (storage state).
Фронтенд vs бекенд
Фронтенд (frontend) — усе, що виконується на боці клієнта, у браузері: HTML, CSS, JavaScript. Це те, що користувач бачить і з чим взаємодіє.
Бекенд (backend) — усе, що виконується на боці сервера: бізнес-логіка, робота з базою даних, автентифікація, інтеграції. Користувач бачить не це, а лише результати.
| Ознака | Фронтенд | Бекенд |
|---|---|---|
| Де виконується | у браузері | на сервері |
| Технології | HTML, CSS, JS, фреймворки (React, Vue…) | будь-яка серверна мова + база даних |
| Що тестуємо | верстку, поведінку UI, локатори, стан на екрані | API, статуси, дані, права доступу, продуктивність |
| Типовий інструмент | Playwright, Cypress, Selenium | Postman, curl, API-клієнти в коді |
Межа буває розмитою: при серверному рендерингу (SSR) HTML для тієї самої сторінки готує сервер, а не браузер. Але поділ ролей лишається тим самим.
Для AQA цей поділ визначає, де шукати причину бага. Якщо на екрані не той текст, а в мережевій відповіді дані правильні — баг фронтендовий (неправильно відрендерив). Якщо відповідь уже містить неправильні дані — баг бекендовий. Тому вміння дивитись у вкладку Network у DevTools часто економить години: воно розводить «клієнт неправильно показав» і «сервер неправильно віддав».
Що таке API
API (Application Programming Interface) — контракт, за яким одна програма звертається до іншої. У вебі найчастіше йдеться про HTTP API: набір ендпоінтів (endpoints), кожен зі своїм шляхом, методом, форматом запиту й відповіді.
Найпоширеніший стиль — REST, де ресурси адресуються через шляхи, а дії — через HTTP-методи. REST не єдиний (є ще GraphQL, gRPC), але у вебі домінує саме він. Відповіді зазвичай у форматі JSON:
{
"id": 42,
"name": "Espresso",
"price": 45.0,
"inStock": true
}
Приклад повного обміну — створення замовлення:
POST /api/orders HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1Ni...
{"productId": 42, "quantity": 2}
HTTP/1.1 201 Created
Content-Type: application/json
{"orderId": 1001, "status": "pending"}
Чому API важливий для тестів окремо від UI:
- Швидкість і стабільність. API-тести не залежать від верстки, анімацій і локаторів — вони рідше флакають.
- Підготовка стану. Замість клікати десять екранів, щоб створити передумову, ти одним POST-запитом готуєш дані для UI-тесту. Це швидше й надійніше.
- Перехоплення мережі (network interception). У UI-тестах можна перехопити запит фронтенду до API й підмінити відповідь (mock), щоб протестувати рідкісний стан — наприклад, як UI поводиться при коді 500 чи при порожньому списку.
Чи може браузер бути сервером
Коротка відповідь: у класичній моделі — ні, браузер це клієнт. Але з нюансами.
Браузер не приймає вхідні HTTP-зʼєднання ззовні — він не «слухає порт», щоб хтось із інтернету до нього постукав. Тому повноцінним вебсервером у звичному сенсі він не є.
Проте:
- Через WebSocket та Server-Sent Events (SSE) сервер може надсилати дані у вже відкрите клієнтом зʼєднання. Технічно ініціатором зʼєднання лишається браузер, але це ламає ілюзію, ніби «сервер ніколи не говорить першим».
- Технологія WebRTC дозволяє браузерам обмінюватись даними майже напряму (peer-to-peer), тобто один браузер приймає дані від іншого — але встановлення цього зʼєднання все одно координує окремий сигнальний сервер.
Практичний висновок для тестів: якщо застосунок оновлює дані «сам» (нотифікації, живі оновлення), не шукай там звичайний запит/відповідь — це, найімовірніше, WebSocket або SSE, і перехоплювати/чекати їх треба інакше, ніж HTTP-запити.
Round-trip і чому він впливає на швидкість
Round-trip — повний цикл «запит пішов → відповідь повернулась». Час на це називають RTT (round-trip time). Він залежить насамперед від фізичної відстані й якості мережі, а не від «потужності» сервера.
Тут важливо розрізняти два поняття:
- Затримка (latency) — скільки часу йде один round-trip. Її диктує швидкість світла й маршрут пакетів. Її не «прискориш» товщим каналом.
- Пропускна здатність (bandwidth) — скільки даних пролізе за одиницю часу. Її можна нарощувати.
Проблема в тому, що завантаження сторінки — це не один round-trip, а багато. Згадай ланцюг вище: DNS-резолюція, TCP-рукостискання, TLS-рукостискання — це вже кілька повних циклів ще до першого байта корисних даних. Потім кожен підресурс і кожен API-запит додають свої. Якщо RTT = 100 мс, то навіть 5 послідовних round-trips — це вже пів секунди, витрачені лише на «подорожі».
Тому оптимізації вебу здебільшого борються саме з кількістю round-trips: перевикористання зʼєднань (keep-alive), мультиплексування в HTTP/2 (багато запитів в одному зʼєднанні), кешування, CDN (ближчі до користувача сервери). HTTP/3 іде далі — працює поверх QUIC (на UDP) і поєднує встановлення транспортного зʼєднання з узгодженням TLS, ще більше скорочуючи затримку до першого байта.
Для AQA це прямо повʼязано з очікуваннями й таймаутами. Локально твій запит до localhost має RTT близький до нуля, а той самий тест на CI бʼється в реальний staging через мережу з RTT у десятки-сотні мілісекунд. Тому жорсткі короткі таймаути, які «завжди встигали» локально, починають зрізати тести на CI. Це одна з найчастіших причин, чому «локально зелено, на CI червоно».
Хостинг — де фізично «живе» сайт
Коли ти відкриваєш сайт, його файли й код десь реально виконуються. Хостинг (hosting) — надання ресурсів (обчислення, диск, мережа), де крутиться серверна частина застосунку.
Основні варіанти, спрощено:
| Тип | Що це | Аналогія |
|---|---|---|
| Власний сервер (on-premise) | своє залізо у своєму приміщенні | власний будинок |
| VPS / виділений сервер | орендована машина в дата-центрі | винайнята квартира |
| Хмара (cloud) | ресурси на вимогу (AWS, GCP, Azure) | готель — береш скільки треба |
| Serverless | код виконується без керування серверами | таксі — платиш за поїздку |
Незалежно від типу, фізично це завжди чиясь машина в дата-центрі, підключена до інтернету, з публічною IP-адресою, на яку вказує DNS-запис твого домену. Розуміння цього допомагає усвідомити, чому в різних географіях сайт відкривається з різною швидкістю (різна відстань до дата-центру = різний RTT) і чому «сайт лежить» іноді означає не баг у коді, а проблему інфраструктури.
localhost і 127.0.0.1
localhost — імʼя, яке вказує на «цю саму машину, з якої я звертаюсь». Воно резолвиться в loopback-адресу: будь-яку з діапазону 127.0.0.0/8 для IPv4 (найчастіше 127.0.0.1) або ::1 для IPv6. Loopback означає, що трафік нікуди не виходить у мережу — він розвертається всередині твоєї ж машини.
Практично: коли ти запускаєш застосунок локально й він каже «слухаю на http://localhost:3000», ти звертаєшся сам до себе. Ніхто зовні до цієї адреси не достукається, бо 127.0.0.1 у кожній машині вказує на неї саму.
Кілька моментів, які реально спотикають AQA:
localhostі127.0.0.1— не завжди строго одне й те саме. Імʼяlocalhostрезолвиться через систему (зазвичай через файлhosts) і може вказувати то на IPv4, то на IPv6. Бувають випадки, коли застосунок слухає лише на IPv4, аlocalhostрезолвиться в::1— і зʼєднання «незрозуміло чому» не встановлюється. Тоді явне127.0.0.1рятує.- Порт (port) — число після двокрапки (
:3000), яке вказує, до якого саме процесу на машині звертатись. Одна машина = багато портів = багато сервісів одночасно.
# перевірити, що локальний сервер відповідає
curl -i http://127.0.0.1:3000/health
Оточення dev / staging / prod
Один і той самий застосунок зазвичай живе в кількох оточеннях (environments) одночасно — це різні незалежні копії з різними даними й налаштуваннями.
| Оточення | Призначення | Дані | Хто користується |
|---|---|---|---|
| dev (development) | розробка, часті зміни | синтетичні, «брудні» | розробники |
| staging | репетиція перед релізом, максимально схоже на prod | наближені до реальних або клон | QA, замовник |
| prod (production) | бойове середовище для реальних користувачів | реальні дані | усі |
Навіщо їх розділяти: щоб експерименти й помилки не зачіпали реальних користувачів. Зміну спершу перевіряють у dev, потім проганяють автотести на staging, і лише потім вона їде в prod.
Для AQA це має кілька важливих наслідків:
- Тести мають знати, проти якого оточення вони біжать. Той самий сценарій на staging і prod може дати різний результат просто через різні дані чи різні ввімкнені фічі (feature flags).
- Ізоляція даних. Синтетичні тестові дані не повинні потрапляти в prod, а реальні дані користувачів — у тестові фікстури.
- Різниця конфігурацій. На staging може бути ввімкнене детальніше логування, інші ліміти, інші зовнішні інтеграції (тестові платіжні системи замість справжніх). Тому баг, який відтворюється на staging, іноді не відтворюється на prod — і навпаки.
Load balancer, оглядово
Коли навантаження зростає, один сервер перестає справлятись. Тоді ставлять кілька однакових серверів, а перед ними — балансувальник навантаження (load balancer). Він приймає всі вхідні запити й розподіляє їх між серверами за певним правилом (по черзі, за найменшою завантаженістю тощо).
Ось де знову «стріляє» stateless: якщо сервери не тримають стану про клієнта, балансувальнику байдуже, на який сервер відправити наступний запит того самого користувача. Але якщо стан усе ж зберігається локально на конкретному сервері (сесія в памʼяті), то без додаткових налаштувань («липких сесій», sticky sessions) користувач може «втратити логін», коли наступний запит потрапить на інший сервер.
Практичні наслідки для тестів:
- Плаваючі падіння. Тест іноді бачить старі дані, іноді нові — бо різні запити потрапили на різні сервери, які ще не синхронізувались.
- Помилки 502/503/504 часто приходять саме від балансувальника, коли сервер за ним не відповів або перевантажений, — а не від самого застосунку.
Чому той самий тест поводиться по-різному локально й на CI
Тест — це програма, а програма чутлива до середовища. Локальна машина й CI (continuous integration — сервер, що автоматично ганяє тести) різняться майже за всіма осями.
| Чинник | Локально | На CI | Наслідок для тесту |
|---|---|---|---|
| Швидкість мережі (RTT) | майже нуль (localhost) | реальна затримка до staging | короткі таймаути зрізають тест |
| Потужність CPU | зазвичай вища | часто слабший, спільний ресурс | UI рендериться повільніше → елемент ще не готовий |
| Режим браузера | headed (з вікном) | headless (без вікна) | інший дефолтний viewport, на CI часто бракує шрифтів → інше перенесення/рендер тексту |
| Паралелізм | 1–2 тести | багато воркерів одночасно | конкуренція за спільні дані → флак |
| Дані/оточення | своя база, свій стан | спільний staging | інші передумови, «брудні» дані від інших прогонів |
| Часовий пояс / локаль | твої налаштування | часто UTC | падають перевірки дат і форматів чисел |
| Стан між прогонами | накопичується | чиста машина щоразу | приховані залежності від залишкового стану спливають на CI |
Головна причина флаку тут — гонки станів (race conditions), які на швидкій локальній машині «не встигають» проявитись, а на повільнішому CI з реальною мережею — проявляються. Тест, який покладається на те, що «дані вже завантажились», бо локально це відбувалось за 10 мс, ламається на CI, де це займає 300 мс.
Що з цим робить досвідчений AQA:
- Жодних фіксованих пауз типу «почекати 500 мс». Замість цього — очікування конкретної умови: елемент видимий, запит завершився, текст зʼявився.
- Ізоляція стану. Кожен тест сам готує свої дані (через API) і не покладається на те, що лишив попередній.
- Однакове оточення. По змозі ганяти локально в тому ж режимі, що й CI (headless, той самий браузер, той самий таргет-URL), щоб різниця «локально/CI» зникала ще до пушу.
- Читати мережу. Коли тест падає на CI, перше питання — який був статус відповіді й скільки часу зайняв запит. Дуже часто відповідь уже там: 401 через протухлу сесію, 500 від перевантаженого сервера, або timeout через RTT.
Автотест — це ще один клієнт у моделі клієнт-сервер, з тими самими залежностями від DNS, round-trips, статусів, кукі та оточень. Що краще ти читаєш реальний обмін запитами за зеленою чи червоною галочкою, то точніше локалізуєш баг і то менше в тебе флаку.
Поясни модель клієнт-сервер. Хто такий клієнт і хто такий сервер?
Клієнт — програма, яка ініціює запит і споживає відповідь; сервер — програма, яка слухає порт, обробляє запити й повертає відповіді. Клієнтом може бути не лише браузер: мобільний застосунок, curl, Postman чи автотест на Playwright — усе це клієнти. Слово «сервер» має два значення — фізична машина («залізо») і програма-процес на кшталт Nginx чи застосунку на Node.js, — і на співбесіді варто це розрізнення озвучити. Ключова властивість моделі — асиметрія: клієнт знає адресу сервера, а сервер не знає наперед, хто до нього прийде, і в класичній схемі сам обмін не ініціює. Для AQA це база: автотест — це ще один клієнт із тими самими залежностями від мережі, статусів і кукі.
Що відбувається, коли ти вводиш URL у браузер і натискаєш Enter?
Це ланцюг із кількох кроків: розбір URL → DNS-резолюція → TCP-рукостискання → TLS-рукостискання (для HTTPS) → HTTP-запит → обробка на сервері → HTTP-відповідь → побудова сторінки. Браузер спершу розкладає адресу на схему, хост, шлях і query string, потім перетворює доменне імʼя на IP (кеш браузера й ОС, файл hosts, далі DNS-резолвер), встановлює зʼєднання через SYN → SYN-ACK → ACK, узгоджує шифрування — і лише тоді надсилає сам запит. Отримавши HTML, браузер будує DOM і робить окремі запити на кожен підресурс (CSS, JS, зображення), а в SPA JavaScript після завантаження ще довантажує дані з API. Для автотестів критичні саме останні кроки: HTML уже прийшов, а даних на екрані ще немає, бо триває запит до API — тому «сторінка завантажилась» ≠ «дані готові», і надійні очікування чіпляються за конкретний елемент або завершення мережевого запиту.
З чого складається HTTP-запит і HTTP-відповідь?
Запит — це метод, шлях, заголовки й (не завжди) тіло; відповідь — код статусу, заголовки й тіло. Метод каже, що робимо, шлях — з чим, заголовки несуть метадані (наприклад, Host, Accept, Authorization), а тіло — дані, які надсилаємо, як-от JSON у POST. У відповіді статус повідомляє, чим усе скінчилось, а заголовки на кшталт Content-Type кажуть, як інтерпретувати тіло. Обмін завжди має форму «один запит → одна відповідь». Для AQA це щоденний інструмент: у вкладці Network у DevTools ти бачиш саме ці частини, і саме за ними локалізуєш баг.
Які HTTP-методи знаєш і чим вони відрізняються?
Основні: GET — отримати ресурс, POST — створити чи надіслати дані, PUT — замінити ресурс повністю, PATCH — частково оновити, DELETE — видалити, HEAD — як GET, але лише заголовки без тіла, OPTIONS — дізнатись доступні опції. Різняться семантикою і наявністю тіла запиту: POST, PUT і PATCH тіло мають, GET і DELETE — зазвичай ні. Важлива відмінність PUT від PATCH: перший заміняє ресурс цілком, другий оновлює лише частину полів. В API-тестах вибір методу — частина контракту: створення передумови для UI-тесту — це зазвичай один POST замість десяти кліків по екранах.
На які групи діляться коди статусів HTTP? Наведи приклади.
За першою цифрою: 1xx — інформаційні, 2xx — успіх, 3xx — перенаправлення, 4xx — помилка клієнта, 5xx — помилка сервера. З 2xx найчастіші 200 OK, 201 Created (після POST) і 204 No Content — успіх без тіла відповіді. З 4xx: 400 — некоректні дані від клієнта, 401 — не автентифікований, 403 — без прав, 404 — ресурсу немає, 422 — дані синтаксично коректні, але не проходять валідацію, 429 — спрацював rate limiting. З 5xx: 500 — помилка застосунку, а 502/503/504 часто приходять від проксі чи балансувальника, коли сервер за ним не відповів. У тестах перша цифра статусу — швидкий орієнтир, на чиєму боці проблема: 4xx — дивись на свій запит, 5xx — на сервер чи інфраструктуру.
Чим відрізняються 401 і 403?
Коротко: 401 — «я не знаю, хто ти», 403 — «я знаю, хто ти, але тобі сюди не можна». 401 Unauthorized означає, що клієнт не автентифікований: креденшелів немає або вони невалідні — наприклад, протухла сесія чи відсутній токен. 403 Forbidden означає, що автентифікація пройшла, але прав на ресурс бракує. Це класичне питання співбесіди й часте джерело плутанини в баг-репортах. Практично: 401 у тесті найчастіше сигналізує про проблему з сесією чи токеном (переотримай і підклади заново), а 403 — про тест-дані з неправильною роллю користувача.
Що означає, що HTTP — stateless-протокол?
Stateless означає, що сервер за замовчуванням не памʼятає нічого про попередні запити — кожен запит обробляється так, ніби клієнт прийшов уперше. Так зробили заради масштабованості: якщо сервер не мусить памʼятати клієнтів, будь-який із десятка однакових серверів може обробити будь-який запит. Наслідок: кожен запит має нести все потрібне сам — токен чи кукі прикладаються до кожного звернення, а не «один раз залогінився». Важливий нюанс, який відрізняє сильного кандидата: stateless — це про протокол, а не про застосунок; кошик і профіль існують, просто цей стан живе в базі даних і в тому, що клієнт приносить із кожним запитом. Для тестів це подарунок: протокол не тримає стану між запитами, тож тести легко робити ізольованими — а будь-який «спільний стан», що тече між тестами й дає флак, ти створив сам через базу, кукі чи глобальні фікстури.
Якщо HTTP нічого не памʼятає, як сайт памʼятає, що я залогінений?
Клієнт сам нагадує про себе в кожному запиті. Найпоширеніший механізм — кукі: після успішного логіну сервер у відповіді ставить кукі через заголовок Set-Cookie (наприклад, session_id), браузер її зберігає й автоматично додає до кожного наступного запиту на цей домен у заголовку Cookie. Сервер знаходить цей ідентифікатор у своєму сховищі сесій і «впізнає» користувача. Альтернатива — токени (найчастіше JWT), які клієнт зберігає сам, наприклад у localStorage, і додає в заголовок Authorization: Bearer .... Для автотестів звідси головний практичний прийом: не проклацувати форму логіну щоразу, а один раз отримати сесію через API й підкласти її в контекст браузера — у Playwright для цього є storage state; це прибирає флак на сторінці логіну й економить час прогону.
Чим кукі відрізняються від токенів?
Ключова відмінність — хто їх додає до запиту: кукі браузер підкладає автоматично до кожного запиту на відповідний домен, а токен із localStorage мусить вручну додати код фронтенду в заголовок Authorization. Тому кукі-сесія «переживає» перезавантаження сторінки без зусиль, а токен потрібно спершу дістати зі сховища. У кукі є атрибути, які прямо впливають на тести: HttpOnly забороняє доступ із JavaScript (не витягнеш через document.cookie — керуй на рівні контексту браузера), Secure вимагає HTTPS (на локальному http:// кукі може не встановитись), SameSite обмежує крос-сайтові запити й ламає сценарії з редіректами між доменами, а Expires/Max-Age задають час життя — протухла сесія дає плаваючі падіння в довгих прогонах. Окремий нюанс із SameSite: браузери на Chromium за відсутності атрибута трактують кукі як SameSite=Lax, а Firefox і Safari такого дефолту не мають — тож у крос-доменних сценаріях дивись у DevTools, що реально прийшло в Set-Cookie.
Чим фронтенд відрізняється від бекенду і як зрозуміти, де баг?
Фронтенд — усе, що виконується в браузері (HTML, CSS, JavaScript); бекенд — усе на боці сервера: бізнес-логіка, база даних, автентифікація. Для локалізації бага алгоритм простий: відкрий вкладку Network і подивись на мережеву відповідь. Якщо на екрані не той текст, а у відповіді дані правильні — баг фронтендовий, він неправильно відрендерив; якщо відповідь уже містить неправильні дані — баг бекендовий. Це вміння розводить «клієнт неправильно показав» і «сервер неправильно віддав» і часто економить години. Нюанс для повноти: при серверному рендерингу (SSR) HTML готує сервер, тож межа буває розмитою, але поділ ролей лишається тим самим.
Що таке API і навіщо тестувати його окремо від UI?
API — контракт, за яким одна програма звертається до іншої; у вебі це найчастіше HTTP API — набір ендпоінтів зі своїм шляхом, методом і форматом запиту-відповіді, зазвичай у JSON. Домінантний стиль — REST, де ресурси адресуються шляхами, а дії — HTTP-методами (є ще GraphQL і gRPC). Тестувати API окремо варто з трьох причин. Перша — швидкість і стабільність: API-тести не залежать від верстки, анімацій і локаторів, тому рідше флакають. Друга — підготовка стану: один POST-запит замінює прокликування десяти екранів для створення передумови UI-тесту. Третя — перехоплення мережі: у UI-тестах можна перехопити запит фронтенду до API й підмінити відповідь (mock), щоб перевірити рідкісний стан — наприклад, як UI поводиться при коді 500 чи порожньому списку.
Що таке ідемпотентність HTTP-методів і чому вона важлива?
Ідемпотентність означає, що повторення запиту дає той самий результат, що й одноразове виконання. GET, PUT і DELETE можна безпечно повторити, а повторний POST може створити дубль ресурсу. Це прямий наслідок семантики: PUT заміняє ресурс на конкретний стан (скільки не повторюй — стан той самий), а POST щоразу створює щось нове. Для автотестів це критично при ретраях: наївний автоматичний ретрай POST-запиту здатен створити два записи — і далі тест або наступні тести падають через несподівані дублікати в даних. Тому перш ніж вмикати ретраї на рівні HTTP-клієнта чи тест-ранера, варто розуміти, які запити в сценарії неідемпотентні.
Чим відрізняються редіректи 301/302 від 307/308?
Дві осі відмінності: постійність і збереження методу. 301 і 308 — постійні перенаправлення, 302 і 307 — тимчасові. Головний нюанс: 307 і 308 гарантують, що метод і тіло запиту збережуться при редіректі, тоді як при 301/302 деякі клієнти історично перетворюють POST на GET. Практично це означає, що POST на ендпоінт, який відповідає 302, може «загубити» тіло й доїхати до фінальної адреси вже як GET — і тест отримає зовсім не ту поведінку, яку очікував. Тому при дивних результатах після редіректів перший крок — подивитись у Network, який саме код повернувся і яким методом пішов наступний запит.
Що таке localhost і 127.0.0.1? Це одне й те саме?
localhost — імʼя, що вказує на «цю саму машину», і воно резолвиться в loopback-адресу: 127.0.0.1 (загалом діапазон 127.0.0.0/8) для IPv4 або ::1 для IPv6. Loopback означає, що трафік не виходить у мережу — він розвертається всередині твоєї машини, тому ззовні до localhost:3000 ніхто не достукається. Але строго кажучи, це не завжди одне й те саме: імʼя localhost резолвиться через систему (зазвичай файл hosts) і може вказувати то на IPv4, то на IPv6. Класична пастка: застосунок слухає лише на IPv4, а localhost резолвиться в ::1 — і зʼєднання «незрозуміло чому» не встановлюється; явне 127.0.0.1 у конфігу тесту це лікує. Порт після двокрапки вказує, до якого саме процесу на машині звертатись — одна машина може тримати багато сервісів на різних портах.
Навіщо потрібні оточення dev, staging і prod?
Щоб експерименти й помилки не зачіпали реальних користувачів: dev — для розробки з синтетичними «брудними» даними, staging — репетиція перед релізом, максимально схожа на prod, prod — бойове середовище з реальними даними. Зміна проходить шлях: перевірка в dev → автотести на staging → викочування в prod. Для AQA звідси три наслідки. Тести мають знати, проти якого оточення біжать: той самий сценарій може дати різний результат через різні дані чи ввімкнені feature flags. Дані мають бути ізольовані: синтетика не потрапляє в prod, реальні дані користувачів — у фікстури. І конфігурації різняться: на staging можуть бути інші ліміти, детальніше логування, тестові платіжні системи — тому баг, що відтворюється на staging, іноді не відтворюється на prod, і навпаки.
Чи може сервер сам надіслати дані клієнту без запиту? А браузер — бути сервером?
У класичній моделі HTTP — ні: сервер лише відповідає на те, що прийшло, а браузер не слухає порт і не приймає вхідні зʼєднання ззовні, тож повноцінним вебсервером не є. Але є технології, які цю картину розширюють. WebSocket і Server-Sent Events (SSE) дозволяють серверу надсилати дані у вже відкрите клієнтом зʼєднання — ініціатором зʼєднання лишається браузер, але далі сервер може «говорити» сам. WebRTC дозволяє браузерам обмінюватись даними майже напряму (peer-to-peer), хоча встановлення зʼєднання все одно координує сигнальний сервер. Практичний висновок для тестів: якщо застосунок оновлює дані «сам» — нотифікації, чат, живі оновлення, — не шукай там звичайний запит/відповідь; це, найімовірніше, WebSocket або SSE, і перехоплювати й чекати їх треба інакше, ніж HTTP-запити.
Що таке latency і bandwidth? Чому сторінка вантажиться повільно навіть на швидкому каналі?
Затримка (latency) — час одного round-trip «запит пішов → відповідь повернулась» (RTT); пропускна здатність (bandwidth) — скільки даних пролізе за одиницю часу. Ключова різниця: bandwidth можна нарощувати, а latency диктується фізичною відстанню й маршрутом пакетів — її товстим каналом не прискориш. Повільне завантаження на швидкому каналі пояснюється тим, що сторінка — це не один round-trip, а багато: DNS, TCP-рукостискання, TLS — це кілька повних циклів ще до першого байта корисних даних, а потім кожен підресурс і API-запит додають свої. При RTT 100 мс навіть 5 послідовних round-trips — уже пів секунди чистих «подорожей». Тому оптимізації вебу борються з кількістю циклів: keep-alive, мультиплексування в HTTP/2, кешування, CDN, а HTTP/3 поверх QUIC поєднує встановлення транспортного зʼєднання з узгодженням TLS. Для AQA це прямо про таймаути: локально RTT до localhost близький до нуля, а на CI тест бʼється в реальний staging — і жорсткі короткі таймаути, які «завжди встигали» локально, починають зрізати тести.
Що робить load balancer і які проблеми він може створювати в тестах?
Балансувальник навантаження приймає всі вхідні запити й розподіляє їх між кількома однаковими серверами — по черзі, за найменшою завантаженістю тощо. Це працює гладко, поки сервери stateless: тоді байдуже, куди потрапить наступний запит того самого користувача. Але якщо стан зберігається локально на конкретному сервері (сесія в памʼяті), без «липких сесій» (sticky sessions) користувач може «втратити логін», коли наступний запит прийде на інший сервер. У тестах балансувальник дає два характерні симптоми. Перший — плаваючі падіння: тест іноді бачить старі дані, іноді нові, бо різні запити потрапили на різні сервери, які ще не синхронізувались. Другий — коди 502/503/504, які часто приходять саме від балансувальника, коли сервер за ним перевантажений або не відповів, — тобто це не обовʼязково помилка в коді застосунку, і в баг-репорті це варто розрізняти.
Чому той самий тест зелений локально, але червоний на CI?
Тому що тест — програма, чутлива до середовища, а локальна машина й CI різняться майже за всіма осями: RTT (localhost проти реальної мережі до staging), потужність CPU (на CI слабший спільний ресурс — UI рендериться повільніше), режим браузера (headed проти headless з іншим viewport і шрифтами), паралелізм (конкуренція воркерів за спільні дані), дані оточення, часовий пояс і локаль (на CI часто UTC — падають перевірки дат), чистота машини між прогонами. Головний механізм флаку — гонки станів (race conditions): те, що локально «встигало» за 10 мс, на повільнішому CI займає 300 мс, і тест, який мовчки покладався на «дані вже завантажились», ламається. Що робить досвідчений AQA: жодних фіксованих пауз — лише очікування конкретної умови (елемент видимий, запит завершився); ізоляція стану — кожен тест сам готує свої дані через API; максимально однакове з CI локальне оточення (headless, той самий браузер і таргет-URL); і читання мережі при падінні — статус і тривалість запиту часто одразу показують причину: 401 через протухлу сесію, 500 від перевантаженого сервера або timeout через RTT.
Три кейси, які показують модель клієнт-сервер у роботі AQA: читання реального обміну в мережі, підготовка стану через API замість кліків по формі логіну та відтворення серверної помилки перехопленням запиту. Скрізь — що дивитися і чому.
Кейс 1. Читаєш Network, коли тест «не бачить» дані
Класична ситуація: HTML прийшов, сторінка «завантажилась», а на екрані порожньо. Причина — крок динамічного довантаження: фронтенд SPA після завантаження документа сам ходить в API за даними, і саме ця відповідь ще в дорозі.
Найшвидший спосіб розвести «клієнт неправильно показав» і «сервер неправильно віддав» — глянути в реальний обмін. Той самий запит, який робить браузер, можна повторити з терміналу:
curl -i 'https://api.example.com/api/orders?id=1001' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1Ni...'
HTTP/1.1 200 OK
Content-Type: application/json
{"orderId": 1001, "status": "pending"}
Що дивитися і чому:
- Статус. 200 — сервер віддав дані, отже баг фронтендовий (не відрендерив). 401 — протухла чи відсутня сесія. 500 — впав бекенд. 429 — спрацював ліміт запитів. Статус одразу вказує, на чиєму боці шукати.
- Тіло відповіді vs екран. Якщо в JSON дані правильні, а на екрані ні — баг у рендері. Якщо вже в JSON сміття — баг на бекенді.
- Час запиту. Довгий round-trip на CI при короткому таймауті = тест зрізає ще живий запит. Це не баг застосунку.
У DevTools дивись ту саму вкладку Network: колонки Status, Time, вкладку Response. Прапорець -i у curl друкує заголовки — саме там видно Set-Cookie, Content-Type і реальний код.
Кейс 2. Логін через API замість форми (Playwright storage state)
HTTP stateless — сервер не памʼятає тебе між запитами, тому кожен запит мусить нести сесію сам (кукі або токен). Проклацувати форму логіну в кожному тесті — повільно й флакітно: сторінка логіну сама по собі джерело гонок. Надійніше один раз отримати сесію й підкласти її в контекст браузера.
Глобальний setup логіниться один раз і зберігає стан автентифікації:
// auth.setup.ts
import { test as setup } from '@playwright/test';
setup('authenticate', async ({ request }) => {
// один POST замість кліків по формі
const res = await request.post('https://api.example.com/api/login', {
data: { email: 'qa@example.com', password: process.env.PASSWORD },
});
// сервер поставив Set-Cookie / повернув токен — зберігаємо весь стан
await request.storageState({ path: 'storage/auth.json' });
});
// playwright.config.ts (фрагмент)
use: { storageState: 'storage/auth.json' }
Що дивитися і чому:
- Чому це стабільніше. Прибираємо крок динамічного довантаження форми логіну — а це один із головних джерел флаку. Тест стартує вже автентифікованим.
- Кукі vs токен. Кукі-сесію браузер підкладає в кожен запит автоматично, тому вона «переживає» перезавантаження без зусиль. Токен із
localStorageмусить додавати сам код фронтенду.storageStateконтексту сторінки зберігає і кукі, іlocalStorage; але зверни увагу: у цьому кейсі логін іде черезrequest(API-контекст), а він зберігає лише кукі — токен уlocalStorageтак не зʼявиться. Для токен-авторизації логінься через сторінку або поклади токен уlocalStorageвручну перед збереженням стану. HttpOnly. Сесійну кукі зHttpOnlyне витягнеш черезdocument.cookie— керуй нею на рівні контексту браузера, а не через JS на сторінці.Secureна локалхості. Кукі з атрибутомSecureшлеться лише через HTTPS. На локальномуhttp://вона може не встановитись — і логін «незрозуміло чому» не тримається.
Кейс 3. Відтворюєш код 500 перехопленням мережі
Рідкісні стани — порожній список, помилка сервера — важко зловити на живому бекенді. Але фронтенд і бекенд спілкуються контрактом (API), тож відповідь можна підмінити (mock), не чекаючи, поки сервер справді впаде. Так перевіряєш, як UI поводиться при коді 500.
test('показує банер помилки, коли API віддає 500', async ({ page }) => {
await page.route('**/api/orders**', (route) => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Internal Server Error' }),
});
});
await page.goto('/orders');
await expect(page.getByText('Не вдалося завантажити')).toBeVisible();
});
Що дивитися і чому:
- Патерн маршруту.
**/api/orders**має ловити саме той запит, який робить фронтенд. Звір його з реальним шляхом у Network — інакше перехоплення мовчки не спрацює, а тест «зелений» через хибну причину. - Очікування за умовою, не за паузою.
toBeVisible()чекає появу елемента, а не фіксовані «500 мс». Фіксовані паузи — перше, що зрізає тести на повільнішому CI. - WebSocket/SSE так не перехопиш. Якщо застосунок оновлює дані «сам» (нотифікації, живі оновлення), це не звичайний запит/відповідь, а WebSocket або Server-Sent Events — їх треба чекати й мокати інакше, ніж HTTP-запити.
Клієнт, сервер і модель запит/відповідь
- Можу пояснити асиметрію ролей: клієнт ініціює запит, сервер лише відповідає й сам першим не заговорить у базовій моделі HTTP.
- Розумію, чому «сервер» — це двозначне слово (залізо vs процес-програма) і за замовчуванням маю на увазі саме програму.
- Знаю, що автотест (Playwright,
curl, Postman) — це такий самий клієнт, що й браузер, з тими самими залежностями від DNS, round-trips і статусів. - Можу пояснити, що обмін завжди має форму «один запит → одна відповідь», а запит складається з методу, шляху, заголовків і (не завжди) тіла.
- Розумію, чому семантика HTTP (метод, шлях, заголовки, статус) однакова в HTTP/1.1, HTTP/2 і HTTP/3 — у DevTools я бачу ті самі поля незалежно від версії.
Шлях від URL до сторінки, статуси й флак
- Можу перелічити ланцюг від Enter до сторінки: розбір URL → DNS → TCP-рукостискання → TLS → HTTP-запит → обробка на сервері → відповідь → побудова DOM і підресурси → динамічне довантаження.
- Розумію, чому «сторінка завантажилась» ≠ «дані готові»: HTML прийшов (крок 7), а дані ще їдуть окремими API-запитами (крок 9) — головне джерело флаку.
- Знаю, чому надійне очікування чіпляється за конкретний елемент або завершення запиту, а не за фіксовану паузу чи подію завантаження документа.
- Знаю ключові коди статусів (200, 201, 204, 301/302, 304, 400, 401, 403, 404, 422, 429, 500, 502/503/504) і типову причину кожного.
- Можу пояснити різницю 401 vs 403: «я не знаю, хто ти» проти «знаю, хто ти, але тобі сюди не можна».
- Розумію, що 422 замість 400 на валідацію — рішення конкретного API, а не універсальне правило: звіряюсь із контрактом бекенду.
Stateless і памʼять про логін
- Можу пояснити, що HTTP stateless: сервер за замовчуванням не памʼятає попередніх запитів, тому кожен запит мусить нести все потрібне сам (токен чи кукі).
- Розумію, що stateless — про протокол, а не про застосунок: стан живе в базі даних і в тому, що клієнт приносить із кожним запитом.
- Знаю ідемпотентність: GET, PUT, DELETE безпечно повторити, а наївний ретрай POST здатен створити дубль.
- Можу пояснити механізм кукі-сесії:
Set-Cookieу відповіді → браузер автоматично додаєCookieдо кожного запиту на домен → сервер «впізнає» заsession_id. - Знаю атрибути кукі, важливі для тестів (
HttpOnly,Secure,SameSite,Expires/Max-Age), і чомуSecure-кукі може не встановитись на локальномуhttp://. - Розумію різницю кукі vs токен: кукі браузер підкладає сам, а токен із
localStorageмусить додати код фронтенду вручну вAuthorization: Bearer. - Можу пояснити, чому в тесті найстабільніше отримати сесію через API й підкласти в контекст (storage state), а не проклацувати форму логіну.
Фронтенд, бекенд і API
- Можу розвести фронтенд (виконується в браузері) і бекенд (виконується на сервері) і сказати, що на кожному боці тестую.
- Розумію, як за вкладкою Network локалізувати баг: правильні дані у відповіді + не той текст на екрані = баг фронтенду; неправильні дані у відповіді = баг бекенду.
- Можу пояснити, що API — це контракт із набором ендпоінтів (шлях + метод + формат), найчастіше REST з JSON.
- Знаю, навіщо тестувати API окремо від UI: швидкість і стабільність, підготовка стану одним запитом, перехоплення мережі (mock рідкісних станів на кшталт 500).
Мережа, оточення та інфраструктура
- Можу розвести затримку (latency, диктує швидкість світла й маршрут) і пропускну здатність (bandwidth) і пояснити, чому завантаження сторінки — це багато round-trips.
- Розумію, чому оптимізації (keep-alive, мультиплексування HTTP/2, кеш, CDN, HTTP/3 поверх QUIC) борються саме з кількістю round-trips.
- Можу пояснити, чому «локально зелено, на CI червоно»: різні RTT, CPU, headless-режим, паралелізм, дані, часовий пояс — а корінь флаку зазвичай гонки станів.
- Розумію, чому браузер у класичній моделі не сервер (не слухає порт ззовні), і що живі оновлення — це WebSocket або SSE, а не звичайний запит/відповідь.
- Знаю різницю dev / staging / prod і чому баг зі staging може не відтворитись на prod через інші дані, фіча-флаги чи конфіг.
- Можу пояснити роль load balancer і чому 502/503/504 часто приходять від нього, а не від застосунку; знаю проблему «липких сесій» при локальному стані.
- Розумію
localhostта loopback (127.0.0.1/::1) і чому явне127.0.0.1рятує, коли застосунок слухає лише IPv4, аlocalhostрезолвиться в::1.
Що з переліченого може бути клієнтом у моделі клієнт-сервер?
Питання
Хто такі клієнт і сервер, і в чому асиметрія їхніх ролей?