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.phpPSR-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
Applicationper test (via the bootstrap pathbootstrap/app.phpreturns). - 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.
UnitandFeature. Run both withphp artisan test, or pick one with--testsuite=Unit. - Coverage scope. The
<source>block limits coverage toapp/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_URLis explicitly neutralized by being set to empty — a security tweak (PR #6761, v12.12.0) that prevents an inheritedDB_URLfrom 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 requiredcomposer 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:
- Checks out the code (
actions/checkout@v6). - Sets up PHP via
shivammathur/setup-php@v2with extensionsdom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite. composer install --prefer-dist --no-interaction --no-progress.cp .env.example .envandphp artisan key:generate.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_casewith atest_prefix. Both bundled examples follow this. PHPUnit also supports@testannotations and aTestsuffix; pick one and stick with it. - Use
RefreshDatabasewhen 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;RefreshDatabasealso 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— thetestscript and the dev requirementphpunit/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/andtests/Unit/. - Customize the base test case in
tests/TestCase.php(e.g. applyRefreshDatabaseglobally, register custom assertions). - Add new test suites by extending
phpunit.xml's<testsuites>block (e.g. aBrowser/suite for Dusk, anIntegration/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.