🧪 Что это
Laravel TDD Workflow — это набор практик и соглашений для тестирования Laravel-приложений в стиле Test-Driven Development (TDD). Skill охватывает полный цикл: от выбора фреймворка (PHPUnit или Pest) до настройки базы данных, фабрик, фейков и измерения покрытия. Цель — довести покрытие unit + feature тестов до 80%+ и сделать тесты быстрыми, изолированными и детерминированными.
⚙️ Как работает
🔁 Red-Green-Refactor
Классический TDD-цикл:
- Red — написать тест, который падает.
- Green — внести минимальные изменения, чтобы тест прошёл.
- Refactor — улучшить код, не ломая зелёных тестов.
🧩 Тестовые слои
- Unit — тестирование чистых PHP-классов, value object'ов, сервисов без зависимостей от Laravel.
- Feature — проверка HTTP-эндпоинтов, аутентификации, валидации, политик доступа.
- Integration — тесты, затрагивающие базу данных, очереди и внешние сервисы одновременно.
Выбор слоя зависит от того, что проверяется:
- Чистая бизнес-логика → Unit.
- HTTP, auth, ответы → Feature.
- Связка DB + очередь + внешний API → Integration.
🗄️ Стратегия работы с БД
Skill рекомендует три трейта для управления миграциями:
RefreshDatabase — миграции выполняются один раз за прогон (через статический флаг), каждый тест оборачивается в транзакцию. Для in-memory SQLite миграции запускаются перед каждым тестом. Используется по умолчанию.
DatabaseTransactions — если схема уже накачена, нужен только откат после каждого теста.
DatabaseMigrations — полная перемиграция перед каждым тестом; дорого, но гарантирует чистоту.
🧪 Выбор фреймворка
- Pest — предпочтительный вариант для новых тестов (более лаконичный синтаксис).
- PHPUnit — если проект уже стандартизирован на нём или требуются специфичные инструменты PHPUnit.
🏭 Фабрики и состояния
Для генерации тестовых данных используются Eloquent factories. Для граничных случаев определяются states:
$user = User::factory()->state(['role' => 'admin'])->create();
🎭 Фейки для побочных эффектов
Чтобы изолировать тесты от реальных сервисов, применяются Laravel-фасады:
Bus::fake() — для джобов.
Queue::fake() — для отложенных задач.
Mail::fake() и Notification::fake() — для уведомлений.
Event::fake() — для доменных событий.
Http::fake() — для внешних HTTP-запросов.
Пример проверки очереди:
Queue::fake();
dispatch(new SendOrderConfirmation($order->id));
Queue::assertPushed(SendOrderConfirmation::class);
🔐 Аутентификация (Sanctum)
Для тестирования API с Sanctum:
Sanctum::actingAs($user);
$response = $this->getJson('/api/projects');
$response->assertOk();
🧾 Inertia-тесты
Если фронтенд использует Inertia.js, вместо сырых JSON-ассертов применяется assertInertia:
$response->assertInertia(fn (AssertableInertia $page) =>
$page->component('Dashboard')
->where('user.id', $user->id)
->has('projects')
);
📊 Покрытие кода
- Цель: 80%+ для unit + feature тестов.
- В CI используется
pcov или XDEBUG_MODE=coverage.
🛠️ Команды для запуска
php artisan test
vendor/bin/phpunit
vendor/bin/pest
⚙️ Конфигурация
- В
phpunit.xml можно задать DB_CONNECTION=sqlite и DB_DATABASE=:memory: для ускорения тестов.
- Для тестов рекомендуется отдельный
.env-файл, чтобы не задеть реальные данные.
🎯 Когда использовать
- При разработке новых фич или эндпоинтов в Laravel.
- При исправлении багов или рефакторинге — чтобы убедиться, что ничего не сломалось.
- Для тестирования Eloquent-моделей, политик, джобов, нотификаций.
- Если проект ещё не стандартизирован на PHPUnit — начинать с Pest.
⚠️ Важно знать
- Не используйте
assertDatabaseHas для ручных SQL-запросов — предпочитайте встроенные ассерты.
- Тесты должны быть изолированы и детерминированы — никакой зависимости от порядка выполнения.
- Для быстрых тестов используйте in-memory SQLite, но помните: некоторые фичи (например, JSON-поля) могут вести себя иначе, чем в PostgreSQL/MySQL.
- Не злоупотребляйте
DatabaseMigrations — это замедляет прогон; RefreshDatabase почти всегда достаточно.
Комментарии
Комментариев пока нет. Будьте первым.