Semakin besar aplikasi, semakin sulit menentukan jenis test yang harus ditulis. Unit test memang cepat, tetapi terlalu banyak mock dapat menjauhkan pengujian dari kondisi production.
Integration test lebih realistis, tetapi jika semua fitur diuji melalui database dan network, pipeline bisa berubah menjadi sangat lambat.
Itulah alasan Cara Menyeimbangkan Unit Test dan Integration Test perlu mengikuti struktur aplikasi serta pola kegagalannya.
Bukan jumlah test yang menjadi tujuan utama, melainkan kemampuan suite memberikan feedback cepat sekaligus menangkap bug yang benar-benar mungkin terjadi saat komponen saling terhubung.
Mulai dari Pertanyaan: Apa yang Sedang Dibuktikan?
Sebelum menulis test, tanyakan apa risiko yang ingin dibuktikan.
Jika ingin memastikan perhitungan pajak menghasilkan angka benar, database bukan bagian dari masalah.
Gunakan unit test.
Namun jika ingin memastikan hasil perhitungan dapat disimpan dengan benar melalui ORM ke database production, integration test lebih relevan.
Microsoft membedakan unit test sebagai pengujian terhadap individual component dan integration test sebagai pengujian terhadap dua atau lebih komponen yang bekerja bersama. Infrastructure seperti database dan network sering masuk pada kategori kedua.
Pertanyaan sederhana ini mencegah test menjadi terlalu besar.
Developer sering membuat integration test bukan karena perlu, tetapi karena lebih gampang menjalankan seluruh aplikasi daripada memisahkan business logic.
Dalam jangka panjang, kebiasaan itu membuat suite semakin berat.
Bangun Fondasi Unit Test yang Cepat
Unit test seharusnya menjadi tempat utama untuk menguji variasi logic.
Bayangkan sistem subscription memiliki empat tipe paket, tiga jenis diskon, dan lima kondisi renewal.
Kombinasinya dapat menghasilkan puluhan skenario.
Jika semuanya dilakukan melalui integration test dengan database, proses pengujian menjadi jauh lebih mahal dibanding manfaat yang didapat.
Google menyebut basis unit test yang kuat sebagai salah satu mekanisme penting untuk menangkap bug sebelum perubahan masuk lebih jauh ke codebase.
Ketika sistem berkembang, integration test kemudian melengkapi pengujian tersebut pada interaksi antarkomponen.
Microsoft juga merekomendasikan unit test yang cepat, independen, repeatable, dan self-checking.
Artinya, developer idealnya dapat menjalankan unit suite berkali-kali selama coding tanpa merasa terganggu oleh waktunya.
Jika unit test membutuhkan beberapa menit, evaluasi apakah scope-nya sudah terlalu besar.
Jangan Membuat Integration Test Terlalu Luas
Integration test tidak harus berarti menjalankan seluruh sistem.
Inilah salah satu kesalahpahaman yang sering membuat pengujian mahal.
Fowler menjelaskan bahwa integration test dapat dibuat dengan scope sempit. Misalnya satu pengujian hanya memastikan aplikasi dapat menulis dan membaca data dari database, sementara dependency lain diganti dengan test double.
Misalnya OrderRepository terhubung ke PostgreSQL.
Integration test cukup menjalankan PostgreSQL, membuat data yang dibutuhkan, memanggil repository, lalu memastikan hasil tersimpan sesuai schema.
Tidak perlu sekaligus menyalakan payment service, notification service, dan frontend.
Cara ini membuat failure lebih mudah didiagnosis.
Jika test gagal, developer langsung tahu masalah kemungkinan berada di boundary antara repository dan database, bukan harus mencari di sepuluh service.
Scope kecil juga membuat integration test lebih mudah dirawat.
Pakai Integration Test untuk Perilaku Infrastructure yang Nyata
Beberapa hal sulit atau bahkan berbahaya jika hanya diuji menggunakan mock.
Contohnya database constraint.
Mock repository mungkin mengatakan dua pelanggan dengan email yang sama boleh disimpan. Database production ternyata mempunyai unique constraint.
Masalah baru terlihat saat runtime.
Hal serupa dapat terjadi pada SQL query, transaction rollback, serialization, migration, message format, atau framework configuration.
Microsoft secara eksplisit menyarankan membatasi integration test pada skenario infrastructure yang paling penting.
Jika suatu behavior dapat diuji dengan unit maupun integration test, unit test biasanya lebih murah; integration test dipakai ketika interaksi dengan komponen asli memang ingin diverifikasi.
Jadi, integrasi tidak harus diuji dalam setiap variasi data.
Misalnya cukup ada skenario create, read, update, constraint failure, serta transaction behavior yang kritis.
Variasi business rule lainnya tetap berada di unit level.
Gunakan Real Database tanpa Membuat Environment Rumit
Salah satu alasan developer menghindari integration test adalah setup infrastructure.
Shared staging database sering membuat masalah karena data berubah, test saling mengganggu, dan developer tidak bisa menjalankannya secara independen.
Container sementara menawarkan alternatif.
Testcontainers memungkinkan aplikasi menjalankan dependency seperti database dan message queue dalam container singkat selama integration test berlangsung.
Dengan cara ini, developer dapat menjalankan database engine yang sama seperti production tanpa harus memelihara server test permanen.
Misalnya CI menjalankan PostgreSQL container, melakukan migration, mengeksekusi test, lalu menghancurkan container.
Environment menjadi lebih mudah direproduksi.
Namun startup container tetap mempunyai biaya.
Jangan menjalankan database baru untuk setiap unit test. Gunakan fixture atau lifecycle yang masuk akal sesuai framework, tanpa membuat test saling berbagi state secara sembarangan.
Tujuannya adalah realisme dengan kompleksitas seminimal mungkin, bukan membuat infrastrktru testing lebih rumit daripada production.
Hindari Duplikasi antara Kedua Level
Masalah test suite sering bukan kekurangan test, tetapi test yang sama ditulis berkali-kali.
Misalnya fungsi validasi umur mempunyai 15 edge case.
Developer menulis 15 unit test lalu mengulang 15 skenario yang sama melalui integration test.
Hasilnya confidence tidak naik banyak, tetapi waktu eksekusi dan maintenance menjadi dua kali lipat.
Practical Test Pyramid menyarankan menghindari duplikasi antarlapisan karena setiap test mempunyai biaya untuk ditulis, dibaca, dijalankan, dan dipelihara.
Integration test cukup membuktikan bahwa validasi tersebut benar-benar terhubung dengan application flow.
Tidak perlu kembali menguji seluruh tabel input.
Gunakan prinsip: uji detail pada level terendah yang mampu membuktikannya, lalu gunakan level yang lebih tinggi hanya untuk risiko tambahan.
Pendekatan ini membuat suite jauh lebih terfokuss.
Susun Pipeline Berdasarkan Waktu Eksekusi
Tidak ada aturan bahwa seluruh test harus berjalan dalam satu tahap CI.
Kelompokkan berdasarkan feedback speed.
Unit test biasanya bisa berjalan setiap commit dan sebelum developer melakukan push.
Narrow integration test yang selesai cepat juga dapat berjalan pada tahap yang sama.
Integration suite yang membutuhkan beberapa container atau setup lebih berat dapat berjalan ketika pull request dibuat.
Fowler menekankan bahwa pipeline sebaiknya mempertimbangkan speed dan scope daripada terlalu terpaku pada label test. Integration test yang sempit dan cepat bahkan dapat memberikan feedback hampir secepat unit test.
Google menggunakan klasifikasi small, medium, dan large test untuk alasan serupa: membedakan constraint serta membantu test dijalankan konsisten dan paralel.
Target akhirnya adalah developer mengetahui kegagalan secepat mungkin.
Bug business logic seharusnya ditemukan dalam hitungan detik, bukan setelah pipeline deployment berjalan setengah jam.
Biarkan Bug Production Memperbaiki Strategi Testing
Test strategy tidak harus sempurna sejak proyek dimulai.
Gunakan kegagalan nyata sebagai feedback.
Misalnya ditemukan bug karena format timestamp antara aplikasi dan database berbeda.
Jangan hanya memperbaiki bug.
Tambahkan integration regression test pada boundary tersebut agar masalah yang sama tidak terjadi lagi.
Sebaliknya, jika bug muncul pada calculation logic dan sudah dapat direproduksi secara terisolasi, tambahkan unit test.
Dengan cara ini, distribusi test berkembang berdasarkan failure mode yang nyata.
Perhatikan juga flaky rate dan waktu eksekusi.
Integration test yang sering gagal karena konfigruasi environment harus diperbaiki, bukan dibiarkan menjadi noise.
Google menekankan test isolation agar pengujian tidak bergantung pada urutan eksekusi atau data yang ditinggalkan oleh test lain. Isolasi juga membantu parallel execution dan membuat suite lebih konsisten.
Test suite yang sehat adalah sistem yang terus dievaluasi, bukan sekadar kumpulan file test yang bertambah setiap sprint.
Cara Menyeimbangkan Unit Test dan Integration Test dimulai dengan memahami risiko yang ingin dibuktikan.
Dorong variasi business logic ke unit test, gunakan integration test secara sempit untuk database, API, dan infrastructure nyata, lalu hindari duplikasi antarlevel.
Pantau kecepatan serta stabilitas pipeline secara rutin. Audit bug production terbaru Anda dan pastikan setiap regression ditempatkan pada level test paling efisien.