Automated tests protect Laravel applications from regressions during refactors and deployments. Laravel ships with PHPUnit configured and provides testing helpers for HTTP, database, events, and mail out of the box.
Project Test Setup
Tests live in the tests/ directory. phpunit.xml defines suites for Unit and Feature tests with environment overrides.
The APP_ENV=testing environment uses array cache and sqlite :memory: database by default for speed and isolation.
Step 1: Run the default test suite
Execute php artisan test or ./vendor/bin/phpunit. Laravel boots the application kernel for feature tests.
A passing example test confirms your environment is wired. Add tests alongside every new feature.
php artisan test
php artisan test --filter=ExampleTest
Step 2: Configure the testing database
In phpunit.xml or .env.testing set DB_CONNECTION=sqlite and DB_DATABASE=:memory: for fast in-memory tests.
For MySQL integration tests, use a dedicated testing database and RefreshDatabase trait to migrate before each test.
<env name="DB_DATABASE" value=":memory:"/>
Step 3: Install Pest optionally
Run composer require pestphp/pest –dev –with-all-dependencies then php artisan pest:install for expressive syntax.
Pest runs on PHPUnit under the hood. Existing PHPUnit tests continue to work alongside Pest files.
composer require pestphp/pest --dev
php artisan pest:install
Step 4: Use RefreshDatabase trait
Add use RefreshDatabase; to feature tests that touch the database. Laravel migrates fresh schema per test class or method.
Combine with DatabaseMigrations for slower but explicit migration runs when debugging schema issues.
use IlluminateFoundationTestingRefreshDatabase;
Feature and HTTP Tests
Feature tests hit routes end-to-end with $this->get(), $this->post(), and assertStatus(), assertRedirect(), assertJson().
Authenticate users with actingAs($user) to test authorization without manual session hacking.
Step 1: Test a GET route returns 200
Create tests/Feature/PostControllerTest.php. Call $response = $this->get('/posts') and $response->assertStatus(200).
Use assertSee() for Blade content or assertJson() for API responses.
$response = $this->get('/posts');
$response->assertStatus(200);
Step 2: POST with validation assertions
Submit invalid data with $this->post('/posts', []) and assertSessionHasErrors(['title']).
Valid submissions should assertRedirect() and assertDatabaseHas('posts', ['title' => 'Hello']).
$this->post('/posts', [])->assertSessionHasErrors('title');
Step 3: Authenticate with actingAs
Create a user with User::factory()->create() then $this->actingAs($user)->get('/dashboard').
Test forbidden access with assertForbidden() when a guest or wrong role hits protected routes.
$user = User::factory()->create();
$this->actingAs($user)->get('/dashboard')->assertOk();
Step 4: Use factories for test data
Model factories in database/factories generate realistic records. Post::factory()->count(10)->create() seeds list pages.
Define states like Post::factory()->published() for scenario-specific attributes.
Post::factory()->count(5)->create();
Mocking and Unit Tests
Unit tests isolate classes without booting the full HTTP stack. Mock external APIs and facades to keep tests fast and deterministic.
Laravel fakes for Event, Mail, Notification, Queue, and Storage prevent side effects during assertions.
Step 1: Fake the Mail facade
Call Mail::fake() before triggering an action. Assert Mail::assertSent(WelcomeEmail::class) afterward.
Use assertNothingSent() on negative paths where email should not fire.
Mail::fake();
// act...
Mail::assertSent(WelcomeEmail::class);
Step 2: Mock a service with Mockery
Bind a mock in the container: $this->mock(PaymentGateway::class, fn($m) => $m->shouldReceive('charge')->once());
Constructor injection resolves the mock automatically in the controller under test.
$this->mock(PaymentGateway::class, function ($mock) {
$mock->shouldReceive('charge')->once()->andReturn(true);
});
Step 3: Write a Pest test with expectations
Pest tests use test('description', function() { … }) with expect($value)->toBeTrue() syntax.
Use beforeEach() for shared setup like actingAs or factory seeding across tests in a file.
test('guests cannot create posts', function () {
$this->post('/posts', [])->assertRedirect('/login');
});
Step 4: Run tests in CI pipelines
Add php artisan test to GitHub Actions or GitLab CI with sqlite and cached composer dependencies.
Fail builds on test errors. Parallel testing with php artisan test –parallel speeds large suites.
php artisan test --parallel
Aadyanex builds production-ready Laravel applications with clean architecture, secure APIs, and long-term support.
