1.5 KiB
Testing Best Practices
Use LazilyRefreshDatabase Over RefreshDatabase
RefreshDatabase runs all migrations every test run even when the schema hasn't changed. LazilyRefreshDatabase only migrates when needed, significantly speeding up large suites.
Use Model Assertions Over Raw Database Assertions
Incorrect: $this->assertDatabaseHas('users', ['id' => $user->id]);
Correct: $this->assertModelExists($user);
More expressive, type-safe, and fails with clearer messages.
Use Factory States and Sequences
Named states make tests self-documenting. Sequences eliminate repetitive setup.
Incorrect: User::factory()->create(['email_verified_at' => null]);
Correct: User::factory()->unverified()->create();
Use Exceptions::fake() to Assert Exception Reporting
Instead of withoutExceptionHandling(), use Exceptions::fake() to assert the correct exception was reported while the request completes normally.
Call Event::fake() After Factory Setup
Model factories rely on model events (e.g., creating to generate UUIDs). Calling Event::fake() before factory calls silences those events, producing broken models.
Incorrect: Event::fake(); $user = User::factory()->create();
Correct: $user = User::factory()->create(); Event::fake();
Use recycle() to Share Relationship Instances Across Factories
Without recycle(), nested factories create separate instances of the same conceptual entity.
Ticket::factory()
->recycle(Airline::factory()->create())
->create();