Вийшла наступна мажорна версія Pest PHP - Pest v5! Нуно Мадуро анонсував Pest 5 на сцені Laracon US 2026 у Бостоні та випустив тег v5.0.0 під час конференції. Цей реліз представляє TIA-движок, який drastично скорочує час виконання тестових наборів. Pest 5 вимагає PHP 8.4 та PHPUnit 13 і об'єднує набір офіційних плагінів для AI-агентів, PHPStan, Rector та інших інструментів.
TIA-движок: революція у швидкості тестів
TIA-движок змінює правила гри у швидкості тестування. Тестовий набір Laravel Cloud із понад 19 000 тестів скоротився з 3 хвилин до 5 секунд - про це повідомив Тейлор Отвелл у соцмережах.
Ось що нового у Pest 5 загалом:
- TIA-движок: перезапускає лише тести, на які вплинули ваші останні зміни, а решту відтворює з кешу без втрати точності покриття
- Плагін Agent: надає вашому AI-агенту одну команду для перевірки, чи працює зміна всередині реального тестового набору
- Плагін Evals: оцінює вихідні дані LLM через детерміновані перевірки та AI-скорери через звичний API
expect()
- Плагін PHPStan: навчає PHPStan розуміти
it(), test(), expect() та $this всередині тестових замикань
- Плагін Rector: 60 правил, що переписують сирі PHP-твердження як Pest-матчери та обробляють оновлення між мажорними версіями
- Нові очікування для email, ULID, IP-адрес та інших форматів, які складно перевіряти вручну
- PHP 8.4 та PHPUnit 13 як нова базова лінія
Аналіз впливу тестів із TIA-движком
TIA - скорочення від Test Impact Analysis (аналіз впливу тестів). Перший запуск записує, які тести торкаються яких файлів, а кожен наступний запуск виконує лише тести, на які вплинули ваші зміни, відтворюючи кешовані результати для решти.
Додайте --tia до будь-якого виклику:
./vendor/bin/pest --parallel --tia
Підсумок показує, як був виконаний запуск:
Tests: 774 passed (2658 assertions, 7 affected, 2 uncached, 765 replayed)
Duration: 3.92s
Відтворення з кешу - це не пропущений тест. Кожен кешований результат зберігає все, що створив тест, включно з точними рядками та гілками, які він покрив, тому звіти --coverage та пороги --min поводяться так, ніби виконався весь набір.
Цікаво те, що граф залежностей розуміє більше, ніж PHP-файли. Змініть міграцію - і Pest перезапустить лише тести, що запитували цю таблицю. Відредагуйте спільний JS-компонент - і він пройде по графу модулів Vite, щоб знайти Inertia-сторінки, що його імпортують. Торкніться Blade-шаблону - і перезапустяться тести, що його рендерили. Він виявляє Laravel, Symfony, Livewire, Inertia та браузерні ресурси через Composer, тому нічого конфігурувати не потрібно.
Pest також нормалізує файли перед хешуванням, видаляючи пробіли, коментарі та docblock'и. Після запуску Pint або редагування README генерується ідентичний хеш, і нічого не виконується.
Зауважте: запис базової лінії потребує драйвера покриття, тому вам знадобиться PCOV або Xdebug. На великому наборі перший запуск займає час, тому команди можуть налаштувати CI для запису базової лінії один раз на злиття в main, а всі інші її завантажують.
Ви можете налаштувати поведінку в tests/Pest.php:
pest()->tia()
->always() // запускати TIA при кожному виклику
->locally() // обмежити always() лише локальним середовищем
->baselined() // завантажувати спільну базову лінію з CI
->filtered(); // завантажувати лише файли зі змінами
Верифікація змін AI-агентів із плагіном Agent
Кодувальні агенти легко пишуть код, але не мають хорошого способу підтвердити, що він працює. Плагін Agent надає їм такий спосіб. Встановіть його як залежність для розробки:
composer require pestphp/pest-plugin-agent --dev
Він додає опцію --agent, що запускає фрагмент коду всередині повноцінного Pest-тесту з вашими фабриками, RefreshDatabase та Laravel-фейками точно так само, як вони були б у feature-тесті:
./vendor/bin/pest --agent='$user = \App\Models\User::factory()->create(); $this->actingAs($user)->get("/dashboard")->assertOk();'
Використовуйте одинарні лапки, щоб ваша оболонка не інтерполювала $user, і повністю кваліфікуйте імена класів. Кожен фрагмент виконується як ізольований тест під назвою "verify", і ви можете передати --agent більше одного разу.
Якщо у вас встановлений плагін для браузерного тестування, той самий зонд може керувати справжнім браузером і перевіряти побічні ефекти на backend, які перевірка лише UI не побачить:
./vendor/bin/pest --agent='\Illuminate\Support\Facades\Mail::fake(); visit("/contact")->type("email", "test@example.com")->press("Send")->assertSee("Message sent");'
Pest чітко заявляє, що це призначено для швидкого зворотного зв'язку під час роботи, а не як заміна регресійним тестам, які ви комітите.
Тестування виводу LLM із плагіном Evals
Надішліть однаковий промпт моделі двічі - і отримаєте два різні відповіді, що робить твердження рівності практично непридатними. Плагін Evals натомість оцінює якість виводу, використовуючи той самий API expect():
composer require pestphp/pest-plugin-evals --dev
use App\Agents\CapitalCityAgent;
it('answers capital city questions correctly', function (): void {
expect(CapitalCityAgent::class)
->prompt('What is the capital of France?')
->toContain('Paris') // детермінована перевірка
->toBeRelevant() // LLM як суддя
->toBeSimilar('Paris, France'); // семантична подібність
});
Кожна оцінка викликає реальну модель, тому вони пропускаються при звичайному запуску і нічого не коштують. Передайте --evals, коли хочете їх виконати:
./vendor/bin/pest # evals пропущено, без API-викликів
./vendor/bin/pest --evals # реальна модель, усі скорери активні
Детерміновані очікування (toContain(), toMatch(), toBe(), toBeJson()) не потребують драйвера взагалі. Оцінювані приймають поріг між 0.0 та 1.0, який за замовчуванням дорівнює 0.7. Ви отримуєте toBeRelevant() для оцінки відповіді відносно промпту, toBeSafe() для перевірки небезпечного контенту та стійкості до prompt injection, toBeFactual() для порівняння з еталонною відповіддю, toBeSimilar() для подібності на основі embeddings та toPassJudge() для критеріїв, які ви описуєте звичайною англійською. Є також toHaveToolCalls() і toFollowTrajectory() для підтвердження, що агент звернувся до правильних інструментів у правильному порядку, repeat() для багаторазового сэмплування промпту та toPassScorer(), якщо ви воліли б написати власний скорер.
Оцінювання за замовчуванням працює через Laravel AI. Обидва драйвери - judge та embeddings - приймають замикання або власні класи, тому інший провайдер - це кілька рядків у tests/Pest.php.
Підтримка PHPStan для Pest-тестів
Офіційна підтримка PHPStan була однією з найбільш запитуваних функцій від спільноти протягом тривалого часу. Зі скриньки PHPStan не має уявлення, що таке it(), test() або expect(), і не знає, на що посилається $this всередині тестового замикання:
composer require pestphp/pest-plugin-phpstan --dev
composer require phpstan/phpstan --dev
Плагін читає вашу конфігурацію Pest.php, щоб зрозуміти, чим має бути $this, і підтримує стилі uses(TestCase::class)->in(...) та pest()->extend(...)->use(...)->in(...). Якщо ви використовуєте phpstan/extension-installer, він реєструється сам; інакше додайте його до phpstan.neon:
includes:
- vendor/pestphp/pest-plugin-phpstan/extension.neon
Типи проходять через ланцюжки очікувань звідти, тому toBeInt() звужує int|string, властивості, які ви призначаєте в beforeEach(), типізовані на $this, а очікування вищого порядку на кшталт expect($user)->name->toBe('Nuno') резолвляться з базового значення. Він також ловить очікування, що ніколи не можуть пройти:
expect(10)->toStartWith('1'); // int ніколи не може задовольнити toStartWith()
Поверх виведення типів ви отримуєте правила, що враховують Pest, для статичних тестових замикань, $this всередині beforeAll(), дублікатів описів тестів та невалідних посилань throws() і covers(). Кожне правило має стабільний ідентифікатор на кшталт pest.expectation.impossible, тому ви можете вибірково їх ігнорувати.
Рефакторинг тестів із Rector
Плагін Rector постачається з 60 правилами, розділеними на набори стилю коду та оновлення версій:
composer require pestphp/pest-plugin-rector --dev
composer require rector/rector --dev
Додайте набір до вашого rector.php:
use Pest\Rector\Set\PestSetList;
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([__DIR__ . '/tests'])
->withSets([
PestSetList::CODING_STYLE,
]);
Набір стилю коду переписує сирі PHP-твердження як матчери та з'єднує надлишкові очікування в ланцюжки:
-expect(count($array))->toBe(5);
-expect(array_key_exists('id', $array))->toBeTrue();
+expect($array)->toHaveCount(5)
+ ->toHaveKey('id');
Інші правила конвертують PHPUnit-твердження в expect(), перетворюють expect($value > 10)->toBeTrue() на toBeGreaterThan(10), згортають твердження індексованих масивів у sequence() та заміняють try/catch-тести виключень на toThrow(). Набори версій під PestLevelSetList обробляють мажорні оновлення та є кумулятивними. Попередньо перегляньте все за допомогою vendor/bin/rector process --dry-run, перш ніж запустити на повну.
Часово-збалансований шардинг
Часово-збалансований шардинг не є новинкою в 5.0, але варто про нього згадати, якщо ви пропустили це, адже він з'явився непомітно в Pest v4.6.0 ще у квітні. Розділення набору між CI-машинами за кількістю файлів зазвичай залишає один шард працювати довго після того, як інші завершилися. Pest розподіляє за записаним часом виконання.
Запишіть таймінги один раз:
./vendor/bin/pest --update-shards
Потім закомітьте tests/.pest/shards.json, і --shard підхопить його самостійно:
./vendor/bin/pest --shard=1/4
Додайте тестові файли перед оновленням таймінгів - і ваш набір все одно виконається. Нові файли розподіляються рівномірно, поки відомі залишаються часово-збалансованими, і Pest попереджає вас, що файл застарів.
Нові очікування для email, ULID та IP-адрес
Вісім нових матчерів покривають перевірки форматів, які незручно писати вручну:
expect('nuno@pestphp.com')->toBeEmail();
expect('01ARZ3NDEKTSV4RRFFQ69G5FAV')->toBeUlid();
expect('192.168.1.1')->toBeIpAddress();
expect('00:1a:2b:3c:4d:5e')->toBeMacAddress();
expect('example.com')->toBeHostname();
expect('example.co.uk')->toBeDomain();
expect('Zm9vYmFy')->toBeBase64();
expect('deadbeef')->toBeHexadecimal();
Кожен приймає опціональне користувацьке повідомлення про помилку, і ви можете заперечити будь-який із них через not.
Оновлення до Pest 5
Pest 5 вимагає PHP 8.4 або вище та працює на PHPUnit 13, і саме друга вимога створить більшість труднощів при оновленні, а не сам Pest. Ознайомтеся зі списком змін PHPUnit 13 щодо будь-чого, що впливає на ваш набір. Власний гід оновлення Pest оцінює роботу приблизно у дві хвилини та не документує жодних breaking changes на рівні API, окрім підвищення версій.
Для більшості наборів це один рядок у composer.json:
- "pestphp/pest": "^4.0",
+ "pestphp/pest": "^5.0",
Поки ви тут, перемістіть будь-які Pest-підтримувані плагіни на ^5.0. Дві речі, які варто знати, перш ніж використовувати головну функцію: TIA-движок потребує PCOV або Xdebug для запису базової лінії, а спільне використання базової лінії в CI вимагає автентифікованого GitHub CLI і працює лише на GitHub.
Посилання
Ви можете прочитати повний анонс Pest 5 та гід оновлення на вебсайті Pest разом із документацією для TIA-движка, плагінів Agent, Evals, PHPStan та Rector.
Pest створений і підтримується Нуно Мадуро. Ви можете знайти реліз v5.0.0, повний diff та вихідний код на GitHub у репозиторії pestphp/pest.