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

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

    Перехоплення й мокання мережі

    Зміст

    Автотест, який відкриває сторінку в браузері, майже ніколи не працює сам із собою. Сторінка робить десятки запитів — по профіль користувача, список замовлень, конфігурацію , аналітику, — і кожен із них іде у справжню систему з власною базою, власними й власними збоями. Звідси головний клас болю в наскрізному (end-to-end) тестуванні: тест червоний не тому, що фронтенд зламався, а тому, що API відповів на 300 мілісекунд повільніше, віддав інший порядок елементів або «моргнув» пʼятисоткою.

    (network interception) — це вклинювання тесту між браузером і мережею: замість покладатися на реальний сервер, тест сам вирішує, пропустити запит, підмінити відповідь, сповільнити її чи обірвати зʼєднання. Підміна відповіді власними даними називається (mocking). Це канонічна глава теми: тут повний виклад техніки та її меж, решта глав розділу посилаються сюди.

    Де стоїть точка перехоплення

    Чому «між браузером і мережею», а не «у коді сторінки»? Браузер — не одна програма, а набір підсистем із власним темпом роботи: одна парсить розмітку й малює, друга виконує JavaScript, третя ходить у мережу (Архітектура браузера й рендеринг). Мережевий шар — саме та підсистема, якою тест маніпулює ззовні: він підміняє те, що приходить із сервера, не чіпаючи ні код сторінки, ні DOM.

    Наслідок практичний і важливий. Код застосунку про підміну не знає взагалі — для нього рукотворна відповідь виглядає як звичайний мережевий обмін, тож перевіряється справжня гілка коду, а не спеціальна тестова. Під контроль потрапляють усі запити сторінки: і XMLHttpRequest, і fetch. Окремої інфраструктури для цього не треба — достатньо оголосити обробник маршруту (route) для або звузити його до однієї сторінки. І перевірка не обмежена HTTP: WebSocket інструмент так само вміє інспектувати й підміняти (WebSockets і реалтайм).

    пропустити

    підмінити

    обірвати

    Код сторінки:
    fetch, XHR, WebSocket

    Мережевий шар у браузері

    Точка перехоплення
    всередині тесту

    Посередник:
    проксі, шлюз, тунель

    Рукотворна відповідь
    без сервера

    Мережева помилка

    Сервер

    пропустити

    підмінити

    обірвати

    Код сторінки:
    fetch, XHR, WebSocket

    Мережевий шар у браузері

    Точка перехоплення
    всередині тесту

    Посередник:
    проксі, шлюз, тунель

    Рукотворна відповідь
    без сервера

    Мережева помилка

    Сервер

    Модель: шаблон, обробник, три рішення

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

    РішенняЩо відбуваєтьсяТипове застосування
    Пропустити (continue)Запит іде на реальний сервер; дорогою можна змінити URL, метод, заголовки чи тілоЛогування, підміна заголовка, часткова модифікація
    Підмінити (fulfill)Сервер не викликається взагалі, браузер отримує рукотворну відповідьДані, помилки, порожні стани
    Обірвати (abort)Запит завершується мережевою помилкоюНедоступність сервісу, втрата мережі
    await page.route('**/api/orders', async (route) => {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ orders: [] }),
      });
    });

    Чотири властивості моделі, які пояснюють більшість «мок не працює». Перше: правило спрацьовує лише для запитів, зроблених після його реєстрації. Друге: glob-шаблон має збігтися з усім URL, а не з його частиною, — для складніших умов беруть . Третє: перехоплення вибіркове, і те, що не підійшло під жоден шаблон, спокійно йде на реальний сервер. Четверте: якщо на один шаблон зареєстровано кілька обробників, вони спрацьовують у зворотному до реєстрації порядку, і останній зареєстрований перекриває попередні; передати керування далі по ланцюгу можна явно через route.fallback(), тоді як route.continue() ланцюг зупиняє. Звідси й прийом: загальний мок кладуть у налаштування, а вужче правило реєструють пізніше — у самому тесті. Маршрут, заданий на рівні контексту, діє й на попапи, тож глушіння аналітики зручно вішати саме туди.

    Наша практика (не канон). Припису «спершу маршрут, потім дія» джерело прямо не формулює — воно лише показує цей порядок у прикладі; вимога наша. Наше й правило прибирати маршрут наприкінці тесту: залишений маршрут перехоплюватиме запити наступних тестів у тому самому й дасть плаваючі падіння, тож зняття моку — частина ізоляції стану, а не косметика.

    Чотири сценарії: підміна, затримка, помилка, обрив

    Підміна даних. Повний мережі означає, що запит до API не відбувається взагалі — відповідь цілком складає тест. Є й мʼякший режим: виконати справжній запит, узяти реальну відповідь і перевизначити в ній лише потрібні поля. Так перевіряють порожній стан, дуже великий список, профіль з адміністраторською роллю чи ввімкнений фіча-флаг — не домовляючись про особливий стан у базі.

    Затримка. Щоб побачити спінер, скелетон і заблоковану на час запиту кнопку, відповідь треба сповільнити: обробник витримує паузу й лише потім віддає керування далі.

    Помилка. Це той самий fulfill, тільки зі статусом помилки й тілом помилки; що означає кожен код — у главі HTTP статус-коди.

    Обрив. Аргумент abort — код помилки із закритого переліку, і коди не синоніми: connectionrefused означає «у зʼєднанні відмовлено», namenotresolved — «імʼя хоста не вдалося розвʼязати», internetdisconnected — «інтернет-зʼєднання втрачено», connectionreset — скидання, що відповідає TCP RST. Тобто «сервіс лежить», «DNS не резолвиться» і «мережі немає» — три різні негативні сценарії, а не один.

    Дві пастки при зміні запиту, які дока називає прямо. Опція headers застосовується і до самого запиту, і до редиректів, які він ініціює, а от url, method і postData на редиректи не переносяться. І заборонені заголовки (Cookie, Host, Content-Length та інші) перевизначити не вийде: підміну буде проігноровано, піде оригінальний заголовок. Тест, який «підмінив» Cookie, зеленіє на непідміненому запиті.

    Наша практика (не канон). Сам прийом «пауза в обробнику перед continue або fulfill» канонічного джерела не має — джерельна тут лише механіка обробника. Наше й спостереження, який клас багів ця пауза ловить: спінер, що не зникає; кнопка, яку встигають натиснути двічі й відправити дубль; , налаштований занадто оптимістично.

    Мокання відповіді дає детермінізм: замокана відповідь однакова щоразу, тож результат тесту залежить від коду фронтенду, а не від стану чужої системи. Другий бік того самого — незалежність від бекенда: при повному стабі запит до API не відбувається взагалі, тож недоступний, ще не написаний або просто «моргаючий» сервіс перестає бути умовою зеленого тесту. Третій — доступ до станів, які в живій системі трапляються раз на місяць: порожня видача, пʼятисотка, обрив.

    Наша практика (не канон). Сам перелік вигод зібраний нами — жодне джерело такого списку не дає. У робочому вигляді він такий: детермінізм, ізоляція стану між тестами, швидкість (локальна відповідь замість мережевої), доступ до і незалежність від готовності бекенда — за узгодженим контрактом фронтенд-тести пишуться ще до того, як реалізували.

    Стаб чи мок: що саме ви ставите

    Дублер (test double) замінює залежність «специфічним для тесту еквівалентом» і зобовʼязаний повторити її API, а не поведінку; при цьому реалізовувати весь інтерфейс він не мусить — лише те, що потрібно конкретному тесту. Різновиди розрізняються не «розумністю», а місцем і моментом перевірки: стаб (stub) — точка керування, він заганяє систему в гілку, у яку інакше не потрапити, і зазвичай ніяк не реагує на те, чого в ньому не запрограмували; (spy) — стаб із записом, перевірка записаного відбувається пізніше; мок (mock) перевіряє взаємодію під час неї і сам є специфікацією очікуваних викликів.

    У мережі обидва режими дає той самий обробник маршруту: fulfill з готовим тілом — це стаб, а читання route.request().postDataJSON() із наступним після дії — уже спай. Повна таксономія дублерів (разом із і дамі) — канон розділу про автоматизацію; тут лише браузерна механіка. Корисна деталь для співбесіди: у силабусі ISTQB CTFL таксономії дублерів немає взагалі — стаби, драйвери, симулятори й названі там елементами тестового оточення.

    Межа техніки: чого мок не перевіряє

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

    Звідси дві нормативні межі з першоджерела таксономії дублерів. Перша: має бути хоча б один тест без дублерів — із дублером ви тестуєте іншу конфігурацію, ніж піде в продакшн. Друга: надмірне вживання дублерів названо причиною через переспецифікацію — мок, що звіряє запит до останнього поля, ламається від доданого аналітичного параметра.

    Наша практика (не канон). Політика мокання рекомендацією джерела не є — це наш орієнтир: мокати зовнішнє й некероване (платіжні , аналітику), не мокати без потреби власний основний API в і мокати на найтоншому потрібному рівні — один ендпоінт, а не всю мережу.

    Спершу подивіться фактичний запит

    Мок пишуть не «за документацією», а за тим, що браузер справді відправляє. Кожен рядок панелі Network — один запит сторінки; журнал ведеться автоматично, але лише поки інструменти розробника відкриті, тож панель відкривають перед відтворенням сценарію. Поле фільтра розуміє властивості на кшталт status-code:500, method:POST і domain:, кілька властивостей зʼєднуються тільки через AND, а інверсію дає окремий Invert. Тіло запиту показує вкладка Payload, тіло відповіді — Response.

    Звідти ж беруть HAR (HTTP Archive) — JSON-формат експорту зафіксованої мережевої сесії: файл можна прикласти до тікета, завантажити назад у панель і розбирати чужу сесію як власну, а ще використати як джерело підмінених відповідей — це режим запису-відтворення, коли дані реалістичні, бо колись справді прийшли з сервера, але тест від сервера вже не залежить. Типовий експорт із панелі «санітизований»: з нього прибрано Cookie, Set-Cookie й Authorization, тож прикріпити його до тікета безпечно. Детальний розбір самої панелі — у главі DevTools: вкладка Network і дебаг.

    Зіставлення маршруту: URL і відсоткове кодування

    Шаблон маршруту зіставляється з URL — а URL це структура, а не рядок (URL і кодування). Три наслідки просто з цього. Перший: фрагмент після # відокремлюється ще до звернення до ресурсу й на сервер не їде взагалі, тож у перехопленому URL його не буде — на сервер ідуть authority, шлях і запит. Другий: типові порти в серіалізації опускаються, тому https://example.com:443/ і https://example.com/ — той самий ресурс, а :8443 дає вже інше . Третій: рівність двох URL визначається збігом їхніх серіалізованих форм, тож «схожі» адреси зіставленню не підлягають.

    Далі — кодування. Байт записується трійкою % плюс дві шістнадцяткові цифри, регістр яких значення не має (%2F і %2f — той самий байт). Пробіл має дві угоди: %20 у звичайному URL і + у форматі application/x-www-form-urlencoded, а справжній плюс у ньому записується як %2B. Той самий розрив живе всередині браузерного API: обʼєкт URL для шляху вживає %20, а searchParams серіалізує пробіл як +. У JavaScript три інструменти дають три різні результати для одного значення: encodeURIComponent (пробіл у %20, кодує &), encodeURI (пробіл теж у %20, але роздільники ; / ? : @ & = + $ , # лишає як є) і URLSearchParams (пробіл у +). Практичний висновок один: зіставляйте розібрані значення (new URL(...).searchParams.get(...)), а не сирі рядки, інакше результат залежить від того, хто і як закодував значення.

    Наша практика (не канон). Те, що порядок параметрів у сирому рядку запиту не гарантований, канонічного джерела не має — це наше робоче припущення, з якого й випливає правило порівнювати розібрані параметри, а не рядок цілком.

    CORS у мокнутих відповідях

    Заголовки Access-Control-* — інструкція для браузера, а не для сервера (CORS і політика одного походження). Для мокання це означає дві речі.

    Перша: один логічний запит дає два записи в мережі. Спершу preflight — окремий запит методом OPTIONS, у якому браузер заявляє Access-Control-Request-Method і Access-Control-Request-Headers; лише потім іде справжній запит. Тіло типу application/json preflight спричиняє, як і Authorization чи методи PUT, PATCH, DELETE. Тому шаблон, який ловить тільки POST /api/..., OPTIONS не перехопить, і тест упаде з CORS-помилкою «якої не мало бути». Окрема пастка: preflight ніколи не надсилається з обліковими даними, тож перевірка авторизації, яку сервер вішає на OPTIONS, завалить preflight сама.

    Друга: підміна CORS не скасовує. Мокнута відповідь на крос-оріджин запит має нести потрібні заголовки Access-Control-*, інакше браузер відкине її так само, як відкинув би справжню. І дзеркально — обійти CORS підготовкою стану через програмний HTTP-клієнт можна, але відтворити браузерну CORS-помилку ним не вийде: такі клієнти не реалізують і заголовки Access-Control-* просто ігнорують. Ще одне: заблокований CORS не означає, що дані не змінилися — простий запит сервер виконує повністю, браузер лише не віддає відповідь скрипту.

    Наша практика (не канон). Потребу окремо обробляти OPTIONS у маршруті дока прямо не описує — це наш висновок із того, що preflight є окремим запитом.

    Посередник як інша точка того самого

    Перехоплення у фреймворку живе всередині браузера. Але та сама ідея давно реалізована зовні — (intermediary). Форм посередника три: — агент пересилання, якого обирає клієнт; шлюз (він же reverse proxy) — вузол, що назовні поводиться як origin-сервер, а всередину транслює запити іншим серверам; — сліпий ретранслятор, який повідомлень не змінює, і саме ним TLS протягують крізь спільний фаєрвольний проксі. Окремий випадок — interception proxy, протилежність інструмента, який тестувальник ставить собі свідомо: його клієнт не обирав, і протокольно такий вузол не відрізняється від на шляху (DNS, IP, порти та мережа).

    Оскільки проксі бачить увесь трафік, він може не лише передавати його, а й інспектувати та змінювати: побачити реальні запити й відповіді, підмінити їх, зімітувати помилку сервера, уповільнити відповідь — той самий набір, що й у маршруті. Ціна входу — TLS: звичайний проксі зашифрований потік не бачить і не змінює, бо CONNECT лише просить відкрити трубу між клієнтом і сервером; щоб зазирнути всередину, інструмент прикидається сервером для клієнта й клієнтом для сервера, а це вимагає довіри до його власного сертифіката. Коли перехоплення в тесті не дістає (нативний застосунок, стороннє SDK), тести можна пустити через проксі штатно — глобально або на окремий контекст, з автентифікацією й списком хостів-винятків. І дрібниця, що економить години: 502 і 504 віддає сервер «у ролі шлюзу або проксі», а 503 означає, що сервер, який відповів, сам тимчасово не здатен обробити запит — посередника в цьому означенні немає.

    OAuth 2.0: потік, який мокають цілком

    OAuth 2.0 — фреймворк делегованого доступу: він дає сторонньому застосунку обмежений доступ до сервісу від імені власника ресурсу (Автентифікація та авторизація). Ролей чотири — власник ресурсу, клієнт, сервер авторизації й сервер ресурсів, — а в потоці з кодом авторизації браузер спершу відправляється на ендпоінт авторизації і лише потім клієнт міняє отриманий код на токен, уже сервер-до-сервера; сам токен предʼявляють у заголовку Authorization зі схемою Bearer. Термінологічна межа, яку люблять уточнювати: OAuth 2.0 — про авторизацію, а «Вхід через Google» технічно спирається на надбудову .

    Сервер авторизаціїВаш застосунокБраузерСервер авторизаціїВаш застосунокБраузер1. Запит авторизації (редирект)2. Код авторизації на redirect_uri3. Повернення з кодом4. Обмін коду на токен (сервер-до-сервера)5. Access tokenСервер авторизаціїВаш застосунокБраузерСервер авторизаціїВаш застосунокБраузер1. Запит авторизації (редирект)2. Код авторизації на redirect_uri3. Повернення з кодом4. Обмін коду на токен (сервер-до-сервера)5. Access token

    Саме тому OAuth — показовий приклад того, що мокають цілком: перевіряти треба, як ваш застосунок обробляє повернутий код або токен, а не роботу чужого провайдера. Провайдера підміняють на рівні мережі або піднімають тестовий сервер ідентичності. І одразу межа техніки: браузерний маршрут бачить кроки 1–3, а крок 4 іде сервер-до-сервера й повз браузер, тож перехопленням у тесті його не дістати. Якщо ж мета — перевірити застосунок після входу, сесію отримують через бекенд або прямим обміном токена, а UI-логін лишають одному окремому -тесту.

    Наша практика (не канон). Двох речей джерела не стверджують. Перша: провайдери цілеспрямовано детектують автоматизацію — , «підозрілий вхід», блокування headless, — тож повний потік через реальний UI Google чи GitHub у тесті погано відтворюваний. Друга: сам принцип «чужого провайдера не тестують» наш; реф покриває лише механізм мокання мережі.

    Гонка станів, затримки й тротлінг

    Гонка (race condition) — залежність результату від порядку завершення асинхронних операцій там, де цей порядок не гарантований (Виконання JavaScript та event loop). Кожне завдання обробляється повністю, перш ніж почнеться інше, тож гонка виникає не всередині завдання, а між завданнями. У коді її лікують відстеженням актуальності запиту — скасуванням попереднього через AbortController або ігноруванням застарілих відповідей.

    Для мокання це має прямий наслідок. Мокаючи кілька ендпоінтів одразу, ви самі задаєте порядок їхнього завершення — і однаковими миттєвими відповідями можете сховати гонку, яка на живому бекенді відтворюється регулярно. Дзеркально: різні затримки в обробниках роблять порядок керованим, і сценарій «повільна відповідь прийшла після швидкої» стає відтворюваним у кожному прогоні. Синхронізуватися при цьому треба з конкретною відповіддю (очікування мережевої відповіді після дії), а не фіксованою паузою: гонку не усуває, а маскує, бо воно не перевіряє стан.

    Сусідній важіль — (throttling): панель мережі ріже за пресетами, які змінювалися між версіями браузера, тому для відтворюваних умов надійніше задати власний профіль із фіксованими значеннями; поруч живуть режим Offline і прапорець Disable cache. Різниця з паузою в обробнику принципова: пауза стосується одного запиту, тротлінг — усього каналу.

    Наша практика (не канон). Наші тут три речі. Стисла формула гонки — «перемагає останній, що відповів, хоча має перемагати останній, що надіслали». Спостереження, що однакові миттєві моки гонку ховають, а різні затримки в обробниках роблять її відтворюваною, теж наше: джерело описує лише механіку обробника, а не цей ефект. І робоче правило про тротлінг: якщо тест падає на повільному профілі, він не «зафлакне колись на швидкому», він просто ще не впав.

    Типові помилки

    • «Маршрут зареєстровано, а запит усе одно пішов на сервер — баг інструмента». Насправді причин зазвичай дві: правило діє лише на запити, зроблені після реєстрації, або glob не збігся з усім URL, а неспійманий запит тихо йде на сервер без жодної помилки.
    • «Мій мок у тесті не спрацював, хоча він точно є». Насправді його перекрив інший обробник на той самий шаблон: спрацьовують вони у зворотному до реєстрації порядку, і виграє останній зареєстрований.
    • «Ми підмінили Cookie в запиті — авторизація тепер інша». Насправді заборонені заголовки при перевизначенні ігноруються, і на сервер піде оригінальний.
    • «Змінили URL у continue — редирект теж піде на нову адресу». Насправді headers переносяться на редиректи, а url, method і postData — ні.
    • «abort є abort, код не має значення». Насправді коди моделюють різні класи збою: в зʼєднанні, нерозвʼязане імʼя хоста, втрачений інтернет і скидання зʼєднання — чотири різні негативні сценарії.
    • «Мок крос-оріджин ендпоінта готовий, а тест падає з CORS». Насправді preflight OPTIONS — окремий запит, і мокнута відповідь без заголовків Access-Control-* справжній запит не пустить.
    • «У запиті поїхало ?q=search+results замість %20 — застосунок псує значення». Насправді + і %20 — дві різні угоди про пробіл; порівнювати треба розібрані параметри, а не сирий рядок.
    • «Набір зелений — інтеграція працює». Насправді зелений мок доводить згоду коду з моком: бекенд міг перейменувати поле ще півроку тому, і жоден мокнутий тест цього не побачить.

    Підсумок

    • Точка перехоплення — мережевий шар, а не код сторінки. Тому підміна не змінює гілку коду, яку виконує застосунок, і однаково покриває XMLHttpRequest, fetch і WebSocket.
    • Модель одна: шаблон URL, обробник, одне з трьох рішень — пропустити, підмінити, обірвати. Усе решта в інструментах — синтаксис навколо цієї моделі.
    • Дві найчастіші причини «мок не спрацював» — час і шаблон. Правило діє лише на запити після реєстрації, а glob звіряється з усім URL, тож зіставляти краще розібрані компоненти адреси, а не рядок.
    • Мокання прибирає недетермінізм разом із перевіркою інтеграції. Звідси нормативна межа: має бути хоча б один тест без дублерів, а від дрейфу контракту страхує окремий шар контрактних перевірок.
    • Затримка й тротлінг — різні важелі. Пауза в обробнику сповільнює один запит, тротлінг ріже весь канал; перше відповідає на «що буде, якщо цей ендпоінт гальмує», друге — на «як застосунок живе в поганій мережі».

    Можливі питання

    • «Що таке перехоплення мережі і на якому рівні воно працює?» Чекають, що кандидат назве рівень: не код сторінки, а мережевий шар усередині браузера, тож застосунок про підміну не знає. Сильна відповідь додає охоплення — XHR, fetch і WebSocket однаково.
    • «Які рішення може ухвалити обробник перехопленого запиту?» Перевіряють, чи є в голові модель, а не завчений синтаксис: пропустити зі змінами, підмінити без звернення до сервера, обірвати мережевою помилкою — і типове застосування кожного.
    • «Тест із моком падає: запит пішов на реальний сервер. Де шукати?» Класика на діагностику. Дві причини — маршрут зареєстровано після дії, або шаблон не збігся з усім URL; перевіряють, чи згадає кандидат вибірковість перехоплення, через яку неспійманий запит іде на сервер мовчки.
    • «Як відтворити помилку сервера, повільну відповідь і обрив зʼєднання?» Три техніки — три рішення обробника. Хороший кандидат окремо зазначить, що коди обриву не взаємозамінні, бо моделюють різні збої.
    • «Чого замокана відповідь перевірити не може?» Питання на зрілість. Чекають дрейф контракту й «зелений мок доводить згоду коду з моком»; сильна відповідь називає страховку — контрактні тести й хоча б один тест без дублерів.
    • «Як тестувати вхід через Google?» Дивляться, чи розуміє кандидат межу відповідальності: чужого провайдера не тестують, потік мокають або піднімають тестовий сервер ідентичності, а обмін коду на токен іде сервер-до-сервера й браузерним маршрутом не перехоплюється.
    • «Чим затримка в обробнику відрізняється від тротлінгу?» Коротке питання з подвійним дном: перше — один запит, друге — увесь канал; звідси й різні питання, на які вони відповідають.

    Джерела

    Де стоїть точка перехоплення

    Модель: шаблон, обробник, три рішення

    • Playwright — Network — три рішення обробника, вибірковість перехоплення, вимога збігу glob із усім URL і регулярний вираз для складніших умов, дія контекстного маршруту на попапи.
    • Playwright API — class: Route — зворотний до реєстрації порядок обробників, різниця route.fallback() і route.continue().
    • Playwright — class Page — маршрут діє лише на запити, зроблені після його реєстрації.

    Чотири сценарії: підміна, затримка, помилка, обрив

    • Playwright — Mock APIs — повний стаб без звернення до API і режим «виконати справжній запит і пропатчити відповідь».
    • Playwright — Network — підміна на основі справжньої відповіді, отриманої окремим запитом.
    • Playwright API — class: Route — закритий перелік кодів обриву з описом кожного, перенесення headers на редиректи й ігнорування заборонених заголовків.

    Навіщо це QA

    • Playwright — Mock APIs — повний стаб мережі як спосіб отримати детерміновані дані без звернення до API.
    • Martin Fowler — Mocks Aren't Stubs — дублер як спосіб прибрати залежність тесту від реальної системи.

    Стаб чи мок: що саме ви ставите

    • xUnit Test Patterns — Test Double — дублер повторює API, а не поведінку; стаб як точка керування, спай як стаб із записом, мок як специфікація очікуваних викликів.
    • Martin Fowler — Mocks Aren't Stubs — стаб віддає заготовлені відповіді, мок несе заздалегідь задані очікування.
    • ISTQB® Certified Tester Foundation Level Syllabus v4.0.1 — §1.4.3: стаби, драйвери, симулятори й віртуалізація сервісів як елементи тестового оточення (таксономії дублерів силабус не дає).

    Межа техніки: чого мок не перевіряє

    Спершу подивіться фактичний запит

    • Chrome DevTools — Network features reference — запис журналу лише за відкритих інструментів, властивості фільтра, AND без OR і прапорець Invert, вкладки Payload і Response, експорт та імпорт HAR і його санітизація.
    • Playwright — Network — записаний HAR як джерело підмінених відповідей у режимі запису-відтворення.

    Зіставлення маршруту: URL і відсоткове кодування

    • RFC 3986 — URI Generic Syntax — компоненти URL, фрагмент дереференсує лише клієнт, на сервер ідуть authority, шлях і запит; механізм відсоткового кодування й регістронезалежність його цифр.
    • WHATWG URL Standard — рівність URL за серіалізованими формами, опускання типових портів, пробіл як + у application/x-www-form-urlencoded, розбіжність URL і searchParams.
    • MDN — URL (Web API) — розбір адреси й читання параметрів запиту замість роботи з сирим рядком.
    • MDN — encodeURIComponent() — три інструменти JavaScript із трьома різними результатами для того самого значення.
    • MDN — encodeURI() — перелік символів, які encodeURI не кодує, і правило вибору між ним і encodeURIComponent.
    • Playwright — Network — зіставлення перехоплених запитів за розібраним URL.

    CORS у мокнутих відповідях

    • MDN — Cross-Origin Resource Sharing (CORS) — заголовки як інструкція браузеру, preflight методом OPTIONS і його заголовки, application/json поза безпечним переліком, два записи на один логічний запит.
    • WHATWG Fetch Standard — умови, за яких запит не потребує preflight, і заборона надсилати preflight з обліковими даними.
    • Playwright — Mock APIs — підміна крос-оріджин відповіді разом із її заголовками.
    • Playwright — class APIRequestContext — програмний HTTP-клієнт поза браузерним контекстом і його межі.
    • MDN — Same-origin policy — простий запит сервер виконує повністю, навіть коли браузер не віддає відповідь скрипту.

    Посередник як інша точка того самого

    • RFC 9110 — HTTP Semantics — три форми посередника й визначальна ознака кожної, interception proxy як вузол, якого клієнт не обирав, різниця 502/504 і 503.
    • mitmproxy — How mitmproxy works — інспекція й зміна трафіку посередником, межа звичайного проксі на CONNECT і потреба власного сертифіката для HTTPS.
    • Playwright — Network — запуск сторінок через проксі глобально або на контекст, автентифікація й хости-винятки.

    OAuth 2.0: потік, який мокають цілком

    Гонка станів, затримки й тротлінг

    • MDN — JavaScript execution model — завдання обробляється повністю до наступного, звідки й береться недетермінований порядок завершення операцій.
    • MDN — Using the Fetch API — скасування застарілого запиту через AbortController.
    • Selenium — Waiting Strategies — фіксована пауза падає, коли спить недостатньо довго, і механізми синхронізації, які дока називає кращими.
    • Playwright — Auto-waiting (actionability) як штатний спосіб гасити частину гонок.
    • Playwright — Network — очікування конкретної мережевої відповіді замість фіксованої паузи.
    • Chrome DevTools — Network features reference — меню тротлінгу з пресетами, власні профілі, режим Offline і прапорець Disable cache.

    Пояснення

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

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

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