Laravel test writing with PHPUnit

Carlos Mendez Jan 2026
2 tabs
<?php

namespace Tests\Feature;

use App\Models\Post;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PostTest extends TestCase
{
    use RefreshDatabase;

    public function test_user_can_view_posts()
    {
        $posts = Post::factory()->count(3)->create();

        $response = $this->get('/posts');

        $response->assertStatus(200);
        $response->assertSee($posts[0]->title);
    }

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

        $response = $this->actingAs($user)->post('/posts', [
            'title' => 'Test Post',
            'body' => 'This is a test post body that is long enough.',
        ]);

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

    public function test_guest_cannot_create_post()
    {
        $response = $this->post('/posts', [
            'title' => 'Test Post',
            'body' => 'Test body',
        ]);

        $response->assertRedirect('/login');
    }

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

        $response = $this->actingAs($user)->post('/posts', [
            'body' => 'Test body',
        ]);

        $response->assertSessionHasErrors('title');
    }

    public function test_user_can_only_delete_own_posts()
    {
        $user = User::factory()->create();
        $otherUser = User::factory()->create();
        $post = Post::factory()->create(['user_id' => $otherUser->id]);

        $response = $this->actingAs($user)->delete("/posts/{$post->id}");

        $response->assertForbidden();
        $this->assertDatabaseHas('posts', ['id' => $post->id]);
    }
}
2 files · php Explain with highlit

Laravel's testing suite built on PHPUnit makes writing tests straightforward. Feature tests simulate HTTP requests and assert responses, while unit tests focus on individual methods. I use database transactions or RefreshDatabase to reset the database after each test. Factories generate test data consistently. The actingAs() method authenticates users for protected routes. Assertions like assertStatus(), assertSee(), assertDatabaseHas() verify behavior. I mock external services with facades or the Mock class. Test organization follows the AAA pattern—Arrange, Act, Assert. Running php artisan test executes the suite with pretty output. Comprehensive tests catch regressions and enable confident refactoring.