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
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
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.