← Semua pembelajaran / Laravel Nol → Enterprise
Fase 6 · Testing & Kualitas Kode

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.

Sumber asli laravel.com Resmi Rangkuman ~6 menit baca

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.xml memakai environment testing — pakai database terpisah, jangan yang asli.
  • RefreshDatabase membungkus 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

JenisPorsi yang sehatUntuk
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

  1. Jalankan paralel: php artisan test --parallel.
  2. ->make() kalau tidak perlu tersimpan; ->create() hanya kalau perlu.
  3. Jangan membuat 100 baris kalau 3 sudah membuktikan hal yang sama.
  4. Palsukan yang lambat: Http::fake(), Queue::fake(), Mail::fake().
  5. Turunkan biaya hashing di lingkungan tes — bcrypt yang 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.