---
title: "Знайдіть несподівані тестові дані за допомогою Fuzz для Pest"
url: https://laravelukraine.com/blog/znaidit-nespodivani-testovi-dani-za-dopomogoiu-fuzz-dlia-pest
date: 2026-09-09
source: https://laravel-news.com/pest-fuzz?utm_medium=feed&utm_source=feedpress.me&utm_campaign=Feed%3A+laravelnews
---

# Знайдіть несподівані тестові дані за допомогою Fuzz для Pest

Тест для парсера зазвичай перевіряє валідний рядок і порожній. Фаз-тестування шукає збої за межами цих написаних вручну випадків, генеруючи вхідні дані та передаючи їх у ваш код.

За допомогою [Fuzz](https://github.com/JonPurvis/fuzz) - пакета від [Джона Пурвіса](https://github.com/JonPurvis) - ви можете робити це всередині тесту [Pest 5](https://laravel-news.com/pest-5). Пакет використовує PHP-Fuzzer від nikic для зміни рядків, які ви надаєте, і повідомлення про будь-які збої через Pest.

## Що означає Coverage-Guided

Фазер змінює вхідні дані, додаючи, видаляючи або замінюючи частини рядка. Потім він запускає ваш код зі зміненими даними.

Coverage-guided фазер також відстежує, якими маршрутами ці дані проходять через ваш код. Коли вхідні дані досягають раніше недослідженого маршруту, фазер зберігає їх і використовує для створення нових варіацій. Збережена колекція називається корпусом.

Уявімо, що парсер перевіряє наявність відкриваючої дужки перед читанням решти рядка. Багато випадкових даних не пройдуть цю першу перевірку. Коли вхідні дані проходять її, фазер може продовжувати змінювати цей рядок для тестування коду, який йде далі.

[PHP-Fuzzer збирає цей зворотний зв'язок](https://github.com/nikic/PHP-Fuzzer#instrumentation), відстежуючи переходи між блоками PHP-коду та приблизну частоту їх виконання. Fuzz обробляє це автоматично; вам не потрібен Xdebug або опція `--coverage` Pest.

Ви обираєте код для виклику. Fuzz пробує згенеровані рядки, поки не знайде збій або не досягне ліміту запусків. Успішний прохід означає, що він не знайшов збоїв у цих спробах; помилки все ще можуть залишатися.

## Спробуємо в Pest

Fuzz потребує PHP 8.4+ та Pest 5. Встановіть його через Composer:

```
composer require jonpurvis/fuzz --dev
```

Припустимо, ваш застосунок читає обмеження швидкості на кшталт `100/60s`, що означає 100 запитів за 60 секунд. Цей хелпер конвертує його в запити за секунду, але не валідує вхідні дані:

```php
namespace App;
final class RateLimit
{
    public static function perSecond(string $spec): float
    {
        $parts = explode('/', $spec);
        $count = (int) $parts[0];
        $window = (int) rtrim($parts[1] ?? '1s', 's');
        return $count / $window;
    }
}
```

Збережіть хелпер у `app/RateLimit.php`. У `tests/Unit/RateLimitTest.php` визначте функцію поза тестом для виклику хелпера:

```php
use App\RateLimit;
use function Fuzz\fuzz;

$target = static function (string $input): void {
    RateLimit::perSecond($input);
};

test('rate limit spec parser never fatals', function () use ($target): void {
    fuzz($target)
        ->seed(['100/60s', '5/1s', '1000/3600s'])
        ->withDictionary(['/', 's', '0', '1'])
        ->runs(2000)
        ->maxLen(16)
        ->run('rate-limit-parser');
});
```

Рядки, передані в `seed()`, є початковими прикладами. `withDictionary()` надає фрагменти, які фазер може вставляти, не обмежуючи його цими символами. `runs(2000)` встановлює бюджет пошуку, а `maxLen(16)` обмежує згенеровані рядки до 16 байтів.

Дайте кожному фаз-тесту власну назву в `run()`. Fuzz використовує цю назву для розділення збережених даних і файлів збоїв.

Залишайте функцію `$target` поза `test()`, як показано. При перевірці Fuzz v1.0.1 цей обгортка записувала покриття, тоді як передача `Closure::fromCallable()` безпосередньо не записувала нічого. Fuzz запускає функцію в окремому PHP-процесі, де згенерований Pest клас тестів недоступний. Визначення її поза `test()` уникає залежності від цього класу.

Запустіть тест як зазвичай:

```
./vendor/bin/pest tests/Unit/RateLimitTest.php
```

Під час нашого запуску Fuzz знайшов `5/`. Відсутнє вікно стає порожнім рядком, який PHP приводить до `0`. Ділення на нього викидає `DivisionByZeroError`, і Pest повідомляє, що тест провалився. Ваш запуск може знайти інші дані або зайняти іншу кількість спроб.

Fuzz зберігає дані, що призвели до збою, у `.pest/fuzz-crashes/` за замовчуванням. Ви можете прочитати цей файл для відтворення збою. Після виправлення парсера додайте ці дані до іменованого датасету та перевірте очікувану поведінку, щоб та сама помилка не пройшла непоміченою.

## Що перевіряє тест

Цей приклад провалюється, коли хелпер падає, але неправильне повернене значення може пройти. Щоб це впіймати, додайте очікування Pest всередину цільової функції, щоб вона перевіряла кожні згенеровані дані. Наприклад, тест для енкодера та декодера міг би перевіряти, що кодування з наступним декодуванням рядка повертає оригінальне значення.

Fuzz повідомляє про помилки типу `TypeError`, а також непридушені PHP-попередження та повідомлення. Звичайні винятки ігноруються за замовчуванням, включаючи винятки валідації Laravel. Метод `allow()` дозволяє звузити цей список, коли несподівані винятки мають провалювати тест. Ви також можете встановити тайм-аут для кожних даних через `timeout()`, що потребує розширення PHP `pcntl`.

## Використання Fuzz поряд з вашими тестами

Зберігайте свої [звичайні тести та датасети](https://laravel-news.com/how-to-start-testing) для відомих випадків та очікуваних результатів. Fuzz корисний, коли код приймає більше можливих вхідних даних, ніж ви можете розумно перелічити, наприклад, парсер, що читає текст від користувачів.

Хелпер обмеження швидкості має мало розгалужень. Керування покриттям більш корисне в парсерах з послідовними етапами валідації, де проходження однієї перевірки дозволяє фазеру досягти наступної.

Почніть з невеликого бюджету запусків у вашому звичайному наборі. Якщо вам потрібен довший пошук, запускайте його в заплановій CI-роботі. [README Fuzz](https://github.com/JonPurvis/fuzz#readme) охоплює решту опцій, включаючи словники та власні директорії зберігання.
