Contract Testing: Menangkap Janji API yang Rusak Sebelum Mencapai Produksi
Anda melakukan deploy perubahan ke user service. Semua pengujian lolos. Pipeline hijau. Lima menit kemudian, notification service mulai memuntahkan error di produksi. Pengguna melihat layar kosong. Akar masalahnya? Anda menghapus sebuah field dari respons API yang tidak pernah digunakan oleh service Anda sendiri, tetapi ternyata dibutuhkan oleh notification service.
Integration test tidak menangkap ini. Kedua tim menjalankan pengujian mereka sendiri, dan semuanya lolos. Masalah baru muncul ketika service-service tersebut benar-benar berkomunikasi satu sama lain di produksi. Inilah celah yang diisi oleh contract testing.
Masalah Sebenarnya yang Dilewatkan Integration Test
Integration test memverifikasi bahwa service dapat terhubung dan bertukar data. Namun, integration test tidak menjamin bahwa kesepakatan antar service tetap valid ketika salah satu pihak berubah. Dua tim bisa memiliki integration test yang hijau sempurna secara independen, tetapi tetap merusak sistem satu sama lain pada deploy berikutnya.
Pikirkan seperti ini: Tim A menjalankan user service. Tim B menjalankan notification service yang mengonsumsi data pengguna. Tim A memutuskan untuk membersihkan respons API dengan menghapus field yang terlihat tidak digunakan di dalam basis kode mereka sendiri. Integration test mereka lolos karena mereka menguji terhadap service mereka sendiri. Integration test Tim B juga lolos karena mereka menguji terhadap mock atau snapshot dari API lama. Kontrak yang rusak baru terungkap ketika kedua service berjalan bersama di staging atau produksi.
Apa yang Sebenarnya Dilakukan Contract Testing
Contract testing membuat kesepakatan implisit antar service menjadi eksplisit dan dapat diperiksa. Setiap kali dua service berkomunikasi melalui API, ada sebuah kesepakatan tentang apa yang dikirim dan apa yang diterima. Contract testing mengubah kesepakatan itu menjadi pemeriksaan otomatis.
Konsep ini bekerja dengan dua peran:
Diagram urutan berikut menunjukkan bagaimana provider mempublikasikan kontrak dan consumer memverifikasinya sebelum deployment:
- Provider: Service yang menyediakan API
- Consumer: Service yang memanggil API
Provider mendeklarasikan apa yang dijamin untuk diberikan. Consumer mendeklarasikan apa yang sebenarnya dibutuhkan. Selama kedua pihak setuju, contract test lolos. Ketika sesuatu berubah, pengujian gagal sebelum perubahan mencapai produksi.
Sebagian besar tim menggunakan pendekatan consumer-driven. Consumer mendefinisikan ekspektasinya dalam sebuah file kontrak. Provider kemudian memeriksa apakah API-nya masih memenuhi semua kontrak dari setiap consumer. Jika perubahan provider melanggar kontrak apa pun, tim provider segera mengetahuinya. Mereka dapat berbicara dengan tim consumer, menyesuaikan perubahan, atau membuat versi API tanpa merusak consumer yang sudah ada.
Mengapa Contract Test Lebih Cepat dari Integration Test
Keuntungan praktis terbesarnya adalah kecepatan dan independensi. Contract test tidak perlu menjalankan service lain. Mereka tidak membutuhkan database sungguhan atau lingkungan staging yang lengkap. Anda cukup menjalankan provider dengan data uji yang telah ditentukan dan memeriksa apakah responsnya sesuai dengan kontrak.
Sebuah contract test selesai dalam hitungan detik. Integration test yang memutar beberapa service dan database membutuhkan waktu menit. Dalam pipeline CI, perbedaan itu sangat berarti. Anda dapat menjalankan contract test lebih awal dan gagal cepat, tanpa menunggu rangkaian integration test yang mahal.
Contract test juga membantu dengan dependensi eksternal yang tidak dapat Anda kendalikan. Jika aplikasi Anda memanggil API pihak ketiga, Anda dapat menulis contract test yang memeriksa apakah API eksternal tersebut masih mengembalikan format yang diharapkan. Ketika pihak ketiga mengubah API mereka, contract test Anda gagal dan Anda tahu sebelum pengguna terkena dampaknya.
Apa yang Tidak Dicakup oleh Contract Test
Contract test hanya memeriksa format dan struktur. Mereka memverifikasi bahwa field yang tepat ada dengan tipe yang tepat. Mereka tidak memeriksa apakah data benar dari perspektif bisnis. Mereka tidak menguji stabilitas jaringan, autentikasi di produksi, atau waktu respons di bawah beban.
Untuk hal-hal tersebut, Anda masih membutuhkan integration test. Contract test dan integration test saling melengkapi, bukan menggantikan. Contract test menangkap ketidakcocokan struktural lebih awal. Integration test menangkap masalah runtime dan data di kemudian hari.
Di Mana Contract Test Cocok dalam Pipeline Anda
Tempatkan contract test setelah unit test dan sebelum integration test. Urutan ini masuk akal karena contract test lebih cepat daripada integration test tetapi memberikan keyakinan tambahan bahwa service yang akan Anda uji bersama masih kompatibel. Jika contract test gagal, tidak ada gunanya menjalankan integration test yang mahal karena kemungkinan besar juga akan gagal.
Berikut adalah urutan pipeline yang umum:
- Unit test
- Contract test
- Integration test
- End-to-end test (jika diperlukan)
Di Mana Memulai
Jangan mencoba menambahkan contract test ke semua service sekaligus. Mulailah dengan service yang paling sering berubah dan paling sering menimbulkan masalah saat berkomunikasi dengan service lain. Inilah titik-titik gesekan di mana tim sering saling merusak.
Cari sinyal-sinyal berikut:
- Service di mana perubahan satu tim sering merusak fitur tim lain
- API yang bergantung pada banyak consumer
- Service yang sering mengubah format respons mereka
- API eksternal yang pernah berubah secara tidak terduga di masa lalu
Fokus pada hal-hal tersebut terlebih dahulu. Setelah contract test berjalan dan menangkap masalah nyata, perluas ke service lain secara bertahap.
Daftar Periksa Praktis Sebelum Menambahkan Contract Test
- Identifikasi tiga pasang service teratas yang paling sering menyebabkan kerusakan lintas tim
- Putuskan apakah akan menggunakan kontrak consumer-driven atau provider-driven
- Pilih alat contract testing yang sesuai dengan tumpukan teknologi Anda (Pact, Spring Cloud Contract, atau sejenisnya)
- Mulai dengan satu provider dan satu consumer
- Jalankan contract test di CI sebelum integration test
- Siapkan proses untuk memberi tahu tim ketika kontrak rusak
Intisari
Contract testing menangkap ketidakcocokan API pada saat perubahan dilakukan, bukan setelah deployment. Contract testing berjalan cepat, tidak memerlukan lingkungan penuh, dan memberikan peringatan dini kepada tim sebelum janji yang rusak mencapai produksi. Mulailah dengan batasan service yang paling menyakitkan, otomatiskan kontraknya, dan biarkan pipeline memberi tahu Anda ketika kesepakatan rusak. Pengguna Anda tidak akan pernah tahu perbedaannya, dan itulah intinya.