QA Interview Hub · База знань · Тести
Cypress
Мережа: cy.intercept
перехоплення й моки
Що таке cy.intercept і де саме відбувається перехоплення?
cy.intercept перехоплює HTTP-запити, які робить сторінка. Перехоплення відбувається не в браузері, а в Node-процесі Cypress, через який проходить увесь трафік як через проксі. Тому він бачить і запити від сторонніх скриптів.
Два режими: spy — лише спостерігати й перевіряти запит і відповідь; stub — підмінити відповідь. WebSocket-повідомлення cy.intercept не перехоплює, лише HTTP.
cy.intercept('GET', '/api/products*').as('products'); // spy
cy.intercept('GET', '/api/products*', { fixture: 'products.json' }); // stub
На співбесіді: Intercept перехоплює HTTP-запити сторінки, і робить це в Node-процесі Cypress, бо весь трафік іде через його проксі. Можна просто спостерігати за запитом і перевіряти його, а можна підмінити відповідь фікстурою чи будь-якими даними. WebSocket він не перехоплює, тільки HTTP.
А в Playwright:page.route() перехоплює запити на рівні протоколу браузера, а не через проксі. Маршрут прив'язаний до сторінки або контексту. На відміну від Cypress, WebSocket теж можна перехопити й замокати через page.routeWebSocket().
Як дочекатися запиту і перевірити його через alias?
Реєструєш intercept, даєш йому alias через .as(), виконуєш дію, а потім cy.wait('@alias'). Він віддає об'єкт interception з request і response, тож можна перевірити, що саме фронтенд відправив і що отримав.
cy.intercept('POST', '/api/orders').as('createOrder');
cy.get('[data-cy=checkout]').click();
cy.wait('@createOrder').then(({ request, response }) => {
expect(request.body.items).to.have.length(2);
expect(response.statusCode).to.eq(201);
});
На співбесіді: Я реєструю intercept з alias, виконую дію, яка викликає запит, і роблю cy.wait на цей alias. Він повертає і запит, і відповідь, тож я перевіряю, що фронт відправив правильне тіло і що бекенд відповів очікуваним статусом.
А в Playwright:Aliases немає: очікування запиту створюють до дії і чекають після неї.
const orderResp = page.waitForResponse('**/api/orders');
await page.getByTestId('checkout').click();
const resp = await orderResp;
expect(resp.status()).toBe(201);
expect(resp.request().postDataJSON().items).toHaveLength(2);
Як підмінити або змінити відповідь сервера?
Статичний stub — третім аргументом: фікстура, тіло, статус, затримка. Динамічний — через обробник req: можна змінити запит перед відправкою, повністю відповісти через req.reply або пропустити запит на сервер і змінити справжню відповідь у req.continue.
// помилка сервера
cy.intercept('GET', '/api/stats', { statusCode: 500, body: { error: 'boom' } });
// повільна мережа
cy.intercept('GET', '/api/stats', { fixture: 'stats.json', delay: 2000 });
// справжня відповідь, але з підміною одного поля
cy.intercept('GET', '/api/stats', (req) => {
req.continue((res) => {
res.body.total = 0;
});
});
На співбесіді: Для простих випадків передаю відповідь третім аргументом: фікстуру, статус-код, затримку. Так я перевіряю, як UI поводиться при помилці п'ятсот чи при повільній мережі. Якщо треба точково змінити справжню відповідь, пишу обробник: пропускаю запит на сервер через req.continue і міняю одне поле у відповіді.
А в Playwright:Підміна — route.fulfill(), пропуск — route.continue(), обрив — route.abort(). Модифікація справжньої відповіді — route.fetch() і fulfill з новим тілом.
await page.route('**/api/stats', async (route) => {
const response = await route.fetch();
const json = await response.json();
json.total = 0;
await route.fulfill({ response, json });
});
Чому intercept треба ставити до дії і що буде, якщо їх кілька на один URL?
Intercept спрацьовує лише на запити, які відбулись після його реєстрації. Якщо поставити його після cy.visit чи кліку, запит може проскочити — і cy.wait впаде за таймаутом. Класична причина флакі-тестів.
Якщо на один запит підходять кілька intercept, першим спрацьовує той, що визначений пізніше. Обробники можуть передавати запит далі, а опція times: 1 обмежує intercept однією відповіддю — зручно для сценарію «спершу помилка, потім успіх». Усі intercept скидаються перед кожним тестом.
cy.intercept('GET', '/api/user', { statusCode: 200, fixture: 'user.json' });
cy.intercept('GET', '/api/user', { times: 1 }, { statusCode: 503 });
// перший запит → 503 (визначений пізніше, спрацьовує першим)
// наступні запити → 200
На співбесіді: Intercept бачить тільки ті запити, що відбулися після його реєстрації, тому я завжди ставлю його до visit або кліку. Інакше запит проскочить і wait впаде — це часта причина флакі. Якщо на один URL кілька intercept, першим спрацьовує останній визначений, а через times один можна зробити сценарій, де спершу помилка, а потім успіх.
А в Playwright:Правила схожі: маршрут має бути зареєстрований до дії, останній зареєстрований обробник спрацьовує першим, передати запит далі можна через route.fallback(), а опція { times: 1 } теж є.
cy.intercept
cy.intercept перехоплює HTTP-запити застосунку за методом і URL-патерном. Його використовують для очікування запитів, стабінгу відповідей і перевірки того, що саме застосунок відправив. Перехоплення треба оголосити до дії, яка ініціює запит.
Для очікування: cy.intercept('GET', '/api/users').as('getUsers'), потім дія і cy.wait('@getUsers'). Після цього можна перевірити тіло запиту чи відповіді й статус-код. Для стабу передають статичну відповідь або фікстуру, наприклад cy.intercept('GET', '/api/users', { fixture: 'users.json' }).
Можна імітувати помилки й повільну мережу: statusCode: 500, forceNetworkError: true, delay. Також можна змінювати запит або відповідь на льоту через колбек. Це дає змогу тестувати UI ізольовано від нестабільного бекенду.