Testing dengan Pest

Kamu sudah bisa membuat fitur CRUD, validasi, auth, dan middleware. Tapi bagaimana cara memastikan semuanya bekerja dengan benar — dan tetap bekerja setelah kamu mengubah kode?

Jawabannya: Automated Testing (tes otomatis). Dan framework testing modern yang paling populer di Laravel saat ini adalah Pest.

1. Kenapa Harus Testing?

Bayangkan kamu punya toko online dengan 50 halaman. Lalu kamu mengubah satu function. Apakah kamu mau klik semua 50 halaman satu per satu untuk cek apakah ada yang rusak?

Dengan automated testing:

  • ✅ Kamu menulis tes sekali
  • ✅ Dijalankan otomatis setiap kali ada perubahan
  • ✅ Dalam hitungan detik, kamu tahu apakah ada yang rusak
  • ✅ Berfungsi sebagai dokumentasi hidup tentang cara kerja aplikasimu

2. Install Pest

Di project Laravel baru (Laravel 11+), Pest sudah tersedia secara default. Jika belum:

# Install Pest
composer require pestphp/pest --dev
composer require pestphp/pest-plugin-laravel --dev

# Setup Pest
./vendor/bin/pest --init

Jalankan tes pertamamu:

# Jalankan semua tes
php artisan test

# Atau langsung pakai Pest
./vendor/bin/pest

Kalau test gagal karena tabel belum ada atau schema database testing belum sinkron, cek dulu Laravel Migration Gagal sebelum mengubah kode test.

3. Jenis Test: Feature vs Unit

JenisApa yang ditest?Contoh
Feature TestAlur lengkap (HTTP request → response)"Apakah halaman /produk mengembalikan status 200?"
Unit TestSatu fungsi/class kecil secara terisolasi"Apakah hitungDiskon(100000, 10) mengembalikan 90000?"

Untuk pemula, mulailah dengan Feature Test — ini yang paling berguna dan mudah dipahami.

4. Feature Test Pertamamu

Buat file test:

php artisan make:test ProdukTest --pest

File akan dibuat di tests/Feature/ProdukTest.php:

<?php

// tests/Feature/ProdukTest.php

use App\Models\Produk;
use App\Models\User;

// ✅ Test 1: Halaman daftar produk bisa diakses
test('halaman produk bisa diakses', function () {
    $response = $this->get('/produk');

    $response->assertStatus(200);
});

// ✅ Test 2: User yang sudah login bisa membuat produk
test('user bisa membuat produk baru', function () {
    $user = User::factory()->create();

    $response = $this->actingAs($user)->post('/produk', [
        'nama'  => 'Kaos Polos Hitam',
        'harga' => 89000,
    ]);

    $response->assertRedirect('/produk');

    // Pastikan produk ada di database
    $this->assertDatabaseHas('produks', [
        'nama'  => 'Kaos Polos Hitam',
        'harga' => 89000,
    ]);
});

// ✅ Test 3: Validasi menolak data yang tidak valid
test('produk tanpa nama ditolak', function () {
    $user = User::factory()->create();

    $response = $this->actingAs($user)->post('/produk', [
        'nama'  => '',     // Kosong!
        'harga' => 89000,
    ]);

    $response->assertSessionHasErrors('nama');
});

// ✅ Test 4: User yang belum login tidak bisa akses form
test('guest tidak bisa membuat produk', function () {
    $response = $this->get('/produk/create');

    $response->assertRedirect('/login');
});

Jalankan:

php artisan test --filter=ProdukTest

Output:

 PASS  Tests\Feature\ProdukTest
 ✓ halaman produk bisa diakses
 ✓ user bisa membuat produk baru
 ✓ produk tanpa nama ditolak
 ✓ guest tidak bisa membuat produk

 Tests:    4 passed
 Duration: 0.58s

5. Assertions yang Sering Dipakai

// HTTP Response
$response->assertStatus(200);           // Status code 200
$response->assertOk();                  // Shortcut untuk 200
$response->assertRedirect('/produk');   // Redirect ke URL tertentu
$response->assertForbidden();           // Status 403
$response->assertNotFound();            // Status 404

// Konten halaman
$response->assertSee('Kaos Polos');     // Teks muncul di halaman
$response->assertDontSee('Error');      // Teks TIDAK ada di halaman

// Database
$this->assertDatabaseHas('produks', ['nama' => 'Kaos']);    // Data ada di DB
$this->assertDatabaseMissing('produks', ['nama' => 'XXX']); // Data TIDAK ada
$this->assertDatabaseCount('produks', 5);                   // Jumlah row

// Session & Validasi
$response->assertSessionHasErrors('nama');       // Ada error untuk field 'nama'
$response->assertSessionHas('success');          // Ada flash message 'success'
$response->assertSessionHasNoErrors();           // Tidak ada error validasi

6. Testing CRUD Lengkap

Contoh test suite lengkap untuk fitur produk:

<?php

use App\Models\Produk;
use App\Models\User;

beforeEach(function () {
    $this->user = User::factory()->create();
});

test('index menampilkan daftar produk', function () {
    Produk::factory()->count(3)->create();

    $this->actingAs($this->user)
        ->get('/produk')
        ->assertOk()
        ->assertViewHas('produkList');
});

test('store menyimpan produk baru', function () {
    $this->actingAs($this->user)
        ->post('/produk', [
            'nama'  => 'Sepatu Running',
            'harga' => 450000,
        ])
        ->assertRedirect(route('produk.index'));

    $this->assertDatabaseHas('produks', ['nama' => 'Sepatu Running']);
});

test('update mengubah data produk', function () {
    $produk = Produk::factory()->create(['nama' => 'Nama Lama']);

    $this->actingAs($this->user)
        ->put("/produk/{$produk->id}", [
            'nama'  => 'Nama Baru',
            'harga' => 100000,
        ])
        ->assertRedirect(route('produk.index'));

    expect($produk->fresh()->nama)->toBe('Nama Baru');
});

test('destroy menghapus produk', function () {
    $produk = Produk::factory()->create();

    $this->actingAs($this->user)
        ->delete("/produk/{$produk->id}")
        ->assertRedirect(route('produk.index'));

    $this->assertDatabaseMissing('produks', ['id' => $produk->id]);
});
beforeEach()

beforeEach() menjalankan kode sebelum setiap test. Berguna untuk setup data yang dibutuhkan semua test, seperti membuat user.

7. Mengenal TDD (Test-Driven Development)

TDD adalah cara memprogram di mana kamu menulis tes dulu, baru kode-nya:

1. 🔴 Tulis test → Jalankan → GAGAL (karena fiturnya belum ada)
2. 🟢 Tulis kode minimal agar test LULUS
3. 🔵 Refactor kode agar lebih bersih
4. Ulangi!

Ini disebut siklus Red → Green → Refactor. Buku "Test-Driven APIs with Laravel and Pest" menjelaskan pendekatan ini secara mendalam.

8. Perbandingan: Manual Testing vs Automated Testing

AspekManual TestingAutomated Testing (Pest)
KecepatanKlik satu per satu (menit-jam)Semua test dalam detik
KonsistensiBisa lupa test sesuatuSelalu test semuanya
Setelah UpdateHarus test ulang manualphp artisan test
DokumentasiTidak adaTest = dokumentasi cara kerja fitur
Confidence"Semoga tidak rusak...""Semua test hijau ✅"

Selanjutnya

Terakhir, pelajari cara membuat dan mengonsumsi API di Laravel. Lanjut ke Laravel API → untuk membangun backend yang bisa diakses oleh mobile app, frontend SPA, atau layanan lain.