Playwright
Что это такое
Playwright автоматизирует браузер, поэтому на нём пишут сквозные и кроссбраузерные тесты. Тест управляет настоящим браузером через небольшой API: находит элемент, щёлкает по нему, вводит текст, проверяет, что показывает страница. Отличительная черта — ожидание: прежде чем действовать, библиотека сама доводит страницу до состояния, нужного человеку, и автору не приходится расставлять задержки по тесту; один и тот же тест к тому же выполняется в нескольких движках браузеров без правки кода.
Почему это важно
Браузерные тесты исторически были нестабильными, медленными и мучительными в отладке, и эту репутацию создали инструменты, которые перекладывали тайминги на автора теста. Playwright переносит ожидание внутрь библиотеки, изолирует каждый тест в собственном контексте браузера и сохраняет достаточно улик — трассировку, снимки экрана, вывод консоли, — чтобы разобраться в отказе задним числом. Экономика меняется: надёжный набор сквозных тестов становится тем, что команда может себе позволить держать, а не тем, что удаляют после квартала красных сборок.
Ключевые идеи
Локаторы описывают элемент, а не его положение
Локатор находит элемент по тому, чем он является: роль, подпись, видимый текст. Это те же сведения, которыми пользуется человек или программа чтения с экрана, поэтому тест переживает изменения оформления и разметки и падает только тогда, когда интерфейс действительно изменился, — а сцепленные локаторы сужают область поиска, и держать их стоит настолько короткими, чтобы читать с одного взгляда.
await page.getByRole("button", { name: "Add to basket" }).click();
await expect(page.getByRole("listitem")).toHaveCount(3);
Автоожидание и утверждения о состоянии
Действия ждут, пока с элементом можно будет работать, а утверждения ждут, пока условие станет истинным, опрашивая его до тайм-аута. Тест описывает ожидаемое конечное состояние, а не последовательность догадок о таймингах. В этом самое большое отличие от старых инструментов, поэтому явная пауза в тесте Playwright обычно означает, что что-то другое сделано неправильно.
await expect(page.getByText("Saved")).toBeVisible();
await page.getByLabel("Email").fill("user@example.com");
Каждый тест получает свежий контекст
По умолчанию каждый тест выполняется в новом контексте браузера: чистые cookie, чистое хранилище, никакой общей сессии, — и эта изоляция бесплатно убирает самый частый источник нестабильности между тестами. Состояние, которое должно быть общим, например выполненный вход, можно подготовить один раз и переиспользовать, а это быстрее, чем входить через интерфейс в каждом тесте.
test.describe("checkout", () => {
test.use({ storageState: "auth.json" }); // старт уже с выполненным входом
});
Один API для всех браузеров
Один и тот же тест выполняется в нескольких движках браузеров, поэтому кроссбраузерное поведение проверяется настройкой, а не переписыванием тестов. Эмуляция размера окна, локали, часового пояса и даже условий сети входит в тот же API, поэтому в одном наборе практично проверять и мобильную вёрстку, и путь медленного соединения.
запустить набор на chromium, firefox и webkit из одной конфигурации
Отладка встроена в рабочий процесс
Трассировка записывает действия, снимки DOM, вывод консоли и сетевой журнал прогона, и её можно открыть задним числом и пройти по тому, что произошло. «Упало в конвейере» превращается в улики, которые можно изучить локально, а это часто разница между исправлением нестабильного теста за десять минут и догадками на весь день.
включить трассировку при отказе -> открыть трассировку -> дойти до упавшего действия
Практика
Один небольшой запускаемый тест
Напишите тест, который открывает страницу, заполняет поле поиска, отправляет его и проверяет, что появился результат. Запустите его в безголовом режиме, затем ещё раз с включённой трассировкой.
import { test, expect } from "@playwright/test";
test("search returns at least one result", async ({ page }) => {
await page.goto("/");
await page.getByLabel("Search").fill("keyboard");
await page.getByRole("button", { name: "Search" }).click();
await expect(page.getByRole("listitem").first()).toBeVisible();
});
Готово, когда: тест проходит на локальном сервере, падает с понятным сообщением, если нарочно очистить поле поиска, а трассировка открывается и показывает упавшее действие.
Замените паузу утверждением
Найдите тест, который ждёт фиксированной задержкой или содержит цикл повторных попыток вокруг утверждения, и замените это утверждением, ждущим нужного состояния.
Готово, когда: тест быстрее и по-прежнему падает правильно, когда ожидание не выполняется.
Частые ошибки
- Выбор по CSS-классу или сгенерированному id — изменение оформления ломает тест, который проверяет поведение; исправить: выбирайте по роли, подписи или видимому тексту.
- Явные ожидания и паузы — тест лишь кажется стабильным и падает на более медленной машине; исправить: полагайтесь на автоожидание и утверждения о состоянии.
- Общая страница для нескольких тестов — состояние протекает, и отказы зависят от порядка; исправить: пусть каждый тест использует свой контекст.
- Утверждения о значениях, которых пользователь не видит — тест доказывает деталь реализации; исправить: утверждайте то, что страница действительно показывает.
- Игнорирование трассировки и догадки о причине — та же нестабильность вернётся через неделю; исправить: откройте записанную трассировку, прежде чем что-то менять.
Что читать дальше
- Сквозное тестирование (01-cs-fundamentals/26-end-to-end-testing) — дисциплина, которой служит инструмент, поэтому сначала прочитайте её: что тестировать и зачем.
- Надёжность сквозных тестов (01-cs-fundamentals/26-end-to-end-testing/02-making-end-to-end-tests-trustworthy) — привычки, сохраняющие браузерному набору честность, с инструментарием Playwright как конкретным случаем.
- Отладка (01-cs-fundamentals/11-debugging) — что делать, когда трассировка показывает отказ, который вы пока не понимаете.