Dasar testing di Laravel
Laravel bisa menjalankan seluruh siklus HTTP di dalam proses tes, tanpa server dan tanpa browser. Itu membuat tes yang paling berguna — tes fitur — jadi yang paling murah ditulis.
Intisari
- Dua jenis: Feature (menjalankan permintaan HTTP lengkap) dan Unit (satu kelas, tanpa framework).
- Prioritaskan tes fitur. Satu tes fitur menutup rute, middleware, validasi, otorisasi, dan database sekaligus.
phpunit.xmlmemakai environmenttesting— pakai database terpisah, jangan yang asli.RefreshDatabasemembungkus tiap tes dalam transaksi lalu me-rollback-nya.- Tes bukan soal persentase cakupan; ia soal apakah kamu berani men-deploy pada Jumat sore.
Bentuk tes fitur
php artisan make:test ArtikelTest # Feature
php artisan make:test HitungPajakTest --unit # Unit
class ArtikelTest extends TestCase
{
use RefreshDatabase;
public function test_pengunjung_melihat_artikel_terbit(): void
{
$artikel = Artikel::factory()->terbit()->create(['judul' => 'Kabar Pagi']);
$this->get(route('artikel.show', $artikel))
->assertOk()
->assertSee('Kabar Pagi');
}
public function test_artikel_draf_tidak_terlihat_publik(): void
{
$artikel = Artikel::factory()->draf()->create();
$this->get(route('artikel.show', $artikel))->assertNotFound();
}
public function test_hanya_editor_yang_bisa_menerbitkan(): void
{
$artikel = Artikel::factory()->draf()->create();
$this->actingAs(User::factory()->kontributor()->create())
->post(route('artikel.terbit', $artikel))
->assertForbidden();
$this->actingAs(User::factory()->editor()->create())
->post(route('artikel.terbit', $artikel))
->assertRedirect();
$this->assertTrue($artikel->fresh()->terbit_pada->isPast());
}
}
Tiga tes di atas menutup jauh lebih banyak daripada yang terlihat. Rutenya benar, middleware-nya jalan, policy-nya menolak orang yang salah, route model binding-nya bekerja, dan datanya benar-benar berubah di database. Untuk menutup hal yang sama dengan tes unit, kamu butuh belasan berkas dan tumpukan tiruan — dan tetap tidak tahu apakah rutenya terdaftar.
Piramida yang realistis untuk aplikasi Laravel
| Jenis | Porsi yang sehat | Untuk |
|---|---|---|
| Feature | ~70% | Setiap rute penting, setiap aturan otorisasi |
| Unit | ~25% | Perhitungan, objek nilai, aturan yang banyak cabangnya |
| Browser (Dusk) | ~5% | Alur yang benar-benar bergantung JavaScript |
Perbandingan ini kebalikan dari piramida tes klasik, dan itu disengaja: di aplikasi web, sebagian besar bug
bukan pada perhitungan di dalam kelas, melainkan pada sambungan antar bagian — middleware yang lupa dipasang,
policy yang tidak dipanggil, kolom yang tidak ada di $fillable.
Konfigurasi lingkungan tes
<!-- phpunit.xml -->
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="CACHE_STORE" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="MAIL_MAILER" value="array"/>
</php>
SQLite in-memory itu cepat, tapi bukan databasemu. Ia berbeda dari MySQL dalam hal fungsi JSON,
ketatnya tipe, sensitivitas huruf besar-kecil, dan perilaku ALTER TABLE. Kompromi yang lazim:
SQLite untuk siklus cepat di laptop, MySQL atau PostgreSQL sungguhan di pipeline CI. Yang tidak boleh:
hanya pernah menjalankan tes di SQLite lalu men-deploy ke MySQL.
Assertion yang paling sering dipakai
// Respons
$response->assertOk(); // 200
$response->assertCreated(); // 201
$response->assertNoContent(); // 204
$response->assertRedirect(route('x'));
$response->assertForbidden(); // 403
$response->assertNotFound(); // 404
$response->assertUnprocessable(); // 422
$response->assertSee('teks');
$response->assertSessionHasErrors(['judul']);
// Database
$this->assertDatabaseHas('artikel', ['slug' => 'kabar-pagi']);
$this->assertDatabaseMissing('artikel', ['id' => 9]);
$this->assertDatabaseCount('artikel', 3);
$this->assertSoftDeleted($artikel);
$this->assertModelExists($artikel);
// Autentikasi
$this->assertAuthenticated();
$this->assertGuest();
Kebiasaan yang membuat suite tetap cepat
- Jalankan paralel:
php artisan test --parallel. ->make()kalau tidak perlu tersimpan;->create()hanya kalau perlu.- Jangan membuat 100 baris kalau 3 sudah membuktikan hal yang sama.
- Palsukan yang lambat:
Http::fake(),Queue::fake(),Mail::fake(). - Turunkan biaya hashing di lingkungan tes —
bcryptyang mahal terasa saat ada ratusan pengguna palsu.
php artisan test
php artisan test --parallel
php artisan test --filter=ArtikelTest
php artisan test --coverage --min=70
Tentang cakupan (coverage). Angka 100% tidak membuktikan apa pun tentang kebenaran; ia hanya berarti setiap baris pernah dijalankan. Yang lebih berguna: pastikan setiap aturan otorisasi dan setiap jalur uang punya tesnya sendiri. Itu daftar yang pendek, dan justru di situlah kegagalan paling mahal.
Latihan: tulis tiga tes seperti contoh di atas untuk model Artikel-mu — satu untuk
pengunjung, satu untuk yang tidak berhak, satu untuk yang berhak. Jalankan
php artisan test --parallel dan catat waktunya.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.