Laravel HTTP Feature Unit Tests Pest Database Refresh
Selesai membaca dokumentasi ini?
Kembali ke rujukan utama Laravel untuk melanjutkan topik lainnya.
Kembali ke rujukan utama Laravel untuk melanjutkan topik lainnya.
Di Laravel, HTTP testing adalah cara menguji request/response lewat layer aplikasi yang nyata, misalnya route, controller, middleware, validasi, redirect, session, dan JSON response. Ini bukan sekadar memanggil method, tetapi menjalankan alur request seperti client sungguhan, dengan test helper Laravel yang meniru request HTTP.
Feature test dipakai untuk menguji alur pengguna atau alur request yang melibatkan beberapa lapisan sekaligus. Unit test dipakai untuk menguji logika kecil yang bisa berdiri sendiri, tanpa harus menjalankan banyak komponen aplikasi.
Pest adalah test runner dan test DSL yang membuat test Laravel lebih ringkas dan enak dibaca. Di atas Laravel test case, Pest memberi syntax yang lebih pendek, assertion yang tetap lengkap, dan gaya penulisan yang cocok untuk test file yang padat.
Database refresh biasanya merujuk ke cara menyiapkan database test agar selalu bersih dan konsisten antar test run. Di Laravel, ini paling sering dilakukan dengan trait seperti RefreshDatabase, atau dengan strategi reset database lain yang setara.
Kalau kamu bingung harus menaruh test di mana, pakai aturan praktis ini:
Feature test cocok untuk hal seperti:
Feature test paling berguna saat kamu ingin menjawab pertanyaan: “Kalau user melakukan X, aplikasi benar-benar melakukan Y?”
Unit test cocok untuk:
Unit test paling berguna saat kamu ingin menjawab pertanyaan: “Fungsi ini, kalau diberi input tertentu, apakah hasilnya benar?”
Kesalahan umum adalah menaruh semua hal di unit test hanya karena terlihat lebih cepat. Akibatnya, test jadi banyak mock dan tidak lagi mewakili cara aplikasi benar-benar bekerja.
Sebaliknya, jangan semua test dipaksa jadi feature test. Kalau logika cuma hitungan sederhana, feature test malah bikin test lebih lambat dan lebih rapuh tanpa nilai tambahan yang sepadan.
Laravel request testing memakai helper seperti get(), post(), put(), patch(), delete(), dan json() untuk mensimulasikan request ke aplikasi.
Yang terjadi bukan request sungguhan dari browser, tetapi Laravel membangun request internal, melewatinya melalui routing, middleware, controller, validation, dan response layer. Karena itu, kamu bisa menguji hampir seluruh alur tanpa membuka browser.
Urutan pentingnya begini:
it('can store a post', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)->post('/posts', [
'title' =>
Contoh di atas menunjukkan bahwa HTTP testing biasanya memeriksa lebih dari satu hal:
Untuk API, biasanya pakai getJson(), postJson(), dan kawan-kawannya.
it('returns validation error for invalid payload', function () {
$response = $this->postJson('/api/posts', [
'title' => '',
]);
$response->assertUnprocessable()
->assertJsonValidationErrors(['title']);
Di sini yang diuji adalah kontrak API, bukan sekadar “method berhasil dipanggil”.
Pola dasar testing tetap sama: arrange → act → assert.
Siapkan semua hal yang dibutuhkan test:
Jalankan aksi yang sedang diuji:
Periksa hasilnya:
Pola ini penting supaya test mudah dibaca. Kalau arrange, act, dan assert bercampur, test cenderung sulit dirawat.
Pest sering dipakai karena sintaksnya lebih ringkas daripada PHPUnit murni, tetapi tetap berjalan di atas ekosistem Laravel yang sama.
it('lists posts', function () {
Post::factory()->count(3)->create();
$response = $this->get('/posts');
$response->assertOk();
});it('rejects invalid titles', function (string $title) {
$this->post('/posts', [
'title' => $title,
'body' => 'Isi',
])->assertSessionHasErrors('title');
})->with
Pest terasa lebih nyaman ketika:
PHPUnit masih sangat valid kalau:
Intinya, Pest bukan mengganti konsep testing. Pest hanya mengubah cara menulisnya.
Test yang menyentuh database harus dimulai dari state yang dapat diprediksi. Di Laravel, cara paling umum adalah memakai RefreshDatabase.
RefreshDatabaseTrait ini memastikan database test disiapkan ulang agar setiap test berjalan dengan kondisi yang bersih.
Biasanya hasilnya:
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
it('creates a post', function () {
$user = User::factory()->create();
$this->actingAs($user)
Tanpa reset database yang konsisten, test bisa lolos di laptopmu tapi gagal di CI, atau sebaliknya. Ini terjadi karena test bergantung pada data sisa, urutan eksekusi, atau state yang tidak sengaja terbentuk sebelumnya.
RefreshDatabase → pilihan default yang paling aman untuk banyak proyekDatabaseTransactions → bisa cepat, tapi tidak cocok untuk semua kasus, terutama jika ada proses yang butuh commit nyataPola ini biasanya paling efektif untuk test production app:
Database test memang wajar, tetapi kalau semua logika kecil ikut masuk feature test, suite akan terasa lambat.
Solusi praktis:
Factory yang saling membuat banyak relasi bisa membuat test lambat.
Contoh masalah:
Solusi:
Test yang hanya cek assertOk() sering terlalu lemah. Test seperti itu bisa tetap hijau walaupun isi response salah.
Lebih baik cek hal yang benar-benar penting:
Kalau test A harus jalan dulu baru test B lulus, berarti ada state bocor.
Ini tanda buruk. Test seharusnya bisa dijalankan sendiri-sendiri dan tetap konsisten.
Mock itu berguna, tetapi terlalu banyak mock membuat test tidak lagi memverifikasi perilaku nyata.
Tanda-tandanya:
Kalau tujuan test adalah request Laravel, jangan tambahkan beban yang tidak relevan:
Gunakan fake, stub, atau assert terpisah bila memang diperlukan.
Nama test yang kabur membuat suite sulit dipelihara.
Lebih baik:
it('stores a post for authenticated users')it('rejects empty title')it('returns post list as JSON')Daripada nama generik yang tidak menjelaskan hasil.
RefreshDatabase untuk menjaga state test tetap bersih.it('creates a post through the web form', function () {
// arrange
$user = User::factory()->create();
// act
$response = $this->actingAs($user)->post('/posts'
Struktur seperti ini paling mudah dipindai, paling mudah direview, dan paling kecil kemungkinan jadi test yang rapuh.