Test matrix di CodeIgniter 4 membantu tim menentukan tes apa yang harus dijalankan, di environment mana, dan kapan tes tersebut dieksekusi. Tujuannya bukan menjalankan semua tes di semua tempat, melainkan menemukan kombinasi yang cukup untuk menangkap regresi tanpa membuat pipeline terlalu lambat.
Untuk aplikasi yang berjalan di local, CI, staging, dan production-like, masalah umum biasanya muncul karena perbedaan konfigurasi, database state, service eksternal, cache, atau cara bootstrap aplikasi. Solusinya adalah memisahkan tes cepat vs tes kritikal, menyiapkan konfigurasi .env.testing yang konsisten, memakai seed data yang stabil, dan hanya memverifikasi dependency eksternal dengan level yang tepat: mock, fake, atau smoke test terbatas.
Mengapa test matrix dibutuhkan di CodeIgniter 4
Dalam proyek kecil, sering kali cukup menjalankan seluruh PHPUnit test secara lokal lalu deploy. Namun saat aplikasi mulai memiliki banyak service, koneksi database berbeda antar environment, dan integrasi eksternal, pendekatan itu biasanya gagal karena:
- Tes terlalu lambat, sehingga developer enggan menjalankannya.
- Tes tidak representatif, karena hanya lolos di mesin lokal tertentu.
- Regresi konfigurasi baru terlihat saat staging atau bahkan setelah deploy.
- Flaky test muncul karena data, waktu, network, atau state antar test tidak terisolasi.
Dengan test matrix, Anda bisa memetakan cakupan pengujian berdasarkan risiko. Prinsip dasarnya:
- Unit test untuk logika murni dan cepat.
- Integration test untuk interaksi antar komponen framework, service, dan request lifecycle.
- Database test untuk repository, model, query, migrasi, dan transaksi.
- Smoke test untuk memastikan aplikasi bisa boot, route penting hidup, dan dependency utama berfungsi di environment tertentu.
Strategi menyusun test matrix multi-environment
Test matrix yang baik tidak berarti setiap environment harus menjalankan semua jenis tes. Yang lebih penting adalah mencocokkan jenis tes dengan tujuan environment tersebut.
Pembagian per environment
- Local: feedback tercepat untuk developer. Jalankan unit test, integration test ringan, dan database test yang terisolasi.
- CI: validasi konsisten di mesin bersih. Jalankan unit test penuh, integration test penting, database test, dan static checks bila ada.
- Staging: validasi perilaku aplikasi yang mendekati nyata. Jalankan smoke test, skenario end-to-end kritikal, dan verifikasi konfigurasi.
- Production-like: fokus pada kompatibilitas infrastruktur, bukan eksplorasi semua skenario. Jalankan smoke test kritikal, health check, konektivitas, dan verifikasi deployment.
Contoh test matrix yang realistis
Berikut contoh matrix sederhana yang cukup umum dipakai:
Jenis Test Local CI Pull Request CI Main Branch Staging Production-like
Unit Ya Ya Ya Tidak wajib Tidak
Integration Ya Ya (subset) Ya Ya (kritikal) Tidak
Database Ya Ya Ya Ya (read/write dasar) Tidak langsung
Smoke Opsional Tidak Ya setelah build Ya Ya
External service Mock Mock/Fake Mock/Fake Sandbox/Stub terbatas Verifikasi minimalAlasan di balik matrix ini:
- Pull request harus cepat agar tidak menghambat review.
- Main branch boleh lebih ketat karena menjadi kandidat deploy.
- Staging menguji apakah aplikasi benar-benar bisa hidup dengan konfigurasi mendekati produksi.
- Production-like sebaiknya tidak dipenuhi test berat; fokusnya adalah validasi readiness.
Memisahkan test cepat vs test kritikal
Salah satu kesalahan paling umum adalah menaruh semua tes dalam satu jalur yang sama. Akibatnya, perubahan kecil pada helper atau validasi sederhana harus menunggu seluruh test suite selesai. Di CodeIgniter 4, pemisahan ini bisa dilakukan lewat struktur direktori, nama namespace, atau grouping di PHPUnit.
Klasifikasi yang disarankan
- Fast tests: unit test dan sebagian integration test tanpa I/O berat.
- Critical tests: login, otorisasi, pembayaran, sinkronisasi data, migrasi, dan skenario database penting.
- Environment checks: smoke test dan boot validation per environment.
Contoh struktur test di CodeIgniter 4
tests/
├── unit/
│ ├── Services/
│ ├── Libraries/
│ └── Validators/
├── integration/
│ ├── Controllers/
│ ├── Filters/
│ └── Routes/
├── database/
│ ├── Models/
│ ├── Repositories/
│ └── Migrations/
├── smoke/
│ ├── HealthCheckTest.php
│ ├── AuthRouteTest.php
│ └── CriticalPageTest.php
└── support/
├── Seeds/
├── Fakes/
└── Fixtures/Dengan struktur seperti ini, pipeline dapat menjalankan subset test dengan lebih jelas. Misalnya:
# cepat untuk developer
phpunit tests/unit tests/integration
# validasi lebih lengkap
phpunit tests/unit tests/integration tests/database
# smoke test terpisah untuk environment deploy
phpunit tests/smokeNama perintah dapat berbeda tergantung konfigurasi proyek Anda, tetapi prinsipnya sama: sediakan jalur eksekusi terpisah antara tes cepat dan tes kritikal.
Pengelolaan konfigurasi .env.testing yang konsisten
Regresi multi-environment sering bukan karena bug logika, melainkan karena konfigurasi test tidak stabil. Karena itu, file .env.testing perlu dirancang untuk menghasilkan perilaku yang deterministik.
Tujuan utama .env.testing
- Mengaktifkan mode testing dan menonaktifkan perilaku yang tidak perlu.
- Mengarahkan koneksi database ke database test yang aman.
- Mematikan cache eksternal atau menggantinya dengan driver yang sederhana.
- Menggunakan endpoint fake/sandbox untuk service eksternal.
- Menjaga secret dan kredensial test terpisah dari local maupun staging.
Contoh isi .env.testing
CI_ENVIRONMENT = testing
app.baseURL = 'http://localhost/'
database.default.hostname = 127.0.0.1
database.default.database = app_test
database.default.username = test_user
database.default.password = test_pass
database.default.DBDriver = MySQLi
cache.handler = dummy
session.driver = CodeIgniter\Session\Handlers\ArrayHandler
external.api.baseUrl = 'https://sandbox.example.test'
external.api.timeout = 2Beberapa catatan penting:
- Jangan arahkan database testing ke database development biasa. Kesalahan ini bisa merusak data lokal atau membuat hasil test tidak konsisten.
- Gunakan session dan cache yang mudah di-reset agar state antar test tidak bocor.
- Jangan bergantung pada URL atau secret manual di mesin developer jika bisa disuntikkan dari environment.
Masalah umum yang muncul
- Test lokal lolos, tetapi CI gagal karena timezone, locale, atau ekstensi PHP berbeda.
- Service container memuat dependency asli karena environment tidak benar-benar masuk mode testing.
- Konfigurasi queue, mailer, atau cache tetap menuju service nyata.
Solusinya adalah memperlakukan .env.testing sebagai bagian dari kode yang harus ditinjau, bukan sekadar file tambahan.
Seed data yang stabil dan mudah dirawat
Database test yang bagus bergantung pada data awal yang minimal, jelas, dan stabil. Banyak flaky test muncul karena seed terlalu besar, terlalu bergantung pada urutan insert, atau memakai data acak tanpa kontrol.
Prinsip seed data untuk test
- Buat data sekecil mungkin, hanya untuk skenario yang diuji.
- Gunakan identifier yang eksplisit agar assertion mudah dibaca.
- Hindari data acak tanpa seed tetap jika hasilnya memengaruhi assertion.
- Reset state database di awal test atau per test class.
Contoh seed yang stabil
<?php
namespace Tests\Support\Seeds;
use CodeIgniter\Database\Seeder;
class UserTestSeeder extends Seeder
{
public function run()
{
$this->db->table('users')->insertBatch([
[
'id' => 1001,
'email' => '[email protected]',
'name' => 'Admin Test',
'status' => 'active',
],
[
'id' => 1002,
'email' => '[email protected]',
'name' => 'User Test',
'status' => 'inactive',
],
]);
}
}Mengapa pendekatan ini efektif:
- ID eksplisit memudahkan relasi dengan tabel lain.
- Email dan status mudah dipahami saat debugging.
- Data tidak berubah antar run, sehingga hasil assertion lebih dapat diprediksi.
Kapan memakai transaction atau reset database
- Transaction per test cocok untuk test yang menulis data dan dapat di-rollback cepat.
- Refresh/reset schema cocok untuk test migrasi atau skenario yang mengubah struktur.
- Seed per suite cocok untuk data referensi yang sama di banyak test, asalkan test tidak mengubahnya.
Trade-off-nya jelas: reset total lebih aman tetapi lebih lambat, sedangkan transaction lebih cepat tetapi tidak selalu cocok jika test memeriksa perilaku commit, trigger, atau integrasi lintas koneksi.
Verifikasi dependency eksternal: kapan mock, fake, atau real sandbox
Aplikasi CodeIgniter 4 sering bergantung pada API pihak ketiga, email, object storage, payment gateway, atau sistem internal lain. Jika semua integration test memanggil service nyata, pipeline akan lambat dan rapuh. Karena itu, dependency eksternal harus dipilih level verifikasinya.
Panduan pemilihan
- Mock: untuk unit test, ketika Anda hanya ingin memastikan method dipanggil dengan kontrak tertentu.
- Fake: untuk integration test, ketika Anda ingin simulasi perilaku lebih realistis tanpa network sungguhan.
- Sandbox/stub environment: untuk staging atau nightly run, ketika kompatibilitas request/response perlu divalidasi.
Contoh kontrak service yang mudah diuji
<?php
namespace App\Contracts;
interface NotificationClientInterface
{
public function send(array $payload): array;
}<?php
namespace Tests\Support\Fakes;
use App\Contracts\NotificationClientInterface;
class FakeNotificationClient implements NotificationClientInterface
{
public array $sent = [];
public function send(array $payload): array
{
$this->sent[] = $payload;
return [
'status' => 'ok',
'message_id' => 'fake-msg-001',
];
}
}Dengan kontrak seperti ini, controller atau service aplikasi tidak perlu tahu apakah implementasinya nyata atau fake. Keuntungannya:
- Test tetap cepat.
- Payload dapat diverifikasi secara eksplisit.
- Kegagalan service eksternal tidak membuat test internal ikut gagal.
Apa yang sebaiknya tetap diuji secara nyata
- Format autentikasi atau header yang rawan salah konfigurasi.
- Serialisasi request body yang sensitif terhadap skema.
- Timeout dan error mapping dasar, biasanya lewat environment sandbox atau stub terkontrol.
Jangan menjadikan semua PR bergantung pada API sungguhan. Itu bukan sinyal kualitas, melainkan sumber ketidakstabilan.
Contoh skenario regresi umum di multi-environment
Berikut beberapa regresi yang sering lolos jika test matrix tidak dirancang dengan benar:
1. Perbedaan base URL dan route
Di lokal route bekerja, tetapi di staging gagal karena base path atau rewrite berbeda. Tes yang relevan:
- Integration test untuk route utama.
- Smoke test untuk endpoint login, dashboard, dan health check.
2. Query berhasil di satu database, gagal di environment lain
Misalnya karena mode SQL lebih ketat, kolom nullable, atau urutan migrasi berbeda. Tes yang relevan:
- Database test untuk repository dan model.
- Smoke test migrasi di CI main branch atau staging.
3. Session atau cache berbeda perilaku
Login sukses di lokal, tetapi session tidak bertahan di staging karena driver berbeda. Tes yang relevan:
- Integration test untuk alur autentikasi.
- Smoke test login sederhana di environment mendekati produksi.
4. Dependency eksternal berubah format response
Unit test sering tetap hijau jika mock terlalu sederhana. Tes yang relevan:
- Integration test dengan fake yang meniru response error dan success.
- Verifikasi sandbox berkala untuk kontrak request/response.
5. Seed data tidak sinkron dengan asumsi test
Test kadang lolos jika dijalankan sendiri, tetapi gagal saat dijalankan penuh. Ini biasanya tanda ada kebocoran state. Tes yang relevan:
- Reset database yang konsisten.
- Isolasi fixture per skenario.
Desain workflow CI yang bertahap
Workflow CI yang baik seharusnya memberikan feedback secepat mungkin, lalu memperketat validasi hanya ketika perubahan memang layak diteruskan. Pola bertahap biasanya lebih efektif daripada satu job besar.
Tahap 1: Fast feedback untuk pull request
- Install dependency.
- Jalankan lint/static checks bila ada.
- Jalankan unit test.
- Jalankan integration test ringan tanpa external network.
Target tahap ini adalah menemukan error sintaks, regressi logika, dan kerusakan wiring dasar.
Tahap 2: Full validation untuk main branch
- Jalankan seluruh fast tests.
- Jalankan database test.
- Jalankan migrasi test database dari kondisi bersih.
- Bangun artefak deploy jika semua lolos.
Tahap ini memeriksa bahwa branch utama benar-benar dapat dipromosikan ke environment berikutnya.
Tahap 3: Post-deploy smoke test di staging
- Cek boot aplikasi.
- Panggil health endpoint.
- Verifikasi login atau endpoint publik utama.
- Cek konektivitas database dan dependency penting.
Smoke test tidak perlu panjang. Tujuannya adalah mendeteksi deploy yang jelas rusak, bukan menggantikan seluruh integration test.
Contoh alur ringkas
PR dibuka
- unit + integration ringan
- selesai cepat untuk reviewer
Merge ke main
- unit + integration + database
- build artefak
Deploy ke staging
- smoke test kritikal
- optional sandbox verification
Promote ke production-like
- smoke test readiness
- verifikasi konfigurasi dan konektivitasContoh implementasi test yang terfokus
Berikut contoh sederhana integration test untuk route kritikal. Fokusnya bukan detail API tertentu, tetapi memastikan endpoint bisa diakses dan memberi respons yang diharapkan pada mode testing.
<?php
namespace Tests\Integration\Controllers;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\FeatureTestTrait;
class HealthCheckTest extends CIUnitTestCase
{
use FeatureTestTrait;
public function testHealthEndpointReturnsOk()
{
$result = $this->get('/health');
$result->assertStatus(200);
$result->assertSee('ok');
}
}Test seperti ini layak dimasukkan ke kategori smoke atau integration ringan karena:
- Cepat dijalankan.
- Langsung mendeteksi aplikasi gagal boot atau route tidak terdaftar.
- Relevan di hampir semua environment.
Checklist untuk mengurangi flaky test
Flaky test merusak kepercayaan tim pada pipeline. Gunakan checklist berikut untuk menurunkan risikonya:
- Isolasi data: setiap test tahu data apa yang dipakai dan siapa yang membuatnya.
- Hindari ketergantungan urutan: test harus lolos walau dijalankan sendiri atau paralel.
- Kontrol waktu: hindari assertion yang sensitif pada detik aktual jika tidak perlu.
- Kontrol random: jika memakai generator acak, gunakan seed tetap.
- Batasi network nyata: gunakan fake atau mock untuk mayoritas test.
- Reset cache/session antar test atau gunakan driver in-memory sederhana.
- Gunakan timeout kecil dan eksplisit pada dependency eksternal di environment test.
- Pastikan migrasi idempoten untuk test database dari kondisi bersih.
- Jangan assert terlalu banyak hal yang tidak relevan; fokus pada perilaku yang benar-benar penting.
- Catat penyebab flaky dan perbaiki akarnya, bukan sekadar menambah retry.
Retry bisa berguna untuk gangguan infrastruktur sesaat, tetapi jika sering dipakai untuk menutupi test yang tidak stabil, masalah dasarnya tidak pernah selesai.
Rekomendasi praktis untuk memulai
Jika proyek Anda belum memiliki test matrix yang rapi, jangan mulai dari semua hal sekaligus. Langkah yang paling masuk akal biasanya:
- Pisahkan direktori unit, integration, database, dan smoke.
- Buat
.env.testingyang aman dan deterministik. - Tentukan 5-10 skenario kritikal yang wajib hijau sebelum merge.
- Tambahkan seed data minimal yang stabil.
- Ganti panggilan dependency eksternal di test dengan kontrak + fake.
- Susun workflow CI bertahap: PR cepat, main lebih lengkap, staging smoke test.
Dengan pendekatan ini, CodeIgniter 4 test matrix tidak hanya mencegah regresi multi-environment, tetapi juga menjaga developer tetap mendapat feedback cepat. Hasil akhirnya bukan sekadar lebih banyak test, melainkan test yang tepat dijalankan di tempat yang tepat.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!