Skip to main content
Chapter 7 Testing and Production Deployment

Feature Testing with PHPUnit

26 min read Lesson 25 / 28

Writing Reliable Tests

Tests are the safety net that lets you refactor and deploy with confidence. Laravel wraps PHPUnit with powerful HTTP testing utilities.

Your First Feature Test

php artisan make:test PostTest
class PostTest extends TestCase
{
    use RefreshDatabase;

    public function test_guest_can_view_published_posts(): void
    {
        $post = Post::factory()->published()->create();

        $response = $this->get(route('posts.show', $post));

        $response->assertStatus(200);
        $response->assertSee($post->title);
    }

    public function test_guest_cannot_view_draft(): void
    {
        $post = Post::factory()->draft()->create();
        $this->get(route('posts.show', $post))->assertStatus(404);
    }

    public function test_user_can_create_post(): void
    {
        $user = User::factory()->create();

        $response = $this->actingAs($user)->post(route('posts.store'), [
            'title' => 'Test Post',
            'slug' => 'test-post',
            'body' => str_repeat('Content. ', 20),
            'category_id' => Category::factory()->create()->id,
        ]);

        $response->assertRedirect();
        $this->assertDatabaseHas('posts', [
            'title' => 'Test Post',
            'user_id' => $user->id,
        ]);
    }

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

        $this->actingAs($other)
            ->get(route('posts.edit', $post))
            ->assertStatus(403);
    }

    public function test_validation_rejects_empty_title(): void
    {
        $user = User::factory()->create();

        $this->actingAs($user)
            ->post(route('posts.store'), [])
            ->assertSessionHasErrors(['title', 'body']);
    }
}

Running Tests

php artisan test                        # All tests
php artisan test --filter=PostTest      # Specific file
php artisan test --filter=test_guest    # Specific test

Always use RefreshDatabase trait and factories for clean, isolated tests.