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

Tes HTTP & API

Tes HTTP adalah kontrak yang bisa dijalankan. Ia membuktikan bahwa endpoint-mu memberi status, bentuk, dan izin yang benar — dan memberitahumu saat salah satunya berubah tanpa sengaja.

Sumber asli laravel.com Resmi Rangkuman ~6 menit baca

Intisari

  • getJson, postJson, dan kawan-kawan memasang header Accept yang benar.
  • assertJsonStructure memeriksa bentuk; assertJsonPath memeriksa nilai.
  • actingAs($user, 'sanctum') untuk menguji rute API tanpa membuat token sungguhan.
  • Storage::fake() dan UploadedFile::fake() untuk unggahan.
  • withoutExceptionHandling() menampilkan exception asli alih-alih halaman 500 yang tidak informatif.

Menguji endpoint JSON

it('mengembalikan daftar artikel terbit', function () {
    Artikel::factory()->terbit()->count(3)->create();
    Artikel::factory()->draf()->count(2)->create();

    $this->getJson('/api/v1/artikel')
        ->assertOk()
        ->assertJsonCount(3, 'data')
        ->assertJsonStructure([
            'data' => [['id', 'judul', 'slug', 'terbit_pada']],
            'links', 'meta',
        ]);
});

it('memvalidasi artikel baru', function () {
    $this->actingAs(editor(), 'sanctum')
        ->postJson('/api/v1/artikel', ['judul' => ''])
        ->assertUnprocessable()
        ->assertJsonValidationErrors(['judul', 'isi']);
});

it('membuat artikel', function () {
    $this->actingAs(editor(), 'sanctum')
        ->postJson('/api/v1/artikel', [
            'judul' => 'Kabar Pagi',
            'isi'   => 'Isi lengkap.',
        ])
        ->assertCreated()
        ->assertJsonPath('data.judul', 'Kabar Pagi');

    $this->assertDatabaseHas('artikel', ['judul' => 'Kabar Pagi']);
});

assertJsonStructure adalah pelindung kontrak API-mu. Ia gagal saat sebuah field hilang atau berubah nama — persis kejadian yang akan memutus aplikasi klien. Pasangkan dengan assertJsonMissing untuk field yang tidak boleh bocor, seperti catatan_internal.

Assertion JSON yang perlu dibedakan

AssertionMemeriksa
assertJson([...])Berisi sebagian struktur ini
assertExactJson([...])Persis sama, tidak lebih dan tidak kurang
assertJsonPath('data.0.judul', 'X')Satu nilai di jalur tertentu
assertJsonStructure([...])Kunci-kuncinya ada; nilainya tidak diperiksa
assertJsonCount(3, 'data')Jumlah elemen
assertJsonMissing(['rahasia'])Tidak mengandung ini
assertJsonValidationErrors([...])422 dengan field yang disebutkan

Autentikasi di dalam tes

$this->actingAs($user);                    // guard default (web)
$this->actingAs($user, 'sanctum');         // rute API
$this->actingAs($admin, 'admin');

// Token dengan ability terbatas — untuk menguji pembatasannya
Sanctum::actingAs($user, ['artikel:baca']);

$this->postJson('/api/v1/artikel', [...])->assertForbidden();

Unggahan berkas

it('menyimpan gambar sampul', function () {
    Storage::fake('s3');

    $berkas = UploadedFile::fake()->image('sampul.jpg', 1200, 630);

    $this->actingAs(editor())
        ->post(route('artikel.store'), ['judul' => 'X', 'sampul' => $berkas])
        ->assertRedirect();

    Storage::disk('s3')->assertExists('sampul/'.$berkas->hashName());
});

it('menolak berkas yang terlalu besar', function () {
    $besar = UploadedFile::fake()->create('dokumen.pdf', 20_000);   // 20 MB

    $this->actingAs(editor())
        ->post(route('artikel.store'), ['sampul' => $besar])
        ->assertSessionHasErrors('sampul');
});

Melihat error yang sebenarnya

it('sesuatu yang sedang rusak', function () {
    $this->withoutExceptionHandling();      // lempar exception asli, bukan halaman 500

    $this->get('/artikel/1')->assertOk();
});
$response->dump();          // cetak isi respons
$response->dumpHeaders();
$response->dumpSession();

Tanpa withoutExceptionHandling(), tes yang gagal cuma bilang "mengharapkan 200, mendapat 500". Dengannya, kamu melihat exception dan baris persisnya. Nyalakan saat sedang men-debug, matikan lagi setelah selesai — kamu tetap ingin menguji bahwa error ditangani dengan benar di tes yang lain.

Menguji halaman Inertia dan Livewire

// Inertia
$this->get('/artikel')->assertInertia(fn (Assert $page) => $page
    ->component('Artikel/Index')
    ->has('artikel.data', 3)
    ->where('filter.q', null));

// Livewire
Livewire::test(CariArtikel::class)
    ->set('kata', 'kabar')
    ->assertSee('Kabar Pagi')
    ->assertDontSee('Berita Sore');

Yang wajib punya tes

  1. Setiap aturan otorisasi — dari sisi yang boleh dan yang tidak boleh.
  2. Setiap aturan validasi yang melindungi integritas data.
  3. Bentuk respons setiap endpoint API publik.
  4. Setiap alur yang menyentuh uang atau data pribadi.
  5. Setiap bug yang pernah terjadi — tulis tesnya sebelum memperbaikinya.

Latihan: tulis tes yang memastikan endpoint daftar artikel tidak mengembalikan field catatan_internal untuk pengguna biasa, tapi mengembalikannya untuk editor. Ini satu tes yang mencegah satu kelas kebocoran data.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.