Arsitektur monolit modern yang memadukan Laravel dan Inertia.js meniadakan kebutuhan pembangunan REST API publik atau GraphQL untuk interaksi internal. Namun, eliminasi lapisan serialisasi formal ini memunculkan risiko struktural: schema drift. Perubahan nama atribut model atau modifikasi pada Resource transformer di sisi controller dapat secara instan merusak komponen React, Vue, atau Svelte di browser klien tanpa peringatan kompilasi di backend.
Menjalankan browser End-to-End (E2E) testing seperti Playwright atau Laravel Dusk untuk setiap kombinasi rute memakan waktu eksekusi yang tinggi dan rentan flaky. Solusi paling deterministik dan efisien pada level HTTP test adalah pengujian kontrak props memanfaatkan helper assertInertia.
Anatomi Masalah: Bagaimana Schema Drift Memicu Runtime Error
Inertia merender halaman pertama via HTML payload dan navigasi berikutnya via JSON response. Masalah muncul ketika frontend mengekspektasikan skema tertentu:
// Frontend (React/Vue) mengharapkan relasi author termuat secara datar
const { user } = usePage().props;
return <span>{user.author.profile_url}</span>;
Jika backend controller diubah tanpa sengaja—misalnya relasi tidak di-eager load atau key profile_url diubah menjadi avatar_url—frontend akan melempar TypeError: Cannot read properties of undefined saat runtime. Unit test controller standar yang hanya memeriksa assertStatus(200) tidak akan mendeteksi cacat struktur ini.
Verifikasi Struktur Skema Menggunakan assertInertia
Laravel menyediakan class Inertia\Testing\AssertableInertia yang memungkinkan inspeksi mendalam terhadap payload Inertia langsung pada response pengujian HTTP tanpa menjalankan JavaScript engine.
1. Validasi Komponen dan Keberadaan Key (has/where/missing)
Pola dasar pengujian kontrak memastikan nama komponen halaman tepat, props penting tersedia, dan field sensitif tidak bocor ke sisi klien.
use Inertia\Testing\AssertableInertia as Assert;
it('memverifikasi kontrak props halaman katalog produk', function () {
Product::factory()->count(3)->create();
$this->get(route('products.index'))
->assertOk()
->assertInertia(fn (Assert $page) => $page
->component('Products/Index')
->has('products.data', 3)
->has('products.data.0', fn (Assert $product) => $product
->hasAll(['id', 'name', 'slug', 'price_formatted'])
->missing('cost_price') // ponytail: cegah kebocoran margin keuntungan ke client
->where('name', fn ($name) => is_string($name) && strlen($name) > 0)
)
->has('filters', fn (Assert $filters) => $filters
->has('search')
->has('category')
)
);
});
Fungsi hasAll() memvalidasi eksistensi seluruh key yang didefinisikan. Fungsi missing() memvalidasi proteksi data agar atribut internal tidak terekspos. Closure pada where() memastikan tipe data primitif tetap konsisten.
Pengujian Partial Reloads dan Lazy / Deferred Props
Inertia menyediakan fitur partial reloads via header HTTP khusus untuk menghemat bandwidth. Fitur ini lazim dipadukan dengan Inertia::lazy() atau Inertia::defer() agar data berat hanya dievaluasi saat diminta eksplisit oleh klien.
Pengujian harus membuktikan dua skenario: data lazy tidak dimuat pada inisialisasi awal, dan data tersebut termuat ketika header partial dikirimkan.
it('mengisolasi evaluasi lazy props pada partial request', function () {
$endpoint = route('dashboard');
// 1. Initial page visit: pastikan metrik finansial tidak dievaluasi
$this->get($endpoint)
->assertOk()
->assertInertia(fn (Assert $page) => $page
->component('Dashboard')
->has('user')
->missing('financialMetrics')
);
// 2. Partial reload request: sertakan header spesifik Inertia
$this->get($endpoint, [
'X-Inertia' => 'true',
'X-Inertia-Partial-Component' => 'Dashboard',
'X-Inertia-Partial-Data' => 'financialMetrics',
])
->assertOk()
->assertInertia(fn (Assert $page) => $page
->has('financialMetrics', fn (Assert $metrics) => $metrics
->hasAll(['net_profit', 'burn_rate', 'updated_at'])
)
->missing('user') // Pastikan props lain dikecualikan saat partial reload
);
});
Integrasi ke Pipeline Continuous Integration (CI)
Pengujian kontrak berbasis assertInertia berjalan di dalam proses PHP standar memory-only (atau via SQLite in-memory). Ratusan pengujian skema dapat diselesaikan dalam hitungan detik di pipeline CI tanpa dependensi terhadap headless Chromium atau WebDriver.
Contoh implementasi langkah pada GitHub Actions:
name: Automated Test Suite
on: [push, pull_request]
jobs:
backend-contracts:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, pdo_sqlite
coverage: none
- name: Install Dependencies
run: composer install --prefer-dist --no-interaction --optimize-autoloader
- name: Execute Contract & Feature Tests
run: php artisan test --parallel --filter=Contract
Trade-offs dan Batasan Teknis
Meskipun assertInertia sangat efektif menangkap regresi backend, ada batasan arsitektur yang perlu diperhatikan:
- One-Way Validation: Backend memvalidasi bahwa payload yang dikirim sudah benar, tetapi backend tidak membaca TypeScript interface di frontend secara langsung. Jika developer frontend mengubah ekspektasi tipe tanpa menyinkronkan test backend, runtime error tetap bisa terjadi.
- Alternatif Pendekatan: Untuk type-safety end-to-end dua arah, pertimbangkan integrasi tool generator tipe (seperti Spatie Laravel Data dengan TypeScript Transformer). Gunakan pendekatan tersebut jika codebase dikerjakan oleh tim backend dan frontend yang terpisah sepenuhnya. Untuk tim monolit terpadu,
assertInertiamenawarkan rasio kecepatan implementasi berbanding proteksi bug tertinggi.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!