Skip to content
Feature Testing Laravel Applications with Pest

Feature Testing Laravel Applications with Pest

Al Amin Ahamed

Al Amin Ahamed

Senior Engineer

8 min read
𝕏 in

Pest is the testing framework I wish existed when I started with Laravel. It's PHPUnit under the hood, but the expressive syntax makes tests a pleasure to read and write.

Why Pest Over PHPUnit Directly

Pest tests read like documentation:

PHP
// PHPUnit
public function test_published_posts_are_visible(): void
{
    $post = Post::factory()->published()->create();
    $response = $this->get('/writing');
    $response->assertStatus(200);
    $response->assertSee($post->title);
}

// Pest
it('shows published posts on the writing page', function () {
    $post = Post::factory()->published()->create();

    get('/writing')
        ->assertOk()
        ->assertSee($post->title);
});

Test Structure I Use

tests/
  Feature/
    Site/
      WritingTest.php  -- public reading experience
      AiChatTest.php   -- AI playground
    Admin/
      PostsTest.php    -- CRUD admin flows
  Unit/
    SlugServiceTest.php

Database Strategy

I use RefreshDatabase for feature tests but LazilyRefreshDatabase in suites that don't modify the DB. For read-heavy tests, seeding once per suite saves significant time:

PHP
beforeAll(function () {
    Post::factory(20)->published()->create();
    Post::factory(5)->draft()->create();
});

it('paginates at 10 posts', function () {
    get('/writing')->assertSee('Newer');
});

Testing Auth Flows

PHP
it('redirects guests away from admin', function () {
    get('/admin/posts')->assertRedirect('/login');
});

it('allows admins to create posts', function () {
    $admin = User::factory()->admin()->create();

    actingAs($admin)
        ->post('/admin/posts', Post::factory()->make()->toArray())
        ->assertRedirect();

    expect(Post::count())->toBe(1);
});

Datasets for Edge Cases

PHP
dataset('invalid slugs', [
    'empty string' => [''],
    'only spaces'  => ['   '],
    'too long'     => [str_repeat('a', 256)],
]);

it('rejects invalid slugs', function (string $slug) {
    actingAs(User::factory()->admin()->create())
        ->post('/admin/posts', ['slug' => $slug])
        ->assertInvalid('slug');
})->with('invalid slugs');
Share 𝕏 in
Al Amin Ahamed

Al Amin Ahamed

Senior software engineer & AI practitioner. Laravel, PHP, WordPress plugins, WooCommerce extensions.

About me →

One email a month. No noise.

What I shipped, what I read, occasional deep dive. Unsubscribe anytime.