Introduction
Laravel ships with a first-class testing experience. Every new application comes preconfigured with either Pest or PHPUnit, a dedicated tests/ directory, and Artisan commands to generate and run tests. You do not need to install anything to write your first test.
Key Concepts
- Test runner: Laravel 13 supports both Pest (expressive, functional style) and PHPUnit (traditional class-based). You choose during
laravel new. - Feature tests: Exercise the HTTP layer end-to-end — routes, middleware, controllers, database.
- Unit tests: Exercise a single class or method in isolation, without booting the full framework.
php artisan test: Runs the full suite with pretty output and parallel testing support.
Real World Context
Every Laravel shop runs tests in CI on every push. When an endpoint breaks, you want the failure to surface in seconds — not after a user reports it. Tests also act as a living specification: reading tests/Feature tells you exactly what the app is supposed to do.
Deep Dive
Laravel generates this structure out of the box:
tests/
├── Feature/ # HTTP-level tests (recommended default)
│ └── ExampleTest.php
├── Unit/ # Isolated class tests
│ └── ExampleTest.php
├── Pest.php # Pest configuration (if Pest was selected)
└── TestCase.php # Base class all PHPUnit tests extend
Generate tests with Artisan:
bash# Feature test (recommended for most cases) php artisan make:test CreateProjectTest # Unit test (no framework bootstrap) php artisan make:test CalculatePriceTest --unit # Pest-flavored test php artisan make:test CreateProjectTest --pest
A minimal Pest feature test dispatches a real HTTP request through your router and middleware:
php<?php use function Pest\Laravel\get; it('shows the welcome page', function () { get('/') ->assertStatus(200) ->assertSee('Laravel'); });
Notice how the chained assertions read like a specification: visit /, expect HTTP 200, expect the word 'Laravel' in the body. Failures surface with readable diffs that tell you exactly what changed.
Run the suite:
bashphp artisan test # Filter to a single file php artisan test --filter=CreateProjectTest # Parallel execution across CPU cores php artisan test --parallel
Common Pitfalls
- Testing against your development database — Always configure a separate test database (SQLite
:memory:works well) inphpunit.xmlso tests cannot corrupt real data. - Mocking when you should integrate — Laravel's testing story is fast enough that most controllers should be covered by real feature tests. Mocks become load-bearing lies the moment the real code drifts.
Best Practices
- Prefer feature tests — They exercise middleware, routing, validation, and controllers together. One feature test often replaces five unit tests.
- Name tests after behavior —
it('rejects posts without a title')beatstestStore()for readability.
Summary
- Laravel 13 ships with Pest or PHPUnit and a
tests/directory out of the box. - Generate tests with
php artisan make:testand run them withphp artisan test. - Feature tests are the recommended default — they cover the full HTTP request cycle.
- Use a separate in-memory database so tests never touch real data.
Code Examples
// tests/Feature/WelcomePageTest.php (Pest)
<?php
use function Pest\Laravel\get;
it('renders the welcome page', function () {
get('/')
->assertStatus(200)
->assertSee('Laravel');
});
it('returns 404 for an unknown route', function () {
get('/does-not-exist')->assertStatus(404);
});