Pest долго был фреймворком «как PHPUnit, только красивее». В версии 4 он стал чем-то заметно большим: теперь это инструмент, который позволяет писать честные end-to-end тесты в реальном браузере — на Chromium, Firefox или Safari — в том же лёгком синтаксисе, что и unit-тесты. Под капотом — Playwright, сверху — полная интеграция с Laravel: фабрики, RefreshDatabase, Event::fake(), assertAuthenticated() работают прямо внутри браузерного теста. В этой статье разберём, как поднять Pest 4 Browser Testing на Laravel-проекте, как писать практичные сценарии и как ускорить прогон в CI через шардирование.
Что нового в Pest 4
Главная фича релиза — браузерное тестирование. Pest 4 запускает реальный браузер, делает в нём клики, ввод текста, проверки DOM, ловит JS-ошибки и сообщения консоли. Тесты выглядят как обычный Pest-код: всё та же функция it(), всё те же expectations, плюс новый объект страницы.
Помимо браузера в релизе появились:
- Visual regression testing — сравнение скриншотов с эталоном через
assertScreenshotMatches(). - Test sharding — разбиение прогона на части (
--shard=1/4), удобно для CI. - Новый движок Type Coverage — в 2 раза быстрее на первом запуске, мгновенно — на последующих.
- Профанити-чекер (
pest-plugin-profanity) и проверка подозрительных юникод-символов. - Тесты на PHPUnit 12.
Дальше — про самое полезное: браузерные тесты.
Установка плагина и Playwright
На свежий Laravel-проект с Pest 4 ставим браузерный плагин и сам Playwright:
composer require pestphp/pest-plugin-browser --dev
npm install playwright@latest
npx playwright install
Команда npx playwright install скачает бинарники Chromium, Firefox и WebKit (для Safari) — это десятки мегабайт, разумно кэшировать их в CI. После этого в проекте можно использовать функцию visit() внутри любого теста.
Полезно сразу добавить в .gitignore папку со скриншотами от упавших тестов:
tests/Browser/Screenshots
Первый рабочий тест: вход в систему
Покажем, как выглядит реалистичный сценарий: пользователь логинится через форму, попадает на дашборд, в системе диспатчится событие. Pest 4 позволяет описать это в одном коротком тесте — без отдельной инфраструктуры Dusk, без ручного управления драйвером и без серверов в фоне.
<?php
use App\Events\UserLoggedIn;
use App\Models\User;
use Illuminate\Support\Facades\Event;
it('логинит пользователя через форму', function () {
Event::fake();
User::factory()->create([
'email' => 'shirley@example.com',
'password' => bcrypt('secret-password'),
]);
$page = visit('/')->on()->mobile()->firefox();
$page->click('Войти')
->assertUrlIs(config('app.url') . '/login')
->assertSee('Вход в аккаунт')
->fill('email', 'shirley@example.com')
->fill('password', 'secret-password')
->click('Отправить')
->assertSee('Панель управления');
$this->assertAuthenticated();
Event::assertDispatched(UserLoggedIn::class);
});
Обратите внимание на три вещи. Во-первых, тест работает с фабрикой и в transactional базе — для этого подключите RefreshDatabase в tests/Pest.php. Во-вторых, прямо в одном тесте смешиваются браузерные ассерты (assertSee, assertUrlIs) и серверные (assertAuthenticated, Event::assertDispatched). В-третьих, цепочка ->on()->mobile()->firefox() переключает viewport и движок без отдельной конфигурации.
Локаторы, ввод, ожидания
Pest 4 умеет искать элементы по тексту, CSS-селекторам, id, классам и data-атрибутам. Достаточно одной строки:
$page->click('Сохранить'); // ссылка/кнопка с таким текстом
$page->click('.btn-primary'); // CSS-класс
$page->click('@save-button'); // data-test="save-button"
$page->click('#submit'); // id
Ввод данных, выбор, чекбоксы, файлы, drag-and-drop — всё через тот же объект страницы:
$page->type('email', 'shirley@example.com')
->typeSlowly('search', 'laravel', delay: 80)
->select('country', 'RU')
->check('terms')
->attach('avatar', storage_path('app/fixtures/avatar.jpg'))
->press('Зарегистрироваться')
->assertPathIs('/welcome');
По умолчанию Pest ждёт элементы до 5 секунд — достаточно для большинства SPA. Если приложение медленное, таймаут поднимается в tests/Pest.php:
pest()->browser()->timeout(10000); // 10 секунд
Smoke-тесты и проверка JS-ошибок
Самый дешёвый способ закрыть «ничего не сломалось при деплое» — пройтись браузером по основным страницам и убедиться, что нет JS-ошибок и логов в консоли. Pest 4 умеет это в две строки:
it('главные страницы не падают', function () {
$routes = ['/', '/about', '/pricing', '/docs', '/blog'];
visit($routes)->assertNoSmoke();
});
Метод assertNoSmoke() — это сокращение для assertNoJavaScriptErrors() + assertNoConsoleLogs(). Для проверок доступности есть assertNoAccessibilityIssues() с настраиваемым уровнем строгости — от critical до minor.
Визуальная регрессия
Если в проекте дизайн-критичные страницы (лендинги, отчёты, документация), визуальная регрессия отлавливает то, что не ловят функциональные тесты — съехавший отступ, потерянную иконку, изменившийся цвет. Pest 4 делает это так:
it('лендинги выглядят как ожидается', function () {
$pages = visit(['/', '/about', '/pricing']);
$pages->assertScreenshotMatches();
});
При первом запуске Pest сохранит эталонные скриншоты. На последующих прогонах сравнит новые с эталоном и упадёт при отличии. Эталоны хранятся рядом с тестами и коммитятся в репозиторий.
Параллельный запуск и шардирование в CI
Браузерные тесты — самые медленные в проекте, поэтому ускорение прогона критично. Pest 4 поддерживает обе стратегии параллельности.
Локально и в одном CI-джобе работает --parallel — тесты бегут в нескольких процессах на одной машине:
./vendor/bin/pest --parallel
На CI с матрицей джобов помогает шардирование: тестовый набор делится на N частей, каждый шард прогоняется на своей машине параллельно. В GitHub Actions это выглядит так:
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Pest
run: ./vendor/bin/pest --parallel --shard=${{ matrix.shard }}/4
Так браузерный сьют, который локально идёт 8 минут, в CI укладывается в 2-3 минуты — без аренды более жирных раннеров.
Отладка падающих тестов
Когда тест падает, главное — увидеть, что было на странице. Pest 4 предлагает несколько инструментов. Флаг --debug при падении открывает реальный браузер и ставит тест на паузу, чтобы можно было потыкать DOM руками:
./vendor/bin/pest --debug
Внутри теста доступны точечные хелперы:
$page->screenshot(fullPage: true); // снимок всей страницы
$page->screenshotElement('#chart'); // снимок отдельного элемента
$page->debug(); // пауза + headed-режим
$page->tinker(); // Tinker-сессия в контексте страницы
Метод tinker() — почти магия: пока браузер на паузе, в терминале открывается полноценный Laravel Tinker, через который можно дёрнуть модели, кэш, очереди — и сразу увидеть результат на странице.
Итоги
Pest 4 закрывает старую боль PHP-стека: e2e-тестирование больше не требует отдельной инфраструктуры. Один composer require, один npx playwright install — и в проекте появляются полноценные браузерные тесты, которые умеют работать с фабриками, базой, событиями и очередями Laravel так же, как обычные unit-тесты.
Если в команде давно лежит идея «надо бы покрыть критичные сценарии браузером, но Dusk страшно поднимать» — Pest 4 это та точка, в которой её можно реализовать за день. Начать имеет смысл с трёх простых шагов: smoke-теста по основным маршрутам, одного полного сценария логина или оплаты и шардированного CI на 4 джоба. Дальше — по мере роста проекта — добавлять визуальную регрессию и тесты на разных viewport’ах.