QA Interview Hub · База знань · Тести
Cypress
Архітектура фреймворку
POM, команди, конфіг, cy.task
Як створити кастомну команду і типізувати її в TypeScript?
Команди реєструють у cypress/support/commands.ts через Cypress.Commands.add. Щоб TypeScript знав про нову команду, розширюють інтерфейс Chainable.
// cypress/support/commands.ts
Cypress.Commands.add('getByCy', (id: string) => {
return cy.get('[data-cy=' + id + ']');
});
declare global {
namespace Cypress {
interface Chainable {
getByCy(id: string): Chainable<JQuery<HTMLElement>>;
login(role?: string): Chainable<void>;
}
}
}
// тест
cy.getByCy('submit').click();
На співбесіді: Кастомні команди я реєструю у support через Cypress.Commands.add. Щоб TypeScript підказував і перевіряв їх, розширюю глобальний інтерфейс Chainable у просторі імен Cypress. Типові приклади — getByCy для data-атрибутів і login.
А в Playwright:Кастомних команд немає. Їхню роль виконують фікстури через test.extend<MyFixtures>() (типізація через generic), хелпер-функції і методи Page Object.
Page Object чи App Actions — що обрати?
Page Object — класи з методами, які обгортають cy-команди для конкретної сторінки. Тест читається як сценарій, локатори в одному місці.
App Actions — підхід, який просуває команда Cypress: замість кліків через UI керувати станом застосунку напряму, через API або функції, відкриті у window. Це швидше й стабільніше для підготовки стану.
На практиці їх поєднують: Page Object для дій, які тест перевіряє, API — для підготовки.
export class StatisticsPage {
private chart = '[data-cy=stats-chart]';
private period = '[data-cy=period-select]';
visit() {
cy.visit('/statistics');
return this;
}
selectPeriod(value: string) {
cy.get(this.period).select(value);
return this;
}
chartShould(...args: Parameters<Cypress.Chainable['should']>) {
return cy.get(this.chart).should(...args);
}
}
// тест
new StatisticsPage().visit().selectPeriod('30d').chartShould('be.visible');
На співбесіді: Я використовую Page Object: класи з методами для кожної сторінки, тож локатори зібрані в одному місці, а тест читається як сценарій. Команда Cypress пропонує ще App Actions — керувати станом застосунку напряму, без UI. Я поєдную обидва: стан готую через API, а Page Object використовую для дій, які тест справді перевіряє. Така комбінація — типовий підхід у сучасних фреймворках.
А в Playwright:Page Object з async-методами, а сторінки часто передаються в тест через фікстури — без new у кожному тесті. App Actions можна робити через page.evaluate() або API.
Що таке фікстури в Cypress? Це те саме, що в Playwright?
Ні, і на цьому легко заплутатися. У Cypress фікстура — це просто файл з тестовими даними в папці cypress/fixtures, який завантажують через cy.fixture() або використовують як відповідь у cy.intercept.
У Playwright фікстура — механізм dependency injection, який готує ресурси (сторінку, контекст, залогіненого користувача) і прибирає їх після тесту. Ближчий аналог у Cypress — хуки плюс кастомні команди.
cy.fixture('users/admin.json').then((admin) => {
cy.get('[data-cy=email]').type(admin.email);
});
cy.intercept('GET', '/api/stats', { fixture: 'stats-empty.json' });
На співбесіді: У Cypress фікстура — це просто файл з тестовими даними, який я завантажую через cy.fixture або підставляю як відповідь в intercept. У Playwright це зовсім інше — механізм dependency injection, що готує і прибирає ресурси. Тому якщо питають про фікстури, я уточнюю, про який інструмент мова.
А в Playwright:Тут фікстура — механізм dependency injection з setup і teardown. Тестові дані з JSON просто імпортують або читають через fs.
Як налаштувати різні середовища і змінні оточення?
Змінні доступні через Cypress.env('NAME'). Джерела: файл cypress.env.json (не комітиться), змінні оточення ОС з префіксом CYPRESS_, прапорець --env, блок env у конфігу.
Для кількох середовищ у setupNodeEvents читають назву середовища і підставляють відповідний baseUrl. У CI секрети передають через secrets пайплайна.
// CI: CYPRESS_USER_PASSWORD=*** npx cypress run --env envName=staging
// cypress.config.ts
setupNodeEvents(on, config) {
const urls = {
dev: 'https://dev.example.com',
staging: 'https://staging.example.com',
};
config.baseUrl = urls[config.env.envName || 'dev'];
return config;
}
На співбесіді: Змінні я читаю через Cypress.env. Локально вони лежать у cypress.env.json, який не комітиться, а в CI передаються як змінні оточення з префіксом CYPRESS_ із секретів пайплайна. Середовище обираю прапорцем env, а в setupNodeEvents підставляю потрібний baseUrl.
А в Playwright:Змінні — звичайний process.env, часто разом із dotenv. Середовища задають через use.baseURL або окремі projects.
Що таке cy.task і setupNodeEvents?
Код тесту виконується в браузері, тому не може звернутися до бази даних, файлової системи чи бібліотек Node. cy.task передає виклик у Node-процес, де задачі зареєстровані в setupNodeEvents.
Задача повинна повернути значення або Promise. Якщо вона нічого не повертає, треба повернути null, інакше Cypress видасть помилку.
// cypress.config.ts
import { Client } from 'pg';
setupNodeEvents(on) {
on('task', {
async 'db:query'(sql) {
const client = new Client({ connectionString: process.env.DB_URL });
await client.connect();
const res = await client.query(sql);
await client.end();
return res.rows;
},
log(message) {
console.log(message);
return null;
},
});
}
// тест
cy.task('db:query', "SELECT status FROM orders WHERE id = 1042")
.its('0.status').should('eq', 'paid');
На співбесіді: Оскільки тест виконується в браузері, він не може напряму звернутися до бази чи файлів. Для цього є cy.task: я реєструю задачу в setupNodeEvents, вона виконується в Node і повертає результат у тест. Наприклад, перевіряю в PostgreSQL, що замовлення отримало правильний статус. Задача обов'язково має щось повернути, хоча б null.
А в Playwright:Аналог не потрібен: тест і так виконується в Node, тож клієнт PostgreSQL імпортуєш прямо в тест або у фікстуру.
Fixtures у Cypress vs Playwright
У Cypress fixtures — це статичні файли з тестовими даними, зазвичай JSON, у папці cypress/fixtures. Їх завантажують через cy.fixture('user.json') або передають у cy.intercept як тіло відповіді. По суті це просто сховище даних.
У Playwright фікстури — механізм dependency injection: функції, які готують середовище перед тестом і прибирають після нього. Вони мають скоупи test і worker, можуть залежати одна від одної й передаються в тест як параметри. Вбудовані фікстури — page, context, browser, request.
Аналогом фікстур Playwright у Cypress є хуки beforeEach і before, custom commands та cy.session. Це частий момент плутанини, тому варто одразу уточнювати, в якому сенсі вживається термін.
Page Object vs App Actions
Page Object — клас, який інкапсулює селектори й дії сторінки, тож тест працює з методами на кшталт loginPage.login(user). Він дає перевикористання й одне місце для зміни селекторів. Мінус у Cypress — зайвий шар абстракції над chainable-командами і спокуса робити все через UI.
App Actions — підхід, рекомендований командою Cypress: стан застосунку готується напряму через його внутрішні функції, доступні через window, або через API. Наприклад, замість кліків по формі створення задач тест викликає функцію застосунку й одразу отримує потрібний стан. Це швидше і стабільніше, але вимагає доступу до коду застосунку.
На практиці я б поєднував: Page Object або модулі з функціями для UI-взаємодій, а підготовку даних — через API і custom commands. Через UI проходимо лише те, що саме тестується.