Laravel Migrations Seeders Factories Testing Dasar
Definisi
Di Laravel, migrations dipakai untuk mengelola struktur database secara terversioning. Seeders dipakai untuk mengisi data awal atau data referensi. Factories dipakai untuk menghasilkan data palsu yang realistis dan konsisten untuk development atau testing. Testing dasar dipakai untuk memverifikasi bahwa schema, data bootstrap, dan perilaku aplikasi tetap berjalan setelah perubahan.
Kalau empat alat ini dipakai bareng, hasilnya adalah database yang lebih mudah direproduksi, lebih aman diubah, dan lebih kecil kemungkinan rusak diam-diam saat proyek tumbuh.
Kapan tiap alat dipakai
Migrations
Pakai migrations ketika kamu mengubah struktur database:
- menambah tabel baru
- menambah atau menghapus kolom
- mengganti tipe data
- menambah index atau foreign key
- mengubah constraint
Migrations adalah catatan perubahan schema, bukan tempat menyimpan data aplikasi. Fokusnya adalah bentuk database, bukan isi tabel.
Seeders
Pakai seeders ketika kamu perlu data awal yang selalu tersedia:
- user admin awal
- role dan permission dasar
- kategori atau status referensi
- data demo untuk local environment
- baseline data untuk onboarding developer baru
Seeders cocok untuk data yang memang ingin ada sejak awal dan bisa diulang dengan hasil yang dapat diprediksi.
Factories
Pakai factories ketika kamu perlu data sintetis dalam jumlah banyak:
- isi tabel untuk local development
- data test untuk feature test
- relasi model yang kompleks
- skenario edge case yang butuh banyak variasi
Factories sangat berguna saat kamu tidak mau menulis data satu per satu. Mereka menghasilkan data cepat, konsisten, dan cukup realistis untuk kebutuhan aplikasi.
Testing dasar
Pakai testing ketika kamu ingin memastikan perubahan tidak merusak perilaku yang sudah ada:
- migration masih bisa dijalankan
- seeding tidak gagal
- factory menghasilkan data valid
- endpoint CRUD tetap bekerja
- relasi model dan constraint tetap benar
Untuk Laravel, testing paling sering dipakai di level feature dan unit. Untuk topik ini, fokus utamanya adalah memastikan bootstrap database dan alur data dasar tetap sehat.
Cara kerja alur schema dan data bootstrap
Alur yang sehat biasanya begini:
- Schema dibentuk lewat migration
- Data referensi diisi lewat seeder
- Data dinamis dibangun lewat factory
- Test memakai database yang bersih dan terkontrol
- Jika perlu, seeding dijalankan ulang untuk mengisi baseline
Urutan ini penting karena setiap alat punya tugas yang berbeda. Kalau urutannya dicampur, hasilnya biasanya sulit direproduksi: schema berubah, data referensi hilang, atau test menjadi bergantung pada keadaan database sebelumnya.
Migrations
Fungsi utama
Migration adalah cara Laravel mencatat perubahan schema database sebagai file PHP yang bisa dijalankan berulang di environment mana pun.
Prinsip utamanya:
- perubahan schema harus bisa dilacak
- perubahan schema harus bisa direplikasi
- perubahan schema harus bisa dipahami dari histori file
Struktur dasar
Contoh migration:
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->text('body');
$table->timestamps();
});Hal yang penting dari migration bukan cuma syntax-nya, tetapi niatnya:
Schema::create()untuk membuat tabel baruSchema::table()untuk mengubah tabel yang sudah ada- foreign key dan index untuk menjaga integritas dan performa
Kapan migration dipilih
Gunakan migration saat perubahan database harus ikut versi code. Contoh:
- menambah kolom
published_at - menambahkan unique index untuk
email - memecah status string menjadi enum atau lookup table
- menambah relasi antar tabel
Catatan implementasi
- jangan simpan data bisnis di migration
- jangan memasukkan logic runtime ke migration
- hindari migration yang terlalu bergantung pada data produksi yang berubah-ubah
Migration yang baik menjelaskan schema, bukan menebak keadaan runtime.
Seeders
Fungsi utama
Seeder dipakai untuk mengisi database dengan data awal atau data referensi.
Contoh umum:
- role admin dan user
- kategori default
- status order
- permissions
- akun demo
Struktur dasar
Contoh seeder:
class DatabaseSeeder extends Seeder
{
public function run(): void
{
User::factory()->create([
'name' => 'Admin',
'email' => 'admin@example.com',
]);
}
}Seeder sering menjadi pintu masuk orkestrasi seeding. Di dalamnya, kamu bisa memanggil seeder lain agar struktur data lebih modular.
Kapan seeder dipilih
Seeder cocok saat data itu:
- harus selalu ada di environment tertentu
- bukan data transaksi pengguna
- perlu konsisten antar developer
- dipakai untuk demo, onboarding, atau testing
Catatan implementasi
- bedakan data referensi dan data dummy
- buat seeder idempotent kalau memungkinkan
- jangan tergantung pada urutan yang rapuh tanpa alasan kuat
Kalau seeder tidak idempotent, developer bisa sulit menjalankan ulang setup tanpa membersihkan database dulu.
Factories
Fungsi utama
Factory menghasilkan instance model dengan data palsu yang valid. Di Laravel, factory biasanya memakai Faker untuk membuat data yang terlihat realistis.
Contoh:
User::factory()->count(10)->create();Atau dengan relasi:
Post::factory()
->for(User::factory())
->count(5)
->create();Kapan factory dipilih
Factory cocok saat kamu perlu:
- banyak data untuk mencoba tampilan UI
- data untuk test yang tidak bergantung pada database manual
- variasi data acak yang tetap valid
- relasi model yang dibangun cepat
Kenapa factory penting
Factory membantu menjaga test dan development tetap ringan. Tanpa factory, developer sering tergoda membuat data manual di database lokal, lalu test menjadi rapuh karena bergantung pada data yang tidak terkontrol.
Catatan implementasi
- pastikan atribut wajib selalu terisi
- jaga konsistensi relasi antar model
- gunakan state untuk variasi yang jelas, misalnya
published,draft, atauinactive - jangan terlalu acak sampai skenario sulit direproduksi
Data palsu yang “terlalu liar” bisa menyulitkan debugging. Yang dibutuhkan bukan sekadar random, tetapi random yang masih masuk akal.
Testing dasar
Tujuan testing pada konteks ini
Testing di sini bukan cuma mengecek satu fungsi. Tujuannya adalah memastikan alur database dan data yang dipakai aplikasi masih benar setelah perubahan.
Yang biasanya dites:
- migration tidak error saat dijalankan
- seeder menghasilkan data yang dibutuhkan
- factory membuat model valid
- relasi model tetap berjalan
- endpoint CRUD menghasilkan efek yang benar di database
Jenis test yang paling relevan
Feature test
Pakai feature test untuk memverifikasi alur dari request sampai data tersimpan.
Contoh:
- user membuat post
- validasi menolak input yang salah
- data tersimpan ke tabel yang benar
- response sesuai ekspektasi
Unit test
Pakai unit test untuk logika kecil yang bisa diuji tanpa banyak dependensi, misalnya helper, service kecil, atau transformasi data.
Pola database test yang sehat
Pada Laravel, test biasanya dijalankan pada database terisolasi, lalu schema disiapkan ulang sebelum test berjalan. Dengan begitu, hasil test tidak bergantung pada data lokal yang kebetulan ada.
Contoh sederhana
public function test_user_can_create_post(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->post('/posts', [
'title' => 'Judul Post',
'body' => 'Isi post',
]);
$response->assertRedirect();
$this->assertDatabaseHas('posts', [
'title' => 'Judul Post',
'user_id' => $user->id,
]);
}Test seperti ini memverifikasi bukan hanya response, tetapi juga efek data di database.
Alur kerja yang umum dipakai di proyek nyata
Urutan yang sering dipakai saat bootstrap project baru:
- buat migration untuk schema dasar
- buat model dan relasi Eloquent
- buat factory untuk model yang sering dipakai
- buat seeder untuk data referensi
- jalankan migrasi dan seeding di local
- tulis feature test untuk flow utama
- ulangi saat schema atau data contract berubah
Kalau alur ini dijaga, environment baru bisa naik dengan cepat dan test suite lebih mudah dijalankan ulang.
Jebakan umum pada data development
1. Mencampur data referensi dan data transaksi
Data referensi seperti role, status, atau kategori sering dianggap sama dengan data transaksi. Padahal beda.
- data referensi: sebaiknya stabil dan bisa di-seed ulang
- data transaksi: biasanya hasil aktivitas user atau skenario test
Kalau dua jenis data ini dicampur, reset environment jadi sulit.
2. Seeder tidak idempotent
Seeder yang terus membuat duplikat akan menyulitkan developer saat menjalankan ulang setup. Kalau perlu, gunakan pendekatan yang aman seperti updateOrCreate() atau cek keberadaan data dulu.
3. Factory tidak menjaga relasi
Factory yang membuat data tanpa relasi yang benar akan memunculkan record “yatim” atau foreign key error. Pastikan relasi model ikut dibentuk, bukan hanya atribut tunggal.
4. Test bergantung pada data manual
Kalau test bergantung pada data yang dibuat tangan di database lokal, test itu tidak portable. Begitu environment berubah, test bisa gagal tanpa alasan yang jelas.
5. Migration dipakai untuk mengisi data besar
Migration seharusnya fokus pada schema. Kalau isinya sudah mulai mirip ETL, bulk data import, atau backfill yang berat, biasanya lebih tepat dipisah ke command, job, atau script khusus.
6. Factory terlalu random
Random itu berguna, tetapi jangan sampai hasilnya sulit dipakai. Contoh masalah:
- status tidak valid secara bisnis
- tanggal dibuat tidak masuk akal
- string terlalu panjang untuk kolom tertentu
- relasi tidak cocok dengan constraint
7. Test tidak membersihkan state
Kalau test tidak memakai database terisolasi, hasilnya bisa dipengaruhi test sebelumnya. Ini salah satu sumber bug paling menyebalkan karena test terlihat “kadang gagal”.
8. Foreign key dan constraint baru ketahuan terlambat
Constraint yang tidak diuji sejak awal sering baru muncul saat data nyata masuk. Karena itu, factory dan test sebaiknya ikut menguji constraint penting, bukan hanya happy path.
Rekomendasi praktis
- pakai migration untuk schema, bukan data bisnis
- pakai seeder untuk data awal yang harus tersedia
- pakai factory untuk data sintetis dan test setup
- pakai feature test untuk memastikan alur data benar
- reset database test supaya hasil konsisten
- jaga factory dan seeder tetap dekat dengan model yang mereka wakili
Referensi terkait
Eloquent Relationship, Eager Loading, dan Query PatternMiddleware, Request, Response, dan Validation Flow di LaravelQueues, Jobs, Events, Listeners, dan Notifications di Laravel
Kalau kamu sudah paham empat alat ini, biasanya langkah berikutnya adalah merapikan cara aplikasi memproses data: request masuk, divalidasi, disimpan, diuji, lalu diulang tanpa bikin database lokal jadi liar.