Laravel Testing
Agent Workflow (MANDATORY)
Before ANY implementation, spawn 3 agents in parallel, one Agent call each with a name:
- fuse-ai-pilot:explore-codebase - Analyze existing test patterns
- fuse-ai-pilot:research-expert - Verify Pest/PHPUnit docs via Context7
- mcp__context7__query-docs - Check assertion and mocking patterns
After implementation, run fuse-ai-pilot:sniper for validation.
Overview
| Type |
Purpose |
Location |
| Feature |
HTTP, full stack |
tests/Feature/ |
| Unit |
Isolated classes |
tests/Unit/ |
| Arch |
Code architecture |
tests/Arch.php |
Decision Guide: Test Type
What to test?
├── HTTP endpoint → Feature test
├── Service/Policy logic → Unit test
├── Code structure → Arch test
├── External API → Mock with Http::fake()
├── Mail/Queue/Event → Use Fakes
└── Database state → assertDatabaseHas()
Decision Guide: Test Strategy
Coverage strategy?
├── Feature tests (70%) → Critical flows
├── Unit tests (25%) → Business logic
├── E2E tests (5%) → User journeys
└── Arch tests → Structural rules
Critical Rules
- Use RefreshDatabase for database isolation
- Use factories for test data (never raw inserts)
- Mock external services - Never call real APIs
- Test edge cases - Empty, null, boundaries
- Run parallel -
pest --parallel for speed
Reference Guide
Pest Basics
| Topic |
Reference |
When to Consult |
| Pest Syntax |
pest-basics.md |
it(), test(), describe() |
| Datasets |
pest-datasets.md |
Data providers, hooks |
| Architecture |
pest-arch.md |
arch() tests |
HTTP Testing
| Topic |
Reference |
When to Consult |
| Requests |
http-requests.md |
GET, POST, headers |
| JSON API |
http-json.md |
API assertions |
| Authentication |
http-auth.md |
actingAs, guards |
| Assertions |
http-assertions.md |
Status, redirects |
Database Testing
| Topic |
Reference |
When to Consult |
| Basics |
database-basics.md |
RefreshDatabase |
| Factories |
database-factories.md |
Factory patterns |
| Assertions |
database-assertions.md |
DB assertions |
Mocking
| Topic |
Reference |
When to Consult |
| Services |
mocking-services.md |
Mock, spy |
| Fakes |
mocking-fakes.md |
Mail, Queue, Event |
| HTTP & Time |
mocking-http.md |
Http::fake, travel |
Other
| Topic |
Reference |
When to Consult |
| Console |
console-tests.md |
Artisan tests |
| Troubleshooting |
troubleshooting.md |
Common errors |
Templates
| Template |
When to Use |
| FeatureTest.php.md |
HTTP feature test |
| UnitTest.php.md |
Service unit test |
| ArchTest.php.md |
Architecture test |
| ApiTest.php.md |
REST API test |
| PestConfig.php.md |
Pest configuration |
Quick Reference
// Feature test
it('creates a post', function () {
$user = User::factory()->create();
$this->actingAs($user)
->postJson('/api/posts', ['title' => 'Test'])
->assertCreated()
->assertJsonPath('data.title', 'Test');
$this->assertDatabaseHas('posts', ['title' => 'Test']);
});
// With dataset
it('validates emails', function (string $email, bool $valid) {
// test logic
})->with([
['valid@test.com', true],
['invalid', false],
]);
// Mock facade
Mail::fake();
// ... action ...
Mail::assertSent(OrderShipped::class);
Commands
# Run all tests
php artisan test
# Pest directly
./vendor/bin/pest
# Parallel execution
./vendor/bin/pest --parallel
# Filter by name
./vendor/bin/pest --filter "user can"
# Coverage
./vendor/bin/pest --coverage --min=80
# Profile slow tests
./vendor/bin/pest --profile
Best Practices
DO
- Use
RefreshDatabase trait
- Follow AAA pattern (Arrange-Act-Assert)
- Name tests descriptively
- Test one thing per test
- Use factories for data
DON'T
- Create test dependencies
- Call real external APIs
- Use production database
- Skip edge cases
Laravel 13 Notes
PHPUnit 12 + Pest 4
Laravel 13 requires PHPUnit 12 and supports Pest 4. PHP attributes replace docblock annotations.
use PHPUnit\Framework\Attributes\Test;
use Illuminate\Foundation\Testing\Attributes\Seed;
use Illuminate\Foundation\Testing\Attributes\Seeder;
#[Seed] // runs DatabaseSeeder
#[Seeder(UserSeeder::class)] // runs a targeted seeder
final class UserTest extends TestCase
{
#[Test]
public function it_creates_user(): void { /* ... */ }
}
Str cache reset
Laravel 13 automatically resets Str caches (random, slug) between tests to avoid state leak. No manual setup required.
Migration from Pest 3
pest --init regenerates Pest.php with the new API
- Datasets now support native PHP generators
expect()->toBeInstanceOf() → strict typing required
1---2name: laravel-testing3description: Use when testing controllers, services, or models, or implementing TDD on Laravel 13 with Pest 4 / PHPUnit 12.4---56<objective>7Covers Laravel 13 testing with Pest 4 and PHPUnit 12: feature tests (HTTP,8full stack), unit tests (isolated classes), and architecture tests; Pest9syntax (it/test/describe), datasets; HTTP testing (requests, JSON10assertions, auth/actingAs, status/redirect assertions); database testing11(RefreshDatabase, factories, DB assertions); mocking (services, spies,12Mail/Queue/Event fakes, Http::fake, time travel); console/Artisan command13tests; and PHPUnit-attribute-based seeding (#[Seed], #[Seeder]).14</objective>1516# Laravel Testing1718## Agent Workflow (MANDATORY)1920Before ANY implementation, spawn 3 agents in parallel, one `Agent` call each with a `name`:21221. **fuse-ai-pilot:explore-codebase** - Analyze existing test patterns232. **fuse-ai-pilot:research-expert** - Verify Pest/PHPUnit docs via Context7243. **mcp__context7__query-docs** - Check assertion and mocking patterns2526After implementation, run **fuse-ai-pilot:sniper** for validation.2728---2930## Overview3132| Type | Purpose | Location |33|------|---------|----------|34| **Feature** | HTTP, full stack | `tests/Feature/` |35| **Unit** | Isolated classes | `tests/Unit/` |36| **Arch** | Code architecture | `tests/Arch.php` |3738---3940## Decision Guide: Test Type4142```43What to test?44├── HTTP endpoint → Feature test45├── Service/Policy logic → Unit test46├── Code structure → Arch test47├── External API → Mock with Http::fake()48├── Mail/Queue/Event → Use Fakes49└── Database state → assertDatabaseHas()50```5152---5354## Decision Guide: Test Strategy5556```57Coverage strategy?58├── Feature tests (70%) → Critical flows59├── Unit tests (25%) → Business logic60├── E2E tests (5%) → User journeys61└── Arch tests → Structural rules62```6364---6566## Critical Rules67681. **Use RefreshDatabase** for database isolation692. **Use factories** for test data (never raw inserts)703. **Mock external services** - Never call real APIs714. **Test edge cases** - Empty, null, boundaries725. **Run parallel** - `pest --parallel` for speed7374---7576## Reference Guide7778### Pest Basics7980| Topic | Reference | When to Consult |81|-------|-----------|-----------------|82| **Pest Syntax** | [pest-basics.md](references/pest-basics.md) | it(), test(), describe() |83| **Datasets** | [pest-datasets.md](references/pest-datasets.md) | Data providers, hooks |84| **Architecture** | [pest-arch.md](references/pest-arch.md) | arch() tests |8586### HTTP Testing8788| Topic | Reference | When to Consult |89|-------|-----------|-----------------|90| **Requests** | [http-requests.md](references/http-requests.md) | GET, POST, headers |91| **JSON API** | [http-json.md](references/http-json.md) | API assertions |92| **Authentication** | [http-auth.md](references/http-auth.md) | actingAs, guards |93| **Assertions** | [http-assertions.md](references/http-assertions.md) | Status, redirects |9495### Database Testing9697| Topic | Reference | When to Consult |98|-------|-----------|-----------------|99| **Basics** | [database-basics.md](references/database-basics.md) | RefreshDatabase |100| **Factories** | [database-factories.md](references/database-factories.md) | Factory patterns |101| **Assertions** | [database-assertions.md](references/database-assertions.md) | DB assertions |102103### Mocking104105| Topic | Reference | When to Consult |106|-------|-----------|-----------------|107| **Services** | [mocking-services.md](references/mocking-services.md) | Mock, spy |108| **Fakes** | [mocking-fakes.md](references/mocking-fakes.md) | Mail, Queue, Event |109| **HTTP & Time** | [mocking-http.md](references/mocking-http.md) | Http::fake, travel |110111### Other112113| Topic | Reference | When to Consult |114|-------|-----------|-----------------|115| **Console** | [console-tests.md](references/console-tests.md) | Artisan tests |116| **Troubleshooting** | [troubleshooting.md](references/troubleshooting.md) | Common errors |117118### Templates119120| Template | When to Use |121|----------|-------------|122| [FeatureTest.php.md](references/templates/FeatureTest.php.md) | HTTP feature test |123| [UnitTest.php.md](references/templates/UnitTest.php.md) | Service unit test |124| [ArchTest.php.md](references/templates/ArchTest.php.md) | Architecture test |125| [ApiTest.php.md](references/templates/ApiTest.php.md) | REST API test |126| [PestConfig.php.md](references/templates/PestConfig.php.md) | Pest configuration |127128---129130## Quick Reference131132```php133// Feature test134it('creates a post', function () {135 $user = User::factory()->create();136137 $this->actingAs($user)138 ->postJson('/api/posts', ['title' => 'Test'])139 ->assertCreated()140 ->assertJsonPath('data.title', 'Test');141142 $this->assertDatabaseHas('posts', ['title' => 'Test']);143});144145// With dataset146it('validates emails', function (string $email, bool $valid) {147 // test logic148})->with([149 ['valid@test.com', true],150 ['invalid', false],151]);152153// Mock facade154Mail::fake();155// ... action ...156Mail::assertSent(OrderShipped::class);157```158159---160161## Commands162163```bash164# Run all tests165php artisan test166167# Pest directly168./vendor/bin/pest169170# Parallel execution171./vendor/bin/pest --parallel172173# Filter by name174./vendor/bin/pest --filter "user can"175176# Coverage177./vendor/bin/pest --coverage --min=80178179# Profile slow tests180./vendor/bin/pest --profile181```182183---184185## Best Practices186187### DO188- Use `RefreshDatabase` trait189- Follow AAA pattern (Arrange-Act-Assert)190- Name tests descriptively191- Test one thing per test192- Use factories for data193194### DON'T195- Create test dependencies196- Call real external APIs197- Use production database198- Skip edge cases199200---201202## Laravel 13 Notes203204### PHPUnit 12 + Pest 4205Laravel 13 requires **PHPUnit 12** and supports **Pest 4**. PHP attributes replace docblock annotations.206207```php208use PHPUnit\Framework\Attributes\Test;209use Illuminate\Foundation\Testing\Attributes\Seed;210use Illuminate\Foundation\Testing\Attributes\Seeder;211212#[Seed] // runs DatabaseSeeder213#[Seeder(UserSeeder::class)] // runs a targeted seeder214final class UserTest extends TestCase215{216 #[Test]217 public function it_creates_user(): void { /* ... */ }218}219```220221### Str cache reset222Laravel 13 automatically resets `Str` caches (random, slug) between tests to avoid state leak. No manual setup required.223224### Migration from Pest 3225- `pest --init` regenerates `Pest.php` with the new API226- Datasets now support native PHP generators227- `expect()->toBeInstanceOf()` → strict typing required