Найбільша шкода від повільного набору в тому, як змінюється поведінка команди: тести перестають запускати локально, pull request чекають CI пів години, а червоний прогін перезапускають, не читаючи. Тому на співбесіді Senior-рівня чекають порядку дій, і починається він з вимірювання. vendor/bin/pest --profile показує найповільніші тести, а час по джобах CI показує, де губляться хвилини поза тестами: установка залежностей, міграції, збирання фронтенду.
Найчастіше половину часу дає невелика частина тестів, і причини в них банальні. Кожен User::factory() хешує пароль, тож BCRYPT_ROUNDS=4 у phpunit.xml економить десятки мілісекунд на користувача. DatabaseMigrations проганяє міграції перед кожним тестом, тоді як RefreshDatabase мігрує один раз і відкочує транзакції, а schema:dump замінює сотні міграцій одним дампом. Паузи в коді ретраїв пишуть через Sleep::for() замість sleep(), і тоді Sleep::fake() їх гасить. Мережу відрізають Http::fake() і Http::preventStrayRequests(). Фабрики, що через has()->count(50) створюють сотні рядків заради одного асерту, переписують на мінімальні дані.
Друга частина роботи структурна. Варіації правил переносять з feature-тестів у швидкі unit-тести домену, на рівні HTTP лишають сценарії, браузерні тести тримають для критичних шляхів. Найдорожчі перевірки, як повне мутаційне тестування чи довгий e2e, ідуть за розкладом уночі, а на pull request лише змінене.
Лише після цього вмикають паралельність: --parallel у Pest (на ParaTest) усередині машини і --shard між джобами CI. Умова в неї одна: ізоляція. Laravel створює для кожного процесу окрему тестову базу, але файли в storage, ключі в Redis, порти й статичні властивості лишаються спільними, і для них є хуки ParallelTesting::setUpProcess() і setUpTestCase(). Тести, що залежали від порядку чи від даних сусіда, після ввімкнення паралельності почнуть падати: проблема була й раніше, паралельність лише зробила її видимою.
Надійність тримається на політиці. Flaky-тест це баг з власником і задачею: його відтворюють (->repeat(), --order-by=random з seed, --parallel), знаходять причину і виправляють. Автоматичний повтор у CI робить набір зеленим, але прибирає єдиний сигнал про гонки, які потім проявляться на проді. Набір цінний рівно доти, доки червоний прогін означає справжню проблему.
// tests/Pest.php: Laravel і база лише для Feature, Unit іде без фреймворку.
pest()->extend(Tests\TestCase::class)
->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
->in('Feature');
// tests/TestCase.php: дешеві налаштування, що знімають хвилини з прогону.
abstract class TestCase extends Illuminate\Foundation\Testing\TestCase
{
protected function setUp(): void
{
parent::setUp();
Http::preventStrayRequests(); // справжня мережа в тестах = падіння, а не 5 с таймауту
Sleep::fake(); // Sleep::for(3)->seconds() не чекає по-справжньому
}
}
// AppServiceProvider::boot(): окрема тека файлів для кожного паралельного процесу.
ParallelTesting::setUpTestCase(function (int $token) {
config(['filesystems.disks.local.root' => storage_path("framework/testing/disk-{$token}")]);
});
// phpunit.xml: <env name="BCRYPT_ROUNDS" value="4"/>, CACHE_STORE=array, QUEUE_CONNECTION=sync
// Локально: vendor/bin/pest --profile найповільніші тести
// vendor/bin/pest --parallel
// CI-матриця: vendor/bin/pest --parallel --shard=1/4 (2/4, 3/4, 4/4 в інших джобах)
Відповідайте як план з цифрами: «спершу `--profile` і час джоб; зазвичай 10% тестів дають половину часу. Потім дешеві налаштування середовища, потім `--parallel` з перевіркою ізоляції, і шардування в CI. Окремо правило: flaky-тест це баг з власником». План з порядком дій звучить сильніше за перелік прапорців.