Belajar Laravel - Testing (Pest/PHPUnit) & Dusk
Episode 21 of 24

Belajar Laravel - Testing (Pest/PHPUnit) & Dusk

Mengamankan kualitas dengan automated testing: feature & unit tests, sintaks Pest yang lebih ekspresif, database testing dengan RefreshDatabase, dan browser E2E dengan Laravel Dusk.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Kita sudah membangun aplikasi selama 20 episode — sekarang saatnya memastikan semuanya tetap bekerja saat aplikasi tumbuh. Automated testing adalah jaring pengaman yang membuat perubahan kode tidak menakutkan: satu tombol php artisan test memberi tahu kalian apakah sesuatu rusak.

Di episode ini kita pelajari dua lapis testing: unit/feature test (logika & HTTP) dan browser test dengan Dusk (alur nyata di browser). Laravel 13 secara default memakai Pest sebagai test framework.

Unit vs Feature Test

JenisFokusKecepatan
Unit testSatu class/method, tanpa framework penuhSangat cepat
Feature testHTTP request end-to-end, controller + DBSedang

Laravel 13 menyediakan keduanya via php artisan make:test:

Buat feature test
php artisan make:test PostTest
tests/Feature/PostTest.php
<?php
 
use App\Models\Post;
use App\Models\User;
 
test('published posts are visible on index', function () {
    $user = User::factory()->create();
 
    $this->actingAs($user)
        ->get('/posts')
        ->assertOk()
        ->assertSee('Post Pertama');
});

$this->actingAs($user) mensimulasikan login; assertOk()/assertSee() memverifikasi response. Feature test menjalankan seluruh request lifecycle — route, middleware, controller, view — jadi sangat dekat dengan perilaku nyata.

Sintaks Pest yang Ekspresif

Pest adalah layer di atas PHPUnit dengan sintaks test() dan describe():

Grouping dengan describe
describe('posts', function () {
    test('guests are redirected to login', function () {
        $this->get('/posts/create')->assertRedirect('/login');
    });
 
    test('authenticated user can create post', function () {
        $user = User::factory()->create();
 
        $this->actingAs($user)
            ->post('/posts', ['title' => 'Halo', 'body' => 'Isi'])
            ->assertRedirect('/posts');
 
        $this->assertDatabaseHas('posts', ['title' => 'Halo']);
    });
});

assertDatabaseHas memverifikasi data benar-benar masuk database — kombinasi yang sangat kuat untuk menguji alur create. Fungsi helper juga bisa dibuat sendiri dan dipakai antar test.

Note

Pest dan PHPUnit bukan dua dunia terpisah: Pest berjalan di atas PHPUnit, jadi kalian bisa memakai keduanya dalam satu proyek. Jika proyek dibuat tanpa Pest, konversi ke Pest adalah composer require pestphp/pest + php artisan pest:install. Laravel 13 default ke Pest saat laravel new.

Database Testing: RefreshDatabase

Test yang menyentuh database harus terisolasi. Trait RefreshDatabase menangani ini: setiap test memulai dari database yang bersih, tanpa mengganggu data development kalian.

RefreshDatabase
use Illuminate\Foundation\Testing\RefreshDatabase;
 
test('post slug is unique', function () {
    Post::factory()->create(['slug' => 'duplicate']);
    Post::factory()->create(['slug' => 'duplicate']);
})->expectsException(QueryException::class);

Di balik layar, RefreshDatabase menangani transaction rollback per-test sehingga cepat, dan menjalankan migrasi sekali. Pastikan konfigurasi test database terpisah dari database development — di .env.testing atau phpunit.xml:

phpunit.xml
<env name="DB_DATABASE" value=":memory:"/>

SQLite :memory: adalah pilihan paling cepat untuk test lokal.

Testing API, Authorization, dan Queue

API Test

Test endpoint API
use App\Models\User;
use Laravel\Sanctum\Sanctum;
 
test('api returns paginated posts', function () {
    Sanctum::actingAs(User::factory()->create());
 
    $this->getJson('/api/posts')
        ->assertOk()
        ->assertJsonStructure(['data', 'links', 'meta']);
});

Authorization Test (dari episode 11)

Test policy
test('non-owner cannot update post', function () {
    $owner = User::factory()->create();
    $post = Post::factory()->for($owner)->create();
    $intruder = User::factory()->create();
 
    $this->actingAs($intruder)
        ->put("/posts/{$post->id}", ['title' => 'Hack'])
        ->assertForbidden();
});

Queue Test

Assert job didispatch
use Illuminate\Support\Facades\Queue;
 
Queue::fake();
 
Post::factory()->create();
 
Queue::assertPushed(SendWelcomeEmail::class);

Queue::fake() mencegah job benar-benar dieksekusi dan memungkinkan assertion apakah job di-dispatch — pola penting untuk menguji alur tanpa efek samping nyata.

Laravel Dusk: Browser E2E Testing

Feature test tidak menjalankan JavaScript. Dusk menguji alur nyata di browser — login, klik, form, dan interaksi JS:

Install Dusk
composer require --dev laravel/dusk
php artisan dusk:install
Jalankan test Dusk
php artisan dusk
tests/Browser/AuthTest.php
<?php
 
namespace Tests\Browser;
 
use App\Models\User;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
 
test('user can login', function () {
    $user = User::factory()->create([
        'email' => 'test@example.com',
        'password' => bcrypt('password123'),
    ]);
 
    $this->browse(function (Browser $browser) use ($user) {
        $browser->visit('/login')
            ->type('email', $user->email)
            ->type('password', 'password123')
            ->press('Login')
            ->assertPathIs('/dashboard')
            ->assertSee('Dashboard');
    });
});

Dusk mengendalikan browser sungguhan (Chrome/Chromium via ChromeDriver). Ini lapisan terakhir pengujian: jika alur auth dan CRUD lolos di browser sungguhan, keyakinan terhadap aplikasi sangat tinggi.

Alur E2E yang Perlu Diuji

  • Register → verifikasi email → login.
  • Login gagal dengan kredensial salah.
  • CRUD post (create → tampil → edit → hapus).
  • Proteksi route: guest diarahkan ke login.
  • Authorization: non-owner melihat 403.

Penutup

Inti yang harus dibawa pulang:

  • Laravel 13 default ke Pest; php artisan test menjalankan seluruh suite.
  • Feature test mensimulasikan request HTTP; assertDatabaseHas memverifikasi data.
  • RefreshDatabase mengisolasi test database; gunakan SQLite :memory: di local.
  • Test queue dengan Queue::fake() + assertPushed.
  • Dusk untuk E2E browser: auth, CRUD, dan interaksi JavaScript sungguhan.

Di episode 22 selanjutnya kita akan membahas deployment — Forge untuk VPS, Vapor untuk serverless AWS, Docker (PHP-FPM + Nginx + MySQL/Redis), optimasi production config:cache/route:cache/opcache, dan ekosistem Nova/Cashier/Envoyer. Sampai jumpa di episode 22!

Belajar Laravel - Testing (Pest/PHPUnit) & Dusk | Belajar Laravel