Introduction to Laravel Testing

+15 Mana ✨

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:

bash
php artisan test

# Filter to a single file
php artisan test --filter=CreateProjectTest

# Parallel execution across CPU cores
php artisan test --parallel

Common Pitfalls

  1. Testing against your development database — Always configure a separate test database (SQLite :memory: works well) in phpunit.xml so tests cannot corrupt real data.
  2. 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

  1. Prefer feature tests — They exercise middleware, routing, validation, and controllers together. One feature test often replaces five unit tests.
  2. Name tests after behavior — it('rejects posts without a title') beats testStore() for readability.

Summary

  • Laravel 13 ships with Pest or PHPUnit and a tests/ directory out of the box.
  • Generate tests with php artisan make:test and run them with php 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

php
// 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);
});
✓ Completed