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

    03 · Веб і мережі для AQA

    Клієнт-сервер і як працює веб

    Зміст

    Уяви, що замовляєш каву. Ти (клієнт) кажеш бариста (сервер), чого хочеш; бариста готує й віддає результат. Ти не стоїш за стійкою і не вариш сам — лише формулюєш запит і отримуєш відповідь. Веб побудований на цій самій ідеї: одна сторона просить, інша відповідає. Для 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 до сторінки на екрані.

    1. Розбір URL. Браузер розкладає адресу на частини. Візьмемо https://shop.example.com/cart?id=42:

      • https — схема (scheme), протокол обміну;
      • shop.example.com — хост (host), доменне імʼя;
      • /cart — шлях (path);
      • ?id=42 — рядок запиту (query string).
    2. DNS-резолюція (DNS lookup). Компʼютери спілкуються не за іменами, а за IP-адресами. Браузер має перетворити shop.example.com на щось на кшталт 93.184.216.34. Спершу перевіряються кеш браузера й операційної системи та файл hosts; якщо там нічого немає, запит іде до DNS-резолвера, який від імені клієнта опитує кореневі, потім TLD- (.com), а тоді авторитетні сервери домену. Це вже перша «подорож» у мережу.

    3. Встановлення зʼєднання (TCP handshake). Перш ніж передати дані, клієнт і сервер домовляються про зʼєднання через тристороннє рукостискання: SYN → SYN-ACK → ACK. Це окремий обмін пакетами, ще до будь-яких корисних даних.

    4. Захищене зʼєднання (TLS handshake). Для https додається узгодження шифрування: сервер показує сертифікат, сторони узгоджують ключі. Це ще один обмін пакетами (у TLS 1.3 його скорочено до одного round-trip).

    5. HTTP-запит. Аж тепер браузер надсилає власне запит:

    GET /cart?id=42 HTTP/1.1
    Host: shop.example.com
    Accept: text/html
    User-Agent: Mozilla/5.0
    1. Обробка на боці сервера. Запит може пройти через балансувальник навантаження (про нього нижче), потрапити на застосунок, той сходить у базу даних, збере дані й згенерує HTML.

    2. HTTP-відповідь. Сервер повертає статус, заголовки й тіло:

    HTTP/1.1 200 OK
    Content-Type: text/html; charset=utf-8
    Content-Length: 5120
    
    <!doctype html><html>...</html>
    1. Побудова сторінки. Браузер парсить HTML, будує дерево DOM (Document Object Model), знаходить у ньому посилання на CSS, JavaScript, зображення — і робить окремі запити на кожен підресурс. Далі будується CSSOM, застосовуються стилі, виконується JavaScript, відбувається рендеринг.

    2. Динамічне довантаження. У сучасних застосунках (SPA — single-page application) JavaScript після завантаження часто робить ще запити до API вже без перезавантаження сторінки — і саме вони приносять дані, які бачить користувач.

    СерверDNSБраузерСерверDNSБраузерПобудова DOM і рендерингРезолюція shop.example.comIP-адресаTCP-рукостискання (SYN / SYN-ACK / ACK)TLS-рукостискання (сертифікат, ключі)GET /cart?id=42200 OK, HTMLЗапити підресурсів (CSS, JS, зображення)Файли підресурсівfetch до API (SPA)JSON із данимиСерверDNSБраузерСерверDNSБраузерПобудова DOM і рендерингРезолюція shop.example.comIP-адресаTCP-рукостискання (SYN / SYN-ACK / ACK)TLS-рукостискання (сертифікат, ключі)GET /cart?id=42200 OK, HTMLЗапити підресурсів (CSS, JS, зображення)Файли підресурсівfetch до API (SPA)JSON із даними

    Практичний висновок для 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 — помилка сервера). Ось найчастіші, які реально трапляються в тестах:

    КодЗначенняТипова причина
    200OKуспіх
    201Createdстворено ресурс (часто після POST)
    204No Contentуспіх без тіла відповіді
    301 / 302Moved Permanently / Foundперенаправлення (redirect)
    304Not Modifiedресурс не змінився, беремо з кешу
    400Bad Requestклієнт надіслав некоректні дані
    401Unauthorizedне автентифікований (немає/невалідні креденшели)
    403Forbiddenавтентифікований, але без прав
    404Not Foundресурсу немає
    422Unprocessable Content (у старій специфікації — Unprocessable Entity)дані синтаксично коректні, але не проходять валідацію
    429Too Many Requestsспрацював ліміт запитів (rate limiting)
    500Internal Server Errorпомилка на боці сервера
    502 / 503 / 504Bad 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). Схема така:

    1. Ти надсилаєш логін і пароль (зазвичай POST).
    2. Сервер перевіряє їх і у відповіді ставить кукі через заголовок Set-Cookie:
    HTTP/1.1 200 OK
    Set-Cookie: session_id=a1b2c3d4; HttpOnly; Secure; SameSite=Lax; Path=/
    1. Браузер зберігає цю кукі й автоматично додає її до кожного наступного запиту на цей домен:
    GET /account HTTP/1.1
    Host: shop.example.com
    Cookie: session_id=a1b2c3d4
    1. Сервер бачить session_id, знаходить у себе (у базі чи сховищі сесій), кому він належить, — і «впізнає» тебе.
    СерверБраузерСерверБраузерБраузер зберігає кукіPOST /login (логін і пароль)200 OK, Set-Cookie: session_idGET /account, Cookie: session_id200 OK (сервер «впізнав» вас)СерверБраузерСерверБраузерБраузер зберігає кукіPOST /login (логін і пароль)200 OK, Set-Cookie: session_idGET /account, Cookie: session_id200 OK (сервер «впізнав» вас)

    Атрибути кукі, важливі для тестування:

    АтрибутЩо робитьЧому важливо для 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, SeleniumPostman, 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). Він приймає всі вхідні запити й розподіляє їх між серверами за певним правилом (по черзі, за найменшою завантаженістю тощо).

    Клієнт

    Балансувальник

    Сервер 1

    Сервер 2

    Сервер 3

    Клієнт

    Балансувальник

    Сервер 1

    Сервер 2

    Сервер 3

    Ось де знову «стріляє» 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, статусів, кукі та оточень. Що краще ти читаєш реальний обмін запитами за зеленою чи червоною галочкою, то точніше локалізуєш баг і то менше в тебе флаку.