Pest is the default test framework for new Laravel projects. This guide covers why it exists, how it compares to PHPUnit, and how to use its most valuable features — from the expect() API to architecture testing.
Since Laravel 11, laravel new defaults to Pest as the test framework. PHPUnit has been the PHP testing standard for years, and Pest is built on top of it. So why the change?The answer is friction. Writing a PHPUnit test requires defining a class, extending TestCase, and naming a method with a specific prefix. Pest removes all of that. You write what you want to test, and the framework stays out of the way.
Pest runs on top of PHPUnit, so all existing PHPUnit tests continue to work. You can migrate gradually — or not at all.
The goal is to lower the barrier to writing tests. When tests are fast to write, developers write more of them. Pest reduces the ceremony so you can focus on describing behavior rather than structuring classes.
class AuthTest extends TestCase{ public function test_user_can_log_in(): void { $user = User::factory()->create(); $response = $this->post('/login', [ 'email' => $user->email, 'password' => 'password', ]); $response->assertRedirect('/dashboard'); }}
The Pest test() function takes a description and a closure. No class, no method naming convention. You can also use it(), which reads as a natural sentence: it('redirects to dashboard after login', ...).
arch() is one of Pest’s most unique features. It lets you write tests that verify the structure of your codebase — not just behavior.
test('models extend Eloquent', function () { arch()->expect('App\Models') ->toExtend(Illuminate\Database\Eloquent\Model::class);});test('controllers are declared final', function () { arch()->expect('App\Http\Controllers') ->toBeFinal();});test('service classes do not depend on controllers', function () { arch()->expect('App\Services') ->not->toUse('App\Http\Controllers');});
Architecture tests analyze source code statically — no HTTP requests, no database queries. They run very fast and can catch structural regressions early.
Pest also ships with preset rule sets for common frameworks:
test('codebase follows Laravel conventions', function () { arch()->preset()->laravel();});
The laravel() preset validates model naming, controller inheritance, middleware structure, and other common Laravel conventions in a single call.
Pest inherits from Laravel’s TestCase, so every Laravel testing helper works without modification: actingAs(), assertDatabaseHas(), HTTP testing methods, and more.
use App\Models\Post;use App\Models\User;test('user can delete their own post', function () { $user = User::factory()->create(); $post = Post::factory()->for($user)->create(); $response = $this ->actingAs($user) ->delete("/posts/{$post->id}"); $response->assertRedirect('/posts'); $this->assertModelMissing($post);});test('user cannot delete another user\'s post', function () { $user = User::factory()->create(); $otherUser = User::factory()->create(); $post = Post::factory()->for($otherUser)->create(); $this ->actingAs($user) ->delete("/posts/{$post->id}") ->assertForbidden(); $this->assertModelExists($post);});
./vendor/bin/pest # Run all tests./vendor/bin/pest --filter="user" # Filter by name./vendor/bin/pest --parallel # Run in parallelphp artisan test # Artisan command (uses Pest automatically in Laravel 11+)
php artisan test automatically detects Pest when it is installed and runs tests through it. You do not need to call ./vendor/bin/pest separately unless you need Pest-specific flags.
Pest is a higher-level layer on top of PHPUnit, not a replacement. Its deep Laravel integration and low migration cost make it worth adopting even in existing projects. The arch() feature alone can catch structural regressions that traditional unit tests miss.
Pest documentation
Datasets, code coverage, parallel execution, and the full API reference.