Laravel Database Testing

Laravel Database Testing

Laravel provides dedicated helpers for asserting on your database state within tests.

1 - Database Assertions

// Assert a record exists in the database
$this->assertDatabaseHas('posts', [
    'title'   => 'Hello World',
    'user_id' => $user->id,
]);

// Assert a record does NOT exist
$this->assertDatabaseMissing('posts', ['title' => 'Deleted Post']);

// Assert count of records
$this->assertDatabaseCount('posts', 5);

// Assert a model has been soft deleted
$this->assertSoftDeleted($post);

2 - Testing with Factories

public function test_post_can_be_deleted(): void
{
    $user = User::factory()->create();
    $post = Post::factory()->for($user)->create();

    $response = $this->actingAs($user)
        ->delete(route('posts.destroy', $post));

    $response->assertRedirect(route('posts.index'));
    $this->assertSoftDeleted($post);
}

public function test_user_cannot_delete_others_post(): void
{
    $owner = User::factory()->create();
    $other = User::factory()->create();
    $post  = Post::factory()->for($owner)->create();

    $this->actingAs($other)
        ->delete(route('posts.destroy', $post))
        ->assertForbidden();

    $this->assertDatabaseHas('posts', ['id' => $post->id]);
}

3 - Mocking External Services

use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeMail;

public function test_welcome_email_is_sent_on_registration(): void
{
    Mail::fake();

    $this->post(route('register'), [
        'name'                  => 'Alice',
        'email'                 => '[email protected]',
        'password'              => 'password',
        'password_confirmation' => 'password',
    ]);

    Mail::assertSent(WelcomeMail::class, fn ($mail) => $mail->hasTo('[email protected]'));
}

Note: Use Mail::fake(), Event::fake(), Queue::fake(), and Notification::fake() to prevent real side-effects in tests while still asserting that those actions were triggered.

-Tip-