TL;DR
- Playwright — лучшая замена Protractor: кросс-браузерность, авто-ожидание, бесплатная параллелизация
- Cypress побеждает по опыту разработчика, но параллельное выполнение платное
- WebdriverIO предлагает самый лёгкий путь миграции при наличии Selenium-инфраструктуры
- Миграция обычно занимает 8-12 недель; начни со smoke-тестов, запускай фреймворки параллельно
Для кого: Команды Angular, мигрирующие с Protractor, которым нужно production-ready E2E тестирование
Пропусти, если: Начинаешь новый проект на Angular 17+ — просто используй Playwright с первого дня
Почему устаревание Protractor изменило всё
Когда Google объявил Protractor устаревшим в апреле 2021 года, это застало многие Angular-команды врасплох. Я руководил QA в финтех-стартапе с 400+ тестами на Protractor, и у нас было шесть месяцев на миграцию перед обновлением на Angular 12. Та миграция научила меня вещам, которых нет в документации.
Настоящая проблема была не в поиске замены — а в выборе между тремя отличными вариантами (Playwright, Cypress, WebdriverIO), каждый из которых превосходен в разных сценариях. После миграции трёх разных проектов и помощи пяти другим командам с их переходами, вот что действительно важно.
Зачем это руководство: Большинство статей «альтернативы Protractor» перечисляют функции без реальных данных о миграции. Это руководство включает фактические сроки миграции, примеры конвертации кода и подводные камни, которые стоили нам недель.
Три претендента: Быстрое сравнение
Прежде чем углубляться, вот краткая сводка:
| Фактор | Playwright | Cypress | WebdriverIO |
|---|---|---|---|
| Лучше для | Кросс-браузер, сложные приложения | Опыт разработчика | Миграция с Selenium |
| Поддержка Angular | Общая (работает хорошо) | Общая (работает хорошо) | Доступны нативные плагины |
| Кривая обучения | Средняя | Низкая | Средняя |
| Параллельное выполнение | Бесплатно, встроено | Платное (Cypress Cloud) | Бесплатно, встроено |
| Усилия на миграцию | Средние | Средние-Высокие | Низкие |
| TypeScript | Отличный | Хороший | Отличный |
| Наше время миграции | 10 недель | 12 недель | 7 недель |
Playwright для Angular: Современный выбор
Playwright стал моей рекомендацией по умолчанию для новых Angular-проектов и большинства миграций. Вот почему, с реальными примерами.
Почему Playwright побеждает для большинства команд
1. Авто-ожидание, которое действительно работает
waitForAngular() в Protractor был магией, пока не переставал работать. Авто-ожидание Playwright обрабатывает zone.js Angular без специальной настройки:
// Playwright автоматически ждёт:
// - Элемент становится видимым
// - Элемент становится активным
// - Сетевые запросы завершаются
await page.click('[data-testid="submit-btn"]');
// Явные ожидания не нужны в 90% случаев
// Сравни с постоянными browser.wait() в Protractor
2. Кросс-браузерность без боли
Тестирование на Chrome, Firefox и Safari с одной кодовой базой:
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
// Мобильные viewports
{ name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
{ name: 'mobile-safari', use: { ...devices['iPhone 12'] } },
],
});
3. Бесплатное параллельное выполнение
Для нас это было огромно. Наш 45-минутный набор тестов Protractor сократился до 8 минут с встроенной параллелизацией Playwright — без платного облачного сервиса.
Playwright + Angular: Полная настройка
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 4 : undefined,
use: {
baseURL: 'http://localhost:4200',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
// Запуск Angular dev сервера
webServer: {
command: 'npm run start',
port: 4200,
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
});
Реальный пример теста Angular
// e2e/checkout.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Оформление заказа', () => {
test.beforeEach(async ({ page }) => {
// Логин один раз на файл тестов
await page.goto('/login');
await page.fill('[formControlName="email"]', 'test@example.com');
await page.fill('[formControlName="password"]', 'password123');
await page.click('button[type="submit"]');
await page.waitForURL('/dashboard');
});
test('завершить покупку с валидной картой', async ({ page }) => {
await page.goto('/cart');
await page.click('[data-testid="checkout-btn"]');
// Заполнение формы доставки (Angular reactive forms)
await page.fill('[formControlName="firstName"]', 'Иван');
await page.fill('[formControlName="lastName"]', 'Петров');
await page.fill('[formControlName="address"]', 'ул. Пушкина 123');
await page.fill('[formControlName="city"]', 'Москва');
await page.selectOption('[formControlName="state"]', 'MOW');
await page.fill('[formControlName="zip"]', '101000');
await page.click('[data-testid="continue-to-payment"]');
// Оплата (работа с iframe)
const stripeFrame = page.frameLocator('iframe[name="stripe-card"]');
await stripeFrame.locator('[name="cardnumber"]').fill('4242424242424242');
await stripeFrame.locator('[name="exp-date"]').fill('12/28');
await stripeFrame.locator('[name="cvc"]').fill('123');
await page.click('[data-testid="place-order"]');
// Проверка успеха
await expect(page.locator('.order-confirmation')).toBeVisible();
await expect(page.locator('.order-number')).toContainText('ORD-');
});
test('показать ошибки валидации для пустой формы', async ({ page }) => {
await page.goto('/checkout');
await page.click('[data-testid="continue-to-payment"]');
// Сообщения об ошибках Angular Material
await expect(page.locator('mat-error')).toHaveCount(5);
await expect(page.locator('mat-error').first()).toContainText('обязательно');
});
});
Cypress для Angular: Лучший опыт разработчика
Cypress предлагает самый удобный опыт разработчика в E2E тестировании. Если твоя команда ценит быструю обратную связь и отладку, Cypress — отличный выбор.
Почему выбрать Cypress
1. Отладка с путешествием во времени
Тест-раннер Cypress показывает каждый шаг визуально. Когда тест падает, ты можешь буквально пройти назад через состояние DOM на каждой команде. Только это сэкономило нам часы отладки.
2. Встроенное тестирование компонентов
Тестируй Angular-компоненты изолированно без полного приложения:
// user-card.component.cy.ts
import { UserCardComponent } from './user-card.component';
describe('UserCardComponent', () => {
it('отображает информацию о пользователе корректно', () => {
cy.mount(UserCardComponent, {
componentProperties: {
user: { name: 'Иван Петров', email: 'ivan@example.com', role: 'Admin' }
}
});
cy.get('.user-name').should('contain', 'Иван Петров');
cy.get('.user-role').should('contain', 'Admin');
});
it('отправляет событие при клике на редактирование', () => {
const onEditSpy = cy.spy().as('onEditSpy');
cy.mount(UserCardComponent, {
componentProperties: {
user: { name: 'Иван', email: 'ivan@example.com' },
onEdit: onEditSpy
}
});
cy.get('[data-cy="edit-btn"]').click();
cy.get('@onEditSpy').should('have.been.calledOnce');
});
});
Cypress + Angular настройка
// cypress.config.ts
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: 'http://localhost:4200',
supportFile: 'cypress/support/e2e.ts',
specPattern: 'cypress/e2e/**/*.cy.ts',
viewportWidth: 1280,
viewportHeight: 720,
video: false,
screenshotOnRunFailure: true,
setupNodeEvents(on, config) {
require('@cypress/code-coverage/task')(on, config);
return config;
},
},
component: {
devServer: {
framework: 'angular',
bundler: 'webpack',
},
specPattern: '**/*.cy.ts',
},
});
Пример теста Cypress с мокированием сети
// cypress/e2e/products.cy.ts
describe('Каталог продуктов', () => {
beforeEach(() => {
// Мокируем ответы API для детерминированных тестов
cy.intercept('GET', '/api/products', { fixture: 'products.json' }).as('getProducts');
cy.intercept('GET', '/api/categories', { fixture: 'categories.json' }).as('getCategories');
cy.visit('/products');
cy.wait(['@getProducts', '@getCategories']);
});
it('фильтрует продукты по категории', () => {
cy.get('[data-cy="category-filter"]').select('Электроника');
cy.get('[data-cy="product-card"]')
.should('have.length', 5)
.first()
.should('contain', 'Ноутбук');
});
it('добавляет продукт в корзину с оптимистичным UI', () => {
cy.intercept('POST', '/api/cart', {
statusCode: 200,
body: { success: true, cartId: 'cart-123' }
}).as('addToCart');
cy.get('[data-cy="add-to-cart"]').first().click();
// Проверяем оптимистичное обновление
cy.get('[data-cy="cart-count"]').should('contain', '1');
// Проверяем что API был вызван
cy.wait('@addToCart').its('request.body').should('have.property', 'productId');
});
it('корректно обрабатывает ошибки API', () => {
cy.intercept('POST', '/api/cart', {
statusCode: 500,
body: { error: 'Server error' }
}).as('addToCartError');
cy.get('[data-cy="add-to-cart"]').first().click();
cy.get('[data-cy="error-toast"]')
.should('be.visible')
.and('contain', 'Не удалось добавить товар');
});
});
Ограничения Cypress
- Параллельное выполнение платное (требуется Cypress Cloud)
- Нет нативной поддержки мульти-табов (есть обходные пути, но они некрасивые)
- Поддержка Safari экспериментальная
- Нельзя тестировать несколько доменов в одном тесте без обходных путей
WebdriverIO для Angular: Самая простая миграция
Если у тебя есть существующая Selenium-инфраструктура или ты хочешь максимально плавную миграцию с Protractor, WebdriverIO — лучший выбор.
Почему WebdriverIO для миграции с Protractor
1. Та же ментальная модель, что и Protractor
WebdriverIO использует протокол WebDriver как Protractor. Существующие знания твоей команды переносятся напрямую:
// Protractor (до)
element(by.css('[data-test="login"]')).click();
expect(element(by.css('.welcome')).getText()).toEqual('Welcome');
// WebdriverIO (после) — почти идентично
await $('[data-test="login"]').click();
await expect($('.welcome')).toHaveText('Welcome');
2. Плагины синхронизации с Angular
WebdriverIO имеет community-плагины, которые воспроизводят Angular-aware ожидание Protractor:
// wdio.conf.js
exports.config = {
// ... другая конфигурация
before: async function() {
// Ожидание стабилизации Angular
await browser.executeAsync((done) => {
if (window.getAllAngularTestabilities) {
const testabilities = window.getAllAngularTestabilities();
let pending = testabilities.length;
testabilities.forEach(t => {
t.whenStable(() => {
pending--;
if (pending === 0) done();
});
});
} else {
done();
}
});
}
};
3. Гибкая поддержка тест-раннеров
Используй Mocha, Jasmine или Cucumber — что твоя команда уже знает:
// wdio.conf.js
exports.config = {
framework: 'mocha', // или 'jasmine' или 'cucumber'
mochaOpts: {
ui: 'bdd',
timeout: 60000
}
};
WebdriverIO + Angular: Полный пример
// test/specs/user-management.spec.ts
describe('Управление пользователями', () => {
before(async () => {
await browser.url('/admin/login');
await $('#username').setValue('admin@example.com');
await $('#password').setValue('adminpass');
await $('button[type="submit"]').click();
await browser.waitUntil(
async () => (await browser.getUrl()).includes('/admin/dashboard'),
{ timeout: 10000 }
);
});
it('должен создать нового пользователя', async () => {
await browser.url('/admin/users');
await $('[data-test="add-user-btn"]').click();
// Заполняем Angular форму
await $('[formControlName="name"]').setValue('Новый пользователь');
await $('[formControlName="email"]').setValue('newuser@example.com');
await $('[formControlName="role"]').selectByVisibleText('Редактор');
await $('[data-test="save-user-btn"]').click();
// Ожидаем обновление Angular
await browser.pause(500);
// Проверяем
await expect($('.success-message')).toBeDisplayed();
await expect($('.user-list')).toHaveTextContaining('newuser@example.com');
});
it('должен валидировать формат email', async () => {
await browser.url('/admin/users/new');
await $('[formControlName="email"]').setValue('invalid-email');
await $('[formControlName="name"]').click(); // Триггерим blur
await expect($('mat-error')).toHaveText('Введите корректный email');
});
});
Стратегия миграции: Проверенный подход
После трёх миграций, вот процесс, который работает:
Фаза 1: Настройка (Неделя 1)
# Установи новый фреймворк рядом с Protractor
npm install --save-dev @playwright/test
# или
npm install --save-dev cypress
# или
npm install --save-dev webdriverio @wdio/cli
# Создай новую директорию для тестов
mkdir e2e-new
Фаза 2: Параллельный запуск (Недели 2-4)
Запускай оба фреймворка одновременно в CI:
// package.json
{
"scripts": {
"test:e2e:legacy": "protractor protractor.conf.js",
"test:e2e:new": "playwright test",
"test:e2e:all": "npm run test:e2e:legacy && npm run test:e2e:new"
}
}
Фаза 3: Миграция по приоритету (Недели 5-10)
Порядок приоритета:
1. Smoke-тесты (5-10 критичных путей пользователя)
2. Потоки аутентификации
3. Основные бизнес-функции
4. Граничные случаи и обработка ошибок
5. Визуальные/accessibility тесты
Фаза 4: Вывод Protractor из эксплуатации (Недели 11-12)
# Только после 100% покрытия в новом фреймворке
npm uninstall protractor
rm -rf protractor.conf.js
rm -rf e2e-legacy/
AI-Assisted миграция
AI-инструменты могут значительно ускорить миграцию с Protractor. Вот как использовать их эффективно.
Что AI делает хорошо:
- Конвертация локаторов Protractor в синтаксис Playwright/Cypress
- Генерация шаблонной структуры тестов
- Объяснение незнакомых паттернов Protractor
- Рекомендации современных best practices тестирования
Что всё ещё требует людей:
- Решение какие тесты оставить vs переписать
- Обработка сложных async-паттернов
- Понимание бизнес-логики в тестах
- Настройка правильного управления тестовыми данными
Полезный промпт для конвертации тестов:
Конвертируй этот тест Protractor в Playwright. Сохрани ту же логику теста, но используй современные async/await паттерны. Используй data-testid селекторы где возможно. Добавь правильные ассерты используя expect().
// Вставь свой тест Protractor сюда
Пример конвертации:
// Protractor (вход для AI)
it('should filter results', async () => {
await browser.get('/search');
await element(by.model('query')).sendKeys('angular');
await element(by.css('.search-btn')).click();
await browser.wait(EC.presenceOf(element(by.css('.results'))), 5000);
expect(await element.all(by.css('.result-item')).count()).toBeGreaterThan(0);
});
// Playwright (выход AI, проверенный человеком)
test('should filter results', async ({ page }) => {
await page.goto('/search');
await page.fill('[ng-model="query"]', 'angular');
await page.click('.search-btn');
await expect(page.locator('.results')).toBeVisible();
await expect(page.locator('.result-item')).not.toHaveCount(0);
});
Реальные цифры миграции
Из нашей финтех-миграции (412 тестов Protractor → Playwright):
| Метрика | Protractor | Playwright | Изменение |
|---|---|---|---|
| Всего тестов | 412 | 398 | -14 (консолидировано) |
| Время выполнения | 47 мин | 9 мин | -81% |
| Флакающие тесты | 23 | 3 | -87% |
| Расходы CI/месяц | $340 | $120 | -65% |
| Время миграции | - | 10 недель | - |
| Размер команды | - | 2 QA + 1 dev | - |
14 тестов, которые мы «потеряли», были дубликатами или устаревшими тестами удалённых функций. Оставшиеся 398 тестов фактически покрывают больше сценариев, чем раньше.
FAQ
Какая лучшая альтернатива Protractor в 2026 году?
Playwright — лучший общий выбор для большинства команд. Он предлагает всё, что делал Protractor — кросс-браузерное тестирование, авто-ожидание, поддержку TypeScript — плюс бесплатное параллельное выполнение, лучшую отладку и активную разработку. Для команд с существующим опытом Selenium/WebDriver, WebdriverIO обеспечивает самую плавную миграцию.
Подходит ли Cypress для тестирования Angular?
Да, Cypress отлично работает с Angular-приложениями. Его отладка с путешествием во времени и тестирование компонентов не имеют аналогов. Однако учти компромиссы: параллельное выполнение требует платный Cypress Cloud, поддержка Safari экспериментальная, и миграция с Protractor требует больше изменений кода, чем WebdriverIO.
Сколько времени занимает миграция с Protractor?
Для тестового набора среднего размера (200-500 тестов), ожидай:
- WebdriverIO: 6-8 недель (минимум изменений кода)
- Playwright: 8-10 недель (умеренный рефакторинг)
- Cypress: 10-14 недель (значительные различия API)
Эти сроки предполагают 2-3 члена команды, работающих над миграцией part-time параллельно с обычной работой.
Можно ли использовать Playwright с Angular-специфичными селекторами?
Playwright не имеет встроенной поддержки Angular как by.model() или by.binding() в Protractor. Однако можно эффективно тестировать Angular-приложения используя:
[formControlName="fieldName"]для reactive forms[ng-model="fieldName"]для template-driven forms- Атрибуты
data-testid(рекомендуется для стабильности тестов) - Стандартные CSS-селекторы для Angular Material компонентов
Мигрировать все тесты сразу или постепенно?
Всегда мигрируй постепенно. Запускай оба фреймворка параллельно во время миграции:
- Начни с критичных smoke-тестов (5-10 тестов)
- Проверь что новый фреймворк работает в твоём CI/CD
- Мигрируй остальные тесты по приоритету
- Выведи Protractor из эксплуатации только после 100% миграции
Такой подход позволяет ловить проблемы рано и поддерживать покрытие тестами на всём протяжении.
Фреймворк принятия решений: Краткий справочник
Выбери Playwright если:
- Кросс-браузерное тестирование необходимо
- Хочешь бесплатное параллельное выполнение
- TypeScript-first разработка
- Тестируешь несколько фреймворков (Angular + React)
Выбери Cypress если:
- Опыт разработчика — главный приоритет
- Тестирование компонентов важно
- Есть бюджет на Cypress Cloud
- Команда предпочитает визуальную отладку
Выбери WebdriverIO если:
- Нужны минимальные усилия на миграцию
- Существующая инфраструктура Selenium Grid
- Команда знает паттерны WebDriver
- Нужна поддержка Cucumber/BDD
Заключение
Устаревание Protractor изначально было болезненным, но подтолкнуло Angular-команды к лучшему инструментарию. Все три альтернативы — Playwright, Cypress и WebdriverIO — превосходят Protractor по функциям, производительности и опыту разработчика.
Моя рекомендация на 2026:
- Новые проекты: Начинай с Playwright
- Быстрая миграция: Используй WebdriverIO
- Приоритет DX: Выбери Cypress (заложи бюджет на Cloud)
Главное — начать миграцию сейчас, если ещё не начал. Совместимость Protractor с современными версиями Angular становится всё более хрупкой с каждым релизом.
Смотрите также
Руководства по миграции:
- Полное руководство по Playwright - Глубокое погружение в функции и best practices Playwright
- Cypress Deep Dive - Архитектура, отладка и мастерство мокирования сети
- Selenium WebDriver в 2025 - Когда Selenium всё ещё имеет смысл
Связанные сравнения:
- Puppeteer vs Playwright - Сравнение инструментов автоматизации браузера
- Jest & Testing Library для React - Паттерны тестирования компонентов, применимые к Angular
Официальные ресурсы:
