QA Interview Hub · База знань · Тести

Cypress

Додаткові питання з типових списків

хуки, асерти, Shadow DOM, дебаг

Які хуки є в Cypress і як вони працюють?

Хуки беруться з Mocha: before — один раз перед усіма тестами блоку, beforeEach — перед кожним тестом, afterEach — після кожного, after — один раз після всіх. Хуки в cypress/support/e2e.ts діють глобально на всі спеки.

Порада від самої команди Cypress: прибирання краще робити в beforeEach, а не в afterEach. Якщо тест упав або ти зупинив його в Test Runner, afterEach може не виконатися, і наступний тест стартує з брудним станом.

describe('Кошик', () => {
  beforeEach(() => {
    cy.request('DELETE', '/api/cart');   // чистий стан ДО тесту
    cy.login();
    cy.visit('/cart');
  });

  it('порожній кошик', () => {
    cy.contains('Кошик порожній').should('be.visible');
  });
});
На співбесіді: Хуки беруться з Mocha: before і after — один раз на блок, beforeEach і afterEach — для кожного тесту. Підготовку і прибирання стану я роблю в beforeEach, бо afterEach може не виконатися, якщо тест упав, і тоді наступний стартує з брудним станом.
А в Playwright:

Є такі самі хуки (test.beforeEach, test.afterAll…), але підготовку й прибирання зазвичай виносять у фікстури: код після await use() виконується навіть при падінні тесту.

Які типи асертів підтримує Cypress?

  • Chai BDD — expect(x).to.eq(), .to.include(), .to.have.length().
  • Chai TDD — assert.equal(), assert.isTrue().
  • Chai-jQuery — асерти стану DOM: be.visible, have.class, have.text, be.checked.
  • Sinon-Chai — для шпигунів і заглушок: be.called, calledWith.

Два стилі використання: неявний через .should('have.text', 'X') — ретраїться; явний через expect усередині .then — виконується один раз, а всередині .should(cb) — ретраїться.

На співбесіді: Cypress використовує Chai у стилях BDD і TDD, Chai-jQuery для перевірок стану DOM і Sinon-Chai для стабів і шпигунів. Важливіше за перелік — різниця між неявними асертами через should, які ретраяться, і явними expect у then, які виконуються один раз.

Як працювати з Shadow DOM?

За замовчуванням cy.get не заходить усередину shadow root. Два способи:

  • Точково — команда .shadow(), яка переходить у shadow root елемента.
  • Глобально — includeShadowDom: true у конфігу, тоді всі запити проходять крізь shadow-межі.
cy.get('payment-widget').shadow().find('[data-cy=pay]').click();

// або в cypress.config.ts
// e2e: { includeShadowDom: true }
На співбесіді: За замовчуванням Cypress не бачить вміст shadow root. Точково я переходжу туди командою shadow, а якщо компонентів з Shadow DOM багато, вмикаю includeShadowDom у конфігу.
А в Playwright:

У Playwright локатори проходять крізь відкритий Shadow DOM за замовчуванням — нічого вмикати не треба.

Як зробити hover і drag-and-drop?

Нативного hover у Cypress немає: .trigger('mouseover') викликає JS-подію, але не CSS-стан :hover. Якщо меню показується лише через CSS, допоможе .invoke('show') або плагін cypress-real-events, який генерує справжні події браузера.

Drag-and-drop — ланцюжок подій через .trigger. Якщо бібліотека працює на HTML5 drag API, потрібні dragstart, drop і dataTransfer, і тут теж часто беруть плагін.

cy.get('[data-cy=menu]').trigger('mouseover');

cy.get('[data-cy=card]')
  .trigger('mousedown', { button: 0 })
  .trigger('mousemove', { clientX: 400, clientY: 200 })
  .trigger('mouseup');
На співбесіді: Hover і drag-and-drop робляться через trigger з потрібними подіями миші. Але trigger викликає лише JS-події, а не справжній CSS-стан hover, тому для таких випадків я використовую плагін cypress-real-events.
А в Playwright:

Вбудовано і через справжні події браузера: locator.hover() і locator.dragTo(target).

Чим cy.exec відрізняється від cy.task?

Обидві команди виконують щось поза браузером, у Node-процесі.

  • cy.exec('команда') запускає shell-команду: скрипт сідування бази, міграцію, npm run. Повертає код виходу, stdout і stderr.
  • cy.task('назва') викликає Node-функцію, яку ти зареєстрував у setupNodeEvents: запит до БД через бібліотеку, роботу з файлами, генерацію даних. Повертає будь-яке значення.

Правило вибору: є готовий скрипт — cy.exec; потрібна логіка й повернення даних у тест — cy.task.

cy.exec('npm run db:seed').its('exitCode').should('eq', 0);
cy.task('db:query', 'SELECT count(*) FROM pets').its('0.count').should('eq', '5');
На співбесіді: Обидві працюють у Node. cy.exec запускає shell-команду, наприклад скрипт сідування бази, і повертає код виходу та вивід. cy.task викликає Node-функцію, яку я зареєстрував у конфігу, і повертає дані в тест — так я роблю запити до бази.
А в Playwright:

Аналоги не потрібні: тест і так у Node, тож shell-команду запускають через child_process, а БД викликають напряму.

Які Node-події є в Cypress, наприклад after:run?

Це хуки рівня всього запуску, а не тесту. Вони реєструються в setupNodeEvents і виконуються в Node:

  • before:run / after:run — до і після всього cypress run; after:run отримує підсумки.
  • before:spec / after:spec — навколо кожного спек-файлу; after:spec часто використовують, щоб видалити відео тестів, які пройшли.
  • before:browser:launch — налаштувати браузер: прапорці, розширення, розмір вікна.
  • after:screenshot — обробити або перейменувати скріншот.
setupNodeEvents(on) {
  on('after:run', (results) => {
    if ('totalFailed' in results) {
      console.log('Упало тестів:', results.totalFailed);
    }
  });
}
На співбесіді: Це події рівня запуску, які я реєструю в setupNodeEvents. After:run спрацьовує після всього прогону і отримує підсумки — зручно для звітів чи сповіщень. After:spec — після кожного файлу, before:browser:launch дозволяє налаштувати браузер.
А в Playwright:

Аналоги — globalSetup / globalTeardown у конфігу або setup-проєкт; підсумки прогону — у власному репортері через onEnd.

Як контролювати порядок запуску спеків?

Через specPattern як масив шляхів у потрібному порядку, або через --spec у CLI. Але правильна частина відповіді — пояснити, що залежність тестів від порядку сама по собі проблема: такі тести ламаються при паралельному розбитті, перезапуску одного тесту або retries.

Якщо сценарію потрібні дані з попереднього кроку, їх готують через API в beforeEach, а не через інший тест.

specPattern: [
  'cypress/e2e/login.cy.ts',
  'cypress/e2e/checkout.cy.ts',
],
На співбесіді: Технічно порядок можна задати масивом у specPattern. Але я намагаюся, щоб тести від порядку не залежали, бо такі тести ламаються при паралельному запуску і повторах. Потрібні дані я створюю через API перед кожним тестом.
А в Playwright:

Ті самі принципи, але порядок за замовчуванням ще менш передбачуваний через воркери; для свідомо послідовних сценаріїв є test.describe.configure({ mode: 'serial' }).

Які браузери й ОС підтримує Cypress?

Браузери: сімейство Chrome (Chrome, Chromium, Edge, вбудований Electron), Firefox і WebKit (рушій Safari) — експериментально. Офіційно підтримуються три останні мажорні версії. Internet Explorer, Opera і справжній Safari — ні, мобільні браузери й нативні застосунки — теж ні.

ОС: Windows, macOS і Linux з актуальними версіями; Node.js — підтримувані LTS-версії. Точні мінімальні версії перевіряй у документації перед співбесідою — вони змінюються з кожним великим релізом.

На співбесіді: Cypress працює з браузерами сімейства Chrome, включно з Edge і Electron, з Firefox і експериментально з WebKit. Справжнього Safari, мобільних браузерів і нативних застосунків немає — це одне з головних обмежень порівняно з Playwright чи Appium.
А в Playwright:

Playwright підтримує Chromium, Firefox і WebKit повноцінно, плюс емуляцію мобільних пристроїв, але теж без нативних мобільних застосунків.

Як дебажити тести в Cypress?

  • Time-travel: у cypress open наведи на команду в Command Log — побачиш знімок DOM у той момент.
  • cy.pause() зупиняє виконання; далі можна крокувати по командах.
  • .debug() зупиняє в DevTools з поточним subject (DevTools мають бути відкриті).
  • cy.log() пише в Command Log; cy.task('log', ...) — у термінал CI.
  • Клік по команді в Command Log виводить у консоль деталі: subject, елементи, запит і відповідь.
  • У CI: скріншоти, відео (якщо ввімкнене) і Test Replay у Cloud.
cy.get('[data-cy=total]').debug();
cy.pause();
cy.get('[data-cy=checkout]').click();
На співбесіді: Локально я використовую time-travel у Command Log, cy.pause, щоб зупинити тест і йти покроково, і debug, щоб подивитися subject у DevTools. Клік по команді в лозі показує в консолі всі деталі. У CI дивлюся скріншоти, відео і логи, які виводжу через cy.task.
А в Playwright:

Аналоги: page.pause(), npx playwright test --debug з інспектором, UI mode (--ui) і Trace Viewer для CI.

Як працювати з URL, навігацією і браузерною історією?

  • cy.url() — повний URL як рядок.
  • cy.location() — об'єкт з pathname, search, hash; зручно перевіряти частини.
  • cy.go('back') / cy.go('forward') — кнопки «назад» і «вперед».
  • cy.reload() — перезавантаження сторінки.

Усі вони — queries або команди з ретраями, тож асерти на URL самі чекають навігацію.

cy.location('pathname').should('eq', '/statistics');
cy.location('search').should('include', 'period=30d');
cy.go('back');
cy.url().should('include', '/dashboard');
На співбесіді: Для URL є cy.url і cy.location — другий зручніший, бо можна перевірити окремо шлях чи параметри. Для історії — cy.go back і forward, для оновлення — cy.reload. Асерти на URL ретраяться, тож вони самі дочекаються навігації.
А в Playwright:

await expect(page).toHaveURL(/statistics/), page.goBack(), page.goForward(), page.reload().