Pemrograman & Development

Contract Testing: Cara Tim Microservices Berhenti Saling Menyalahkan Saat Deploy

Ada momen yang akrab bagi hampir setiap tim yang memakai arsitektur microservices. Tim A men-deploy versi baru layanan mereka. Sepuluh menit kemudian, tim B panik karena endpoint…


Ada momen yang akrab bagi hampir setiap tim yang memakai arsitektur microservices. Tim A men-deploy versi baru layanan mereka. Sepuluh menit kemudian, tim B panik karena endpoint yang mereka panggil tiba-tiba mengembalikan struktur data berbeda. Slack dipenuhi tuduhan, war room dibuka, dan root cause akhirnya ditemukan: sebuah field yang di-rename tanpa pemberitahuan.

Masalahnya bukan pada kode. Masalahnya pada asumsi. Tim A mengasumsikan tidak ada yang memakai field itu. Tim B mengasumsikan field itu akan selalu ada. Kedua tim benar menurut pemahaman masing-masing, dan keduanya sama-sama tidak punya cara untuk membuktikannya sebelum deploy.

Contract testing hadir untuk menutup celah itu.

Apa Itu Contract Testing, Sebenarnya

Contract testing adalah praktik di mana dua layanan yang saling berkomunikasi menyepakati sebuah "kontrak" eksplisit tentang bentuk interaksi mereka, lalu masing-masing layanan diuji secara terpisah terhadap kontrak tersebut.

Bedanya dengan integration testing konvensional cukup mendasar. Integration test membutuhkan kedua layanan hidup bersamaan di lingkungan yang sama. Artinya, pengujian baru bisa jalan ketika semua dependensi siap, environment tersedia, dan tidak ada yang sedang deploy. Dalam praktiknya, ini berarti integration test sering kali lambat, rapuh, dan menjadi bottleneck rilis.

Contract test tidak menuntut hal itu. Konsumen layanan menulis ekspektasi mereka terhadap penyedia layanan, ekspektasi itu direkam menjadi artefak kontrak, dan penyedia layanan memverifikasi bahwa implementasi mereka masih memenuhi semua kontrak yang ada. Kedua belah pihak bisa menguji tanpa perlu bertemu di satu environment.

Mengapa Ini Relevan untuk Startup, Bukan Hanya Perusahaan Besar

Ada anggapan bahwa contract testing adalah kemewahan perusahaan besar dengan puluhan tim. Anggapan itu keliru, dan justifikasinya justru terbalik.

Startup biasanya memiliki tim kecil yang memegang banyak layanan sekaligus. Satu orang bisa jadi pemilik tiga service sekaligus. Dalam kondisi seperti ini, koordinasi informal masih mungkin dilakukan lewat percakapan langsung. Namun justru di sinilah bahayanya: ketika tim tumbuh dari lima menjadi lima belas orang, kebiasaan koordinasi informal yang dulu berhasil berubah menjadi sumber kesalahan yang sistematis. Tidak ada yang sengaja menghapus field yang dipakai tim lain, tetapi tidak ada mekanisme yang mencegahnya.

Contract testing memasang pagar sebelum pagar itu benar-benar dibutuhkan.

Manfaat keduanya adalah kecepatan. Tanpa contract testing, tim biasanya menahan deploy sampai seluruh rangkaian integration test hijau. Dengan contract testing, setiap tim bisa bergerak independen karena kepastian kompatibilitas sudah ditegakkan di level kontrak, bukan di level environment bersama.

Tiga Pola yang Layak Dipertimbangkan

Pertama, consumer-driven contract. Dalam pola ini, tim yang mengonsumsi API yang menuliskan kontraknya terlebih dahulu. Mereka mendefinisikan dengan tepat field apa yang mereka pakai, tipe datanya, dan kondisi apa yang mereka harapkan. Kontrak ini kemudian dipublikasikan, dan tim penyedia layanan menjalankannya sebagai bagian dari pipeline mereka. Jika perubahan pada penyedia layanan melanggar kontrak, pipeline gagal sebelum kode mencapai produksi.

Keunggulan pola ini: kontrak mencerminkan kebutuhan nyata, bukan spekulasi. Sebuah field hanya dianggap penting jika ada konsumen yang benar-benar memakainya.

Kedua, contract sebagai dokumentasi hidup. Banyak tim menulis dokumentasi API secara manual, dan hampir semua dokumentasi manual pasti basi. Dengan contract testing, artefak kontrak dapat berfungsi ganda sebagai dokumentasi yang selalu sinkron dengan kode. Jika dokumentasi menyimpang dari implementasi, salah satunya akan gagal dalam pengujian.

Ketiga, versioning yang disiplin. Contract testing memaksa tim untuk berpikir serius tentang kompatibilitas mundur. Menambah field opsional umumnya aman. Mengubah tipe data atau menghapus field yang masih dipakai tidak. Dengan kontrak eksplisit, keputusan ini menjadi diskusi konkret, bukan sesuatu yang ditemukan setelah produksi bermasalah.

Jebakan yang Sering Terjadi

Contract testing bukan obat mujarab, dan ada beberapa kesalahan umum yang membuatnya tidak memberikan nilai.

Yang paling sering adalah memperlakukan kontrak sebagai formalitas. Tim menulis kontrak yang terlalu longgar, hanya memverifikasi bahwa response berstatus 200, tanpa benar-benar memeriksa struktur dan isi data. Kontrak seperti ini lolos dari semua pengujian tetapi tidak menangkap apa pun yang berharga.

Kesalahan kedua adalah tidak memperbarui kontrak ketika kebutuhan berubah. Kontrak yang tertinggal dari realitas lebih berbahaya daripada tidak ada kontrak sama sekali, karena memberi rasa aman yang palsu.

Kesalahan ketiga adalah menerapkan contract testing pada semua interaksi tanpa pandang bulu. Untuk layanan internal yang jarang berubah dan hanya dipakai oleh satu konsumen, biaya memelihara kontrak bisa lebih besar daripada manfaatnya. Prioritaskan pada batas antar tim, pada layanan dengan banyak konsumen, dan pada endpoint yang sering berubah.

Hubungannya dengan Pengujian Lain

Contract testing tidak menggantikan unit test atau end-to-end test. Ketiganya bekerja pada lapisan berbeda.

Unit test memastikan logika internal benar. Contract test memastikan perjanjian antar layanan terjaga. End-to-end test memastikan alur bisnis dari ujung ke ujung berjalan, tetapi dijalankan jauh lebih jarang karena mahal dan lambat.

Kombinasi yang sehat: unit test dijalankan pada setiap commit, contract test pada setiap pull request, dan end-to-end test pada tahap sebelum rilis. Dengan pembagian seperti ini, umpan balik tercepat datang dari pengujian termurah, dan pengujian termahal hanya dijalankan saat benar-benar diperlukan.

Mulai dari Mana

Untuk tim yang ingin mencoba, jalan terpendek biasanya bukan mengadopsi alat contract testing untuk seluruh sistem sekaligus. Pilih satu titik integrasi yang paling sering menyebabkan masalah, biasanya antara layanan yang dikelola tim berbeda. Tulis kontrak untuk titik itu saja. Rasakan sendiri apakah kontrak tersebut benar-benar menangkap masalah sebelum produksi.

Jika ya, perluas ke titik integrasi berikutnya. Jika tidak, evaluasi apakah masalahnya ada pada kontrak yang terlalu longgar atau memang titik itu tidak membutuhkan pendekatan ini.

Yang perlu diingat: tujuan contract testing bukan menambah pekerjaan administratif. Tujuannya mengurangi percakapan darurat pada jam sebelas malam, dan menggantinya dengan diskusi terstruktur yang terjadi sebelum kode di-merge.

Itu perbedaan yang cukup layak diperjuangkan.