Open-Source Wikis

/

Laravel

/

Systems

/

Tests

laravel/laravel

Tests

Active contributors: Taylor Otwell, Nuno Maduro

Purpose

tests/ holds the PHPUnit test suite. The skeleton ships with two example tests — one feature, one unit — plus a base TestCase for the feature suite. The phpunit.xml at the repo root defines two test suites and overrides every persistence env var so tests run in isolation.

Directory layout

tests/
├── Feature/
│   └── ExampleTest.php
├── Unit/
│   └── ExampleTest.php
└── TestCase.php

PSR-4 autoloading from composer.json (autoload-dev block):

"autoload-dev": {
    "psr-4": {
        "Tests\\": "tests/"
    }
}

So tests/Feature/ExampleTest.php is Tests\Feature\ExampleTest.

tests/TestCase.php

The base class for feature tests:

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    //
}

Extending Illuminate\Foundation\Testing\TestCase does the heavy lifting:

  • Boots a fresh Application per test (via the bootstrap path bootstrap/app.php returns).
  • Provides HTTP test helpers ($this->get('/'), $this->post('/api/...'), $this->actingAs($user)).
  • Resets the container and rebinds singletons between tests.

If you need cross-test setup (database refresh, queue assertions, custom traits), add them to this class. Many Laravel apps customize TestCase to apply CreatesApplication, RefreshDatabase, or domain-specific helpers.

tests/Feature/ExampleTest.php

namespace Tests\Feature;

// use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_the_application_returns_a_successful_response(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

The single example boots the framework, dispatches a GET /, and asserts a 200 response. The RefreshDatabase trait import is commented out — uncomment and add use RefreshDatabase; to the class to run each test inside a transaction that's rolled back at the end.

tests/Unit/ExampleTest.php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;

class ExampleTest extends TestCase
{
    public function test_that_true_is_true(): void
    {
        $this->assertTrue(true);
    }
}

The unit suite extends PHPUnit\Framework\TestCase directly — not Laravel's framework-aware base. This is intentional: unit tests should not boot the framework. They're for testing pure-PHP behaviour without container, config, or DB access.

phpunit.xml (root)

<phpunit ... bootstrap="vendor/autoload.php" colors="true">
    <testsuites>
        <testsuite name="Unit">
            <directory>tests/Unit</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory>tests/Feature</directory>
        </testsuite>
    </testsuites>
    <source>
        <include>
            <directory>app</directory>
        </include>
    </source>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="APP_MAINTENANCE_DRIVER" value="file"/>
        <env name="BCRYPT_ROUNDS" value="4"/>
        <env name="BROADCAST_CONNECTION" value="null"/>
        <env name="CACHE_STORE" value="array"/>
        <env name="DB_CONNECTION" value="sqlite"/>
        <env name="DB_DATABASE" value=":memory:"/>
        <env name="DB_URL" value=""/>
        <env name="MAIL_MAILER" value="array"/>
        <env name="QUEUE_CONNECTION" value="sync"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="PULSE_ENABLED" value="false"/>
        <env name="TELESCOPE_ENABLED" value="false"/>
        <env name="NIGHTWATCH_ENABLED" value="false"/>
    </php>
</phpunit>

What this gives you:

  • Two suites. Unit and Feature. Run both with php artisan test, or pick one with --testsuite=Unit.
  • Coverage scope. The <source> block limits coverage to app/ so you don't get noise from configs and migrations.
  • Hermetic env. Every persistent backend (DB, cache, session, queue, mail, broadcast) is swapped to in-memory or null drivers. DB_URL is explicitly neutralized by being set to empty — a security tweak (PR #6761, v12.12.0) that prevents an inherited DB_URL from pointing tests at a real database.
  • Pulse / Telescope / Nightwatch off. If your application installs any of those observability packages, they're force-disabled during tests so they don't intercept events or pollute traces.

The DB_URL="" line was added 2026-02-26 (commit 41e7b8f0) to address an issue where developers ran tests after setting a DB_URL in their shell and accidentally hit production.

Running tests

composer test                 # clears config, runs artisan test
php artisan test              # full suite, pretty output
php artisan test --testsuite=Unit
php artisan test --testsuite=Feature
php artisan test --filter=ExampleTest
php artisan test --parallel   # parallel test runner (uses brianium/paratest if installed)
php artisan test --coverage   # XDebug or PCOV required

composer test runs php artisan config:clear --ansi @no_additional_args before the test command. The @no_additional_args directive (added in v13.4.0, PR #6799) prevents extra CLI arguments from leaking into the config:clear invocation.

CI

.github/workflows/tests.yml runs the same suite across PHP 8.3, 8.4, 8.5:

strategy:
  fail-fast: true
  matrix:
    php: [8.3, 8.4, 8.5]

For each matrix entry the workflow:

  1. Checks out the code (actions/checkout@v6).
  2. Sets up PHP via shivammathur/setup-php@v2 with extensions dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite.
  3. composer install --prefer-dist --no-interaction --no-progress.
  4. cp .env.example .env and php artisan key:generate.
  5. php artisan test.

There is no separate lint or static-analysis job in CI. Pint runs are expected to be a pre-commit hook on developer machines (or via vendor/bin/pint manually).

Test patterns to follow

  • Feature tests extend Tests\TestCase — gives you HTTP helpers and a booted app.
  • Unit tests extend PHPUnit\Framework\TestCase — keep them framework-free.
  • Method names are snake_case with a test_ prefix. Both bundled examples follow this. PHPUnit also supports @test annotations and a Test suffix; pick one and stick with it.
  • Use RefreshDatabase when a test touches the DB. The in-memory SQLite (DB_DATABASE=:memory:) is recreated per process, so tests in different files don't see each other's tables; RefreshDatabase also wraps each individual test in a transaction.
  • Use Storage::fake(), Mail::fake(), Queue::fake(), Bus::fake(), Event::fake() for assertion-friendly fakes of the corresponding facades.

Integration points

  • composer.json — the test script and the dev requirement phpunit/phpunit ^12.5.12.
  • phpunit.xml — suites, env overrides, coverage source.
  • .github/workflows/tests.yml — CI matrix.
  • bootstrap/app.php — the same bootstrap path drives the test application instance.

Entry points for modification

  • Add Feature/Unit tests under tests/Feature/ and tests/Unit/.
  • Customize the base test case in tests/TestCase.php (e.g. apply RefreshDatabase globally, register custom assertions).
  • Add new test suites by extending phpunit.xml's <testsuites> block (e.g. a Browser/ suite for Dusk, an Integration/ suite for slower tests).
  • Tweak CI matrix in .github/workflows/tests.yml (add PHP versions, add a separate job for coverage, etc.).

Key source files

File Purpose
tests/TestCase.php Base class for feature tests (extends framework TestCase)
tests/Feature/ExampleTest.php Boots the framework, asserts GET / returns 200
tests/Unit/ExampleTest.php Boots only PHPUnit; trivial assertion
phpunit.xml Suites, env overrides, coverage source
.github/workflows/tests.yml CI matrix (PHP 8.3, 8.4, 8.5)

See How to contribute → Testing for testing playbooks and patterns.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Tests – Laravel wiki | Factory