TL;DR
- Cypress работает внутри браузера — тесты быстрые и надёжные без WebDriver
- Установка:
npm install cypress --save-dev, запуск:npx cypress open- Используй
data-*атрибуты для селекторов — они переживают изменения UIПодойдёт: Начинающим в автоматизации, командам с JavaScript веб-приложениями Не подойдёт: Если нужно тестирование мобильных приложений или Safari из коробки
Помню свою первую неделю с автоматизацией браузерных тестов на Selenium. Конфигурационные файлы, скачивание драйверов, непонятные ошибки про session ID. Я тратил больше времени на отладку настроек, чем на написание тестов. Когда я открыл для себя Cypress, всё изменилось. Рабочий тест был готов за 15 минут.
Cypress стал основным выбором для тестирования современных веб-приложений. Он работает прямо в браузере, даёт мгновенную обратную связь и требует минимальной конфигурации. Это руководство проведёт тебя от установки до запуска тестов в CI/CD пайплайнах.
Почему Cypress?
Прежде чем писать код, давай разберёмся, чем Cypress отличается от традиционных инструментов.
Cypress vs традиционные инструменты:
| Характеристика | Cypress | Selenium/WebDriver |
|---|---|---|
| Архитектура | Работает внутри браузера | Внешний процесс драйвера |
| Время настройки | Минуты | Часы (драйверы, конфиги) |
| Отладка | Time-travel, снапшоты | Скриншоты, логи |
| Скорость | Быстрая (нет сетевых запросов) | Медленнее (HTTP команды) |
| Нестабильность | Низкая (автоматические ожидания) | Выше (ручные ожидания) |
Cypress отлично подходит для тестирования SPA на React, Vue, Angular и подобных фреймворках. Он обеспечивает live-перезагрузку, автоматические ожидания и отладку на уровне DevTools браузера.
Если ты строишь комплексную стратегию тестирования, понимание пирамиды автоматизации тестов поможет определить место Cypress рядом с unit-тестами и API-тестами.
Установка Cypress
Требования
Нужен Node.js на машине. Любая версия от 18.x подойдёт.
# Проверка версии Node.js
node --version
# Должно вывести v18.x.x или выше
Шаги установки
1. Инициализация проекта (если его нет):
mkdir my-cypress-tests
cd my-cypress-tests
npm init -y
2. Установка Cypress:
npm install cypress --save-dev
Это скачает Cypress и его Electron-браузер. Первая установка занимает пару минут.
3. Запуск Cypress:
npx cypress open
Это открывает Cypress Test Runner — визуальный интерфейс для запуска тестов.
Первый запуск
При первом открытии Cypress создаёт структуру папок:
cypress/
├── e2e/ # Твои тестовые файлы
├── fixtures/ # Тестовые данные (JSON)
├── support/ # Кастомные команды и настройка
│ ├── commands.js # Кастомные команды
│ └── e2e.js # Выполняется перед каждым файлом
└── downloads/ # Скачанные файлы во время тестов
Cypress также создаёт cypress.config.js в корне проекта — это главный конфигурационный файл.
Пишем первый тест
Напишем тест, который переходит на страницу и проверяет её содержимое. Создай файл cypress/e2e/first-test.cy.js:
describe('Мой первый тест', () => {
it('посещает тестовую страницу', () => {
cy.visit('https://example.cypress.io')
cy.contains('type').click()
cy.url().should('include', '/commands/actions')
cy.get('.action-email')
.type('test@example.com')
.should('have.value', 'test@example.com')
})
})
Разбор:
describe()группирует связанные тестыit()определяет один тест-кейсcy.visit()переходит по URLcy.contains()ищет элемент по текстуcy.get()ищет элемент по CSS-селектору.type()вводит текст в поле.should()делает проверку
Запусти этот тест, кликнув на first-test.cy.js в Test Runner.
Структура теста
Каждый тест Cypress следует паттерну:
describe('Название фичи', () => {
beforeEach(() => {
// Выполняется перед каждым тестом
cy.visit('/login')
})
it('делает что-то конкретное', () => {
// Arrange: подготовка условий
// Act: выполнение действия
// Assert: проверка результата
})
it('обрабатывает другой сценарий', () => {
// Ещё один тест-кейс
})
})
Хук beforeEach выполняется перед каждым тестом в блоке describe. Используй его для общей подготовки: логин, переход на страницу.
Селекторы: поиск элементов
Правильный поиск элементов критичен для надёжных тестов. Cypress поддерживает несколько стратегий селекторов.
Приоритет селекторов (от лучшего к худшему)
1. Data-атрибуты (рекомендуется):
cy.get('[data-test="submit-button"]')
cy.get('[data-cy="login-form"]')
cy.get('[data-testid="user-email"]')
Data-атрибуты существуют специально для тестирования. Они не меняются при изменении стилей.
2. ID-селекторы:
cy.get('#username')
ID стабильны, но не всегда доступны. Не добавляй ID только для тестов — используй data-атрибуты.
3. Текстовое содержимое:
cy.contains('Отправить')
cy.contains('button', 'Отправить') // Более точный
Хорош для кнопок и ссылок. Ломается при изменении текста или локализации.
4. CSS-селекторы (избегай):
cy.get('.btn-primary')
cy.get('form input[type="email"]')
CSS-классы часто меняются. Сложные селекторы хрупкие.
Добавление тестовых атрибутов в приложение
Работай с командой разработки для добавления тестовых атрибутов:
<!-- До -->
<button class="btn btn-primary">Регистрация</button>
<!-- После -->
<button class="btn btn-primary" data-test="signup-button">Регистрация</button>
Атрибут data-test переживает рефакторинг, смену темы и обновления CSS.
Cypress Testing Library
Для лучших селекторов на основе доступности установи Testing Library:
npm install @testing-library/cypress --save-dev
Добавь в cypress/support/commands.js:
import '@testing-library/cypress/add-commands'
Теперь можно использовать доступные селекторы:
cy.findByRole('button', { name: 'Отправить' })
cy.findByLabelText('Email адрес')
cy.findByPlaceholderText('Введите email')
Эти селекторы соответствуют тому, как пользователи взаимодействуют с приложением.
Assertions и проверки
Cypress использует Chai assertions. Самые частые паттерны:
Should assertions
// Видимость
cy.get('[data-test="header"]').should('be.visible')
cy.get('[data-test="modal"]').should('not.exist')
// Содержимое
cy.get('[data-test="title"]').should('have.text', 'Добро пожаловать')
cy.get('[data-test="title"]').should('contain', 'Добро')
// Атрибуты
cy.get('input').should('have.value', 'test@example.com')
cy.get('a').should('have.attr', 'href', '/dashboard')
// Состояние
cy.get('button').should('be.disabled')
cy.get('input').should('be.enabled')
cy.get('checkbox').should('be.checked')
// Количество
cy.get('[data-test="list-item"]').should('have.length', 5)
cy.get('[data-test="list-item"]').should('have.length.gt', 3)
Цепочки assertions
cy.get('[data-test="user-card"]')
.should('be.visible')
.and('contain', 'Иван Петров')
.and('have.class', 'active')
Expect для сложных проверок
cy.get('[data-test="product-list"]').then(($list) => {
const itemCount = $list.find('.product').length
expect(itemCount).to.be.greaterThan(0)
expect(itemCount).to.be.lessThan(100)
})
Взаимодействие с элементами
Основные действия
// Клики
cy.get('button').click()
cy.get('button').dblclick()
cy.get('button').rightclick()
// Ввод текста
cy.get('input').type('Привет мир')
cy.get('input').type('Привет{enter}') // Нажать Enter
cy.get('input').clear().type('Новый текст')
// Выбор из списка
cy.get('select').select('Опция 1')
cy.get('select').select(['Опция 1', 'Опция 2']) // Мультивыбор
// Чекбоксы и радиокнопки
cy.get('[type="checkbox"]').check()
cy.get('[type="checkbox"]').uncheck()
cy.get('[type="radio"]').check()
// Загрузка файлов
cy.get('input[type="file"]').selectFile('cypress/fixtures/image.png')
// Скролл
cy.get('[data-test="footer"]').scrollIntoView()
Работа с формами
Типичный тест формы:
describe('Форма контактов', () => {
beforeEach(() => {
cy.visit('/contact')
})
it('успешно отправляется с валидными данными', () => {
cy.get('[data-test="name"]').type('Анна Сидорова')
cy.get('[data-test="email"]').type('anna@example.com')
cy.get('[data-test="message"]').type('Привет от Cypress!')
cy.get('[data-test="submit"]').click()
cy.get('[data-test="success-message"]')
.should('be.visible')
.and('contain', 'Спасибо')
})
it('показывает ошибку при невалидном email', () => {
cy.get('[data-test="email"]').type('invalid-email')
cy.get('[data-test="submit"]').click()
cy.get('[data-test="email-error"]')
.should('be.visible')
.and('contain', 'валидный email')
})
})
Работа с API
Cypress может перехватывать и мокать API-вызовы. Это делает тесты быстрее и надёжнее.
Перехват запросов
cy.intercept('GET', '/api/users').as('getUsers')
cy.visit('/users')
cy.wait('@getUsers')
cy.get('[data-test="user-list"]').should('be.visible')
cy.wait('@getUsers') останавливается до завершения API-вызова. Никаких хаков с cy.wait(5000).
Мокирование API-ответов
cy.intercept('GET', '/api/products', {
statusCode: 200,
body: [
{ id: 1, name: 'Продукт A', price: 29.99 },
{ id: 2, name: 'Продукт B', price: 39.99 }
]
}).as('getProducts')
cy.visit('/products')
cy.wait('@getProducts')
cy.get('[data-test="product"]').should('have.length', 2)
Мокирование позволяет тестировать граничные случаи без изменений бэкенда.
Тестирование ошибок
cy.intercept('POST', '/api/checkout', {
statusCode: 500,
body: { error: 'Ошибка оплаты' }
}).as('checkoutError')
cy.get('[data-test="checkout-button"]').click()
cy.wait('@checkoutError')
cy.get('[data-test="error-message"]')
.should('be.visible')
.and('contain', 'Ошибка оплаты')
Для глубокого погружения в сетевые стабы читай Cypress Deep Dive: Архитектура, отладка и работа с сетью.
Конфигурация тестов
cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1280,
viewportHeight: 720,
defaultCommandTimeout: 10000,
video: false,
screenshotOnRunFailure: true,
setupNodeEvents(on, config) {
// Обработчики событий Node.js
}
}
})
Основные настройки:
| Настройка | Описание | По умолчанию |
|---|---|---|
baseUrl | Добавляется к URL в cy.visit() | нет |
viewportWidth/Height | Размеры браузера | 1000 × 660 |
defaultCommandTimeout | Время повторных попыток | 4000мс |
video | Запись видео | true |
retries | Автоповтор упавших тестов | 0 |
Переменные окружения
// cypress.config.js
module.exports = defineConfig({
e2e: {
env: {
apiUrl: 'http://localhost:4000',
adminUser: 'admin@test.com'
}
}
})
Доступ в тестах:
cy.visit(Cypress.env('apiUrl') + '/login')
cy.get('input').type(Cypress.env('adminUser'))
Или через командную строку:
npx cypress run --env apiUrl=http://staging.example.com
Кастомные команды
Создавай переиспользуемые команды в cypress/support/commands.js:
Cypress.Commands.add('login', (email, password) => {
cy.visit('/login')
cy.get('[data-test="email"]').type(email)
cy.get('[data-test="password"]').type(password)
cy.get('[data-test="submit"]').click()
cy.url().should('include', '/dashboard')
})
Cypress.Commands.add('logout', () => {
cy.get('[data-test="user-menu"]').click()
cy.get('[data-test="logout"]').click()
})
Использование в тестах:
describe('Дашборд', () => {
beforeEach(() => {
cy.login('user@example.com', 'password123')
})
it('показывает профиль пользователя', () => {
cy.get('[data-test="profile"]').should('be.visible')
})
})
Программный логин
Для ускорения тестов пропускай UI-логин:
Cypress.Commands.add('loginByApi', (email, password) => {
cy.request({
method: 'POST',
url: '/api/auth/login',
body: { email, password }
}).then((response) => {
window.localStorage.setItem('authToken', response.body.token)
})
})
Это намного быстрее, чем заполнять формы каждый раз.
Организация тестов
Структура папок
cypress/
├── e2e/
│ ├── auth/
│ │ ├── login.cy.js
│ │ └── registration.cy.js
│ ├── products/
│ │ ├── listing.cy.js
│ │ └── checkout.cy.js
│ └── smoke/
│ └── critical-paths.cy.js
├── fixtures/
│ ├── users.json
│ └── products.json
└── support/
├── commands.js
└── e2e.js
Использование fixtures
Храни тестовые данные в cypress/fixtures/:
// cypress/fixtures/users.json
{
"admin": {
"email": "admin@example.com",
"password": "admin123"
},
"regular": {
"email": "user@example.com",
"password": "user123"
}
}
Загрузка в тестах:
cy.fixture('users').then((users) => {
cy.login(users.admin.email, users.admin.password)
})
// Или напрямую в intercept
cy.intercept('GET', '/api/users', { fixture: 'users.json' })
Запуск тестов в CI/CD
Выполнение из командной строки
# Запуск всех тестов
npx cypress run
# Запуск конкретного файла
npx cypress run --spec "cypress/e2e/auth/*.cy.js"
# Запуск в конкретном браузере
npx cypress run --browser chrome
# Запуск с переменными окружения
npx cypress run --env apiUrl=http://staging.example.com
Интеграция с GitHub Actions
Создай .github/workflows/cypress.yml:
name: Cypress Tests
on: [push, pull_request]
jobs:
cypress:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Start application
run: npm start &
- name: Wait for app
run: npx wait-on http://localhost:3000
- name: Run Cypress tests
uses: cypress-io/github-action@v6
with:
wait-on: 'http://localhost:3000'
browser: chrome
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots
Больше паттернов CI/CD в статье GitHub Actions для QA-автоматизации.
Параллельное выполнение
Для больших тест-сьютов запускай тесты параллельно:
jobs:
cypress:
runs-on: ubuntu-latest
strategy:
matrix:
containers: [1, 2, 3]
steps:
- uses: cypress-io/github-action@v6
with:
record: true
parallel: true
group: 'UI Tests'
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
Для этого нужен Cypress Cloud (есть бесплатный тариф).
Отладка упавших тестов
Time Travel
Кликни на любую команду в Test Runner, чтобы увидеть состояние DOM в тот момент. Это невероятно полезно для понимания, почему селектор не сработал.
Скриншоты и видео
// Сделать скриншот вручную
cy.screenshot('before-submit')
// Скриншоты автоматические при падении
// Видео записывает весь прогон теста
Настройка в cypress.config.js:
module.exports = defineConfig({
e2e: {
screenshotOnRunFailure: true,
video: true,
videosFolder: 'cypress/videos',
screenshotsFolder: 'cypress/screenshots'
}
})
Команда debug
cy.get('[data-test="menu"]').debug()
Это останавливает выполнение и выводит элемент в консоль.
Логирование в консоль
cy.get('[data-test="items"]').then(($items) => {
console.log('Количество элементов:', $items.length)
console.log('Первый элемент:', $items.first().text())
})
Открой DevTools браузера, чтобы увидеть вывод.
Лучшие практики
Делай так
- Используй
data-testатрибуты для селекторов - Жди API-вызовы вместо произвольных таймаутов
- Держи тесты независимыми (каждый может запускаться отдельно)
- Используй
beforeEachдля общей подготовки - Тестируй одну вещь в одном тест-кейсе
- Мокируй внешние сервисы
Не делай так
- Не используй
cy.wait(5000)— жди конкретных событий - Не передавай состояние между тестами
- Не тестируй детали реализации (CSS-классы, HTML-структуру)
- Не злоупотребляй моками — некоторые тесты должны идти на реальный API
- Не пиши огромные тесты — держи их фокусированными
Борьба с нестабильными тестами
// Плохо - зависит от времени
cy.wait(3000)
cy.get('[data-test="results"]').should('exist')
// Хорошо - ждёт конкретное условие
cy.intercept('GET', '/api/search*').as('search')
cy.get('[data-test="search"]').type('query')
cy.wait('@search')
cy.get('[data-test="results"]').should('exist')
Что дальше?
Когда освоишь основы, изучи продвинутые темы:
- Компонентное тестирование: тесты React/Vue компонентов в изоляции
- Визуальная регрессия: ловля неожиданных изменений UI
- Мониторинг производительности: отслеживание времени загрузки
- Тестирование доступности: приложение работает для всех
Для продвинутых паттернов Cypress включая глубокий разбор архитектуры и мастерство сетевых стабов читай Cypress Deep Dive. Если оцениваешь альтернативы, Полное руководство по Playwright покрывает конкурирующий фреймворк от Microsoft.
FAQ
Cypress бесплатный?
Да, Cypress — open-source и полностью бесплатный для локальной разработки и CI/CD. Cypress Cloud (бывший Dashboard) предлагает бесплатный тариф с ограниченным количеством записей тестов. Платные тарифы дают больше параллельных запусков, длинную историю и продвинутую аналитику. Большинство команд начинают бесплатно и апгрейдятся по мере роста тест-сьюта.
Нужно ли знать JavaScript для работы с Cypress?
Базовые знания JavaScript помогут, но не нужно быть экспертом. Синтаксис Cypress спроектирован читаемым — cy.get('button').click() понятен даже без опыта в JavaScript. Начни с простых тестов и изучай паттерны JavaScript (промисы, стрелочные функции, деструктуризация) по мере необходимости. Многие QA-инженеры изучают JavaScript через Cypress.
Может ли Cypress тестировать любой сайт?
Cypress тестирует веб-приложения, которые работают в браузере. Он работает с любым фронтенд-фреймворком: React, Vue, Angular, чистый JavaScript или серверный рендеринг. Однако Cypress не может тестировать нативные мобильные приложения (iOS/Android), десктопные приложения или Electron-приложения в продакшн-сборках. Для мобильного тестирования нужны инструменты вроде Appium. У Cypress также есть ограничения с cross-origin iframe и несколькими вкладками браузера.
Сколько времени нужно на изучение Cypress?
Первый тест можно написать и запустить в течение часа после установки. Базовое владение — написание надёжных тестов, правильное использование селекторов, работа с формами — приходит через 2-4 недели регулярной практики. Продвинутые навыки вроде кастомных команд, моков API и интеграции с CI/CD занимают больше времени. Большинство инженеров чувствуют уверенность после автоматизации первой реальной фичи от начала до конца.
Смотрите также
- Cypress Deep Dive: Архитектура и работа с сетью - Продвинутые техники после освоения основ
- Полное руководство по Playwright - Сравнение с альтернативой от Microsoft
- Стратегия пирамиды автоматизации - Место Cypress в стратегии тестирования
- GitHub Actions для QA-автоматизации - Паттерны интеграции с CI/CD
- Непрерывное тестирование в DevOps - Интеграция автоматических тестов в пайплайн
