Tes database
Tes yang menyentuh database adalah tes yang paling meyakinkan, asalkan tiap tes tidak mewarisi kekacauan tes sebelumnya. Laravel menyediakan tiga strategi dengan biaya yang berbeda.
Intisari
RefreshDatabasemembungkus tiap tes dalam transaksi lalu me-rollback โ cepat, dan yang paling sering dipakai.DatabaseMigrationsmenjalankan ulang seluruh migrasi tiap tes โ sangat lambat, jarang perlu.DatabaseTruncationmengosongkan tabel; berguna saat tes tidak bisa hidup dalam transaksi.- Tes tidak boleh bergantung pada urutan atau pada data yang ditinggalkan tes lain.
--parallelmembuat database terpisah per proses; pastikan tidak ada state global yang dibagi.
Tiga strategi
| Trait | Cara kerja | Kecepatan | Pakai saat |
|---|---|---|---|
RefreshDatabase | Migrasi sekali, tiap tes dalam transaksi yang di-rollback | Cepat | Default โ hampir selalu ini |
DatabaseTruncation | Kosongkan tabel setelah tiap tes | Sedang | Kode yang memakai transaksinya sendiri, atau tes browser |
DatabaseMigrations | migrate:fresh sebelum tiap tes | Lambat | Saat menguji migrasinya itu sendiri |
// tests/Pest.php โ sekali untuk seluruh folder
pest()->extend(Tests\TestCase::class)
->use(RefreshDatabase::class)
->in('Feature');
Kenapa RefreshDatabase begitu cepat. Migrasi hanya berjalan sekali untuk seluruh suite.
Setiap tes dibuka dengan BEGIN dan ditutup dengan ROLLBACK, jadi database kembali
bersih tanpa satu pun tabel dibangun ulang. Konsekuensinya: kode yang menjalankan COMMIT sendiri
atau membuka koneksi terpisah tidak akan terlindungi โ di situlah DatabaseTruncation masuk.
Assertion database
$this->assertDatabaseHas('artikel', [
'slug' => 'kabar-pagi',
'status' => StatusArtikel::Terbit->value,
]);
$this->assertDatabaseMissing('artikel', ['slug' => 'draf-rahasia']);
$this->assertDatabaseCount('artikel', 3);
$this->assertSoftDeleted($artikel);
$this->assertNotSoftDeleted($artikel);
$this->assertModelMissing($artikel);
// Memastikan tidak ada query sama sekali โ misalnya karena cache bekerja
$this->expectsDatabaseQueryCount(2);
expectsDatabaseQueryCount() adalah cara menjadikan N+1 sebagai kegagalan tes.
Tulis jumlah query yang kamu harapkan pada endpoint daftar; saat seseorang menambahkan relasi baru di template
tanpa with(), angkanya melonjak dan tes berwarna merah. Ini pelengkap
Model::preventLazyLoading() dari Fase 2, dan bekerja bahkan untuk query yang bukan berasal dari
relasi.
Tes yang saling bergantung โ dan cara mencegahnya
// RAPUH โ bergantung pada isi seeder dan pada ID yang kebetulan 1
it('menampilkan artikel', function () {
get('/artikel/1')->assertOk();
});
// KOKOH โ membuat sendiri apa yang dibutuhkannya
it('menampilkan artikel', function () {
$artikel = Artikel::factory()->terbit()->create();
get(route('artikel.show', $artikel))->assertOk();
});
Aturannya: setiap tes membuat sendiri seluruh data yang dibutuhkannya. Tes yang mengandalkan seeder akan patah saat seeder berubah, dan tes yang mengandalkan ID akan patah saat dijalankan paralel.
Seeder di dalam tes, kalau memang perlu
uses(RefreshDatabase::class);
beforeEach(function () {
$this->seed(KategoriSeeder::class); // hanya data referensi
});
Hanya seed data referensi. Kategori, wilayah, dan peran memang harus ada supaya foreign key valid. Data uji tetap dibuat oleh factory di dalam tes yang membutuhkannya, supaya jelas terbaca data mana yang relevan untuk perilaku yang sedang diuji.
Menjalankan paralel
composer require --dev brianium/paratest
php artisan test --parallel
php artisan test --parallel --recreate-databases
Laravel membuat satu database per proses (db_test_1, db_test_2, โฆ). Yang bisa merusak
paralelisme: berkas yang ditulis ke path tetap, cache bersama yang tidak dinamai unik, dan tes yang mengubah
konfigurasi global. Kalau suite-mu hijau saat berurutan tapi merah saat paralel, salah satu dari tiga itu
penyebabnya.
Latihan: tambahkan expectsDatabaseQueryCount() pada tes daftar artikelmu dengan angka
yang benar. Lalu hapus with('penulis') dari controller-nya, jalankan tes, dan pastikan ia gagal
dengan angka yang jauh lebih besar.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.