Factory & seeder
Tes yang bergantung pada data manual akan rapuh dan lambat ditulis. Factory membuat data yang valid secara default, sehingga setiap tes hanya perlu menyebutkan bagian yang benar-benar penting baginya.
Intisari
- Factory menyediakan nilai default yang valid; tes hanya menimpa field yang relevan bagi dirinya.
- State memberi nama pada variasi:
Produk::factory()->habis()->create(). - Relasi dibuat sekaligus:
->has(Ulasan::factory()->count(3))atau->for($kategori). make()hanya membuat objek;create()menyimpannya ke database.- Seeder untuk data referensi (kategori, peran); factory untuk data uji. Jangan tertukar.
Bentuk sebuah factory
php artisan make:factory ProdukFactory --model=Produk
class ProdukFactory extends Factory
{
protected $model = Produk::class;
public function definition(): array
{
$nama = fake()->words(3, true);
return [
'kategori_id' => Kategori::factory(), // dibuat otomatis kalau belum ada
'nama' => $nama,
'slug' => Str::slug($nama).'-'.fake()->unique()->numberBetween(1, 99999),
'harga' => fake()->numberBetween(5_000, 500_000),
'stok' => fake()->numberBetween(0, 100),
'status' => StatusProduk::Aktif,
];
}
// State: variasi yang punya nama
public function habis(): static
{
return $this->state(fn () => ['stok' => 0]);
}
public function mahal(): static
{
return $this->state(fn () => ['harga' => fake()->numberBetween(1_000_000, 5_000_000)]);
}
}
Memakainya
Produk::factory()->create(); // satu, tersimpan
Produk::factory()->count(50)->create(); // lima puluh
Produk::factory()->make(); // tidak menyentuh database
Produk::factory()->habis()->mahal()->create(); // state bisa digabung
Produk::factory()->create(['nama' => 'Kopi Susu']); // timpa field tertentu
// Dengan relasi
Produk::factory()
->for(Kategori::factory()->create(['nama' => 'Minuman']))
->has(Ulasan::factory()->count(3))
->create();
// Urutan yang berputar โ berguna untuk memastikan variasi
Produk::factory()
->count(9)
->sequence(
['status' => StatusProduk::Aktif],
['status' => StatusProduk::Arsip],
['status' => StatusProduk::Draf],
)
->create();
Ini yang membuat tes terbaca. Bandingkan dua tes: satu yang mengisi dua belas kolom hanya untuk
memeriksa perilaku saat stok nol, dan satu yang menulis
Produk::factory()->habis()->create(). Yang kedua menyatakan maksudnya; yang pertama
menyembunyikannya di antara nilai-nilai yang tidak relevan.
Nilai unik dan jebakannya
fake()->unique()->safeEmail();
fake()->unique()->numberBetween(1, 100); // akan kehabisan setelah 100 pemanggilan
unique() mengingat nilai yang sudah dipakai dalam satu proses. Kalau ruang nilainya
sempit, factory ke-101 akan melempar OverflowException. Untuk slug dan kode, gabungkan dengan sesuatu
yang benar-benar unik seperti angka acak besar atau Str::ulid().
Seeder: data yang memang harus ada
class DatabaseSeeder extends Seeder
{
public function run(): void
{
// Data referensi: nyata, tetap, dan dibutuhkan aplikasi untuk berjalan
foreach (['Minuman', 'Makanan', 'Peralatan'] as $nama) {
Kategori::firstOrCreate(['slug' => Str::slug($nama)], ['nama' => $nama]);
}
// Data uji: hanya di luar produksi
if (! app()->isProduction()) {
User::factory()->create([
'email' => '[email protected]',
'password' => 'password',
]);
Produk::factory()->count(100)->create();
}
}
}
php artisan db:seed
php artisan db:seed --class=KategoriSeeder
php artisan migrate:fresh --seed # aman di lokal, TIDAK PERNAH di produksi
| Seeder | Factory | |
|---|---|---|
| Isinya | Data referensi yang nyata | Data palsu yang bentuknya benar |
| Boleh jalan di produksi | Ya, bagian yang idempoten | Tidak pernah |
| Dipanggil dari | db:seed, deploy pertama | Tes dan seeder |
| Harus idempoten | Ya โ firstOrCreate | Tidak relevan |
Seeder produksi wajib idempoten. Ia akan dijalankan lagi โ saat deploy ulang, saat memulihkan
lingkungan, saat seseorang menjalankannya karena ragu. Kategori::create(...) akan menghasilkan
duplikat pada pemanggilan kedua; firstOrCreate(...) tidak.
Bahasa Indonesia di data palsu
// config/app.php
'faker_locale' => 'id_ID',
Dengan locale id_ID, nama, alamat, dan nomor telepon yang dihasilkan berbentuk Indonesia. Ini bukan
kosmetik: layar yang penuh nama Indonesia akan mengungkap masalah lebar kolom dan pemotongan teks yang tidak
terlihat kalau semua nama contoh berbunyi "John Doe".
Latihan: buat ProdukFactory dengan state habis(), lalu jalankan
php artisan tinker dan buat 20 produk yang tiga di antaranya habis. Verifikasi dengan
Produk::where('stok', 0)->count(). Data ini akan kamu pakai lagi di Fase 6.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.