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.