Sebuah deployment baru-baru ini merusak tiga microservice meskipun setiap unit test, integration test, dan pengecekan mock-server telah lolos. Tim tersebut mengganti API mock mereka dengan contract testing. Dalam enam bulan, jumlah kontrak meningkat dari tiga menjadi 47 dan tingkat kegagalan integrasi bulanan turun dari dua insiden menjadi nol.
Mengapa mock tidak dapat melindungi Anda
Sebuah mock server hanya meniru bentuk yang diharapkan oleh consumer; ia tidak pernah memeriksa apakah provider benar-benar menyajikan bentuk tersebut. Jika provider mengubah nama field—misalnya dari name menjadi display_name—mock tetap mengembalikan payload lama, test consumer tetap hijau, dan sistem live mengalami crash. Kegagalan produksi pada pukul 14.00 di hari Selasa adalah tepat seperti itu: mock tersebut "berbohong" tentang kontrak yang sebenarnya.
Contract testing mengisi celah tersebut
Contract testing memaksa kedua sisi API untuk menyepakati definisi bersama sebelum kode apa pun menyentuh produksi. Ada dua pendekatan umum:
- Consumer-driven contracts – layanan konsumen menulis ekspektasi; layanan penyedia memvalidasinya. Ini bekerja dengan baik untuk microservice internal yang berkembang bersama.
- Provider-driven contracts – penyedia menerbitkan spesifikasi; konsumen memeriksa kode mereka terhadap spesifikasi tersebut. Ini adalah pola yang biasa untuk API publik.
Pendekatan pertama umumnya mencegah integrasi yang rusak di dalam arsitektur microservice.
Cara kerja consumer-driven contract
- Consumer menulis test yang menjelaskan secara tepat apa yang dibutuhkannya dari provider.
- Menjalankan test tersebut menghasilkan pact file – sebuah dokumen JSON yang mencatat ekspektasi tersebut.
- Provider menjalankan layanan aslinya terhadap pact file di dalam pipeline CI-nya.
- Jika provider mengubah sebuah field, verifikasi gagal dan build akan terblokir.
Karena verifikasi dijalankan pada kode provider yang sebenarnya, setiap breaking change akan terdeteksi lebih awal, bukan setelah deployment.
Di mana posisi contract test dalam piramida pengujian Anda
- Unit tests – cepat, menguji logika yang terisolasi.
- Contract tests – kecepatan sedang, mengonfirmasi bahwa perjanjian API tetap terjaga.
- End-to-end tests – lambat, menjalankan seluruh alur bisnis.
Perlakukan contract test sebagai jembatan antara feedback cepat dari unit test dan cakupan luas dari suite end-to-end. Targetkan titik-titik integrasi yang paling sering rusak dan mulailah dengan dua atau tiga endpoint kritis.
Kisah adopsi di dunia nyata
Tim yang memicu artikel ini memulai dengan tiga kontrak yang mencakup panggilan (calls) mereka yang paling rapuh. Enam bulan kemudian, mereka memiliki 47 kontrak yang mencakup mayoritas lalu lintas antar-layanan. Selama periode tersebut, insiden kerusakan API turun dari dua kali per bulan menjadi nol.
Kapan contract testing mungkin tidak sepadan
- Anda adalah pengembang solo yang menyimpan semua layanan dalam satu repositori tunggal.
- API sangat stabil dan tidak berubah selama bertahun-tahun.
- Anda sedang membangun prototipe sementara yang akan segera dibuang.
Dalam skenario tersebut, overhead untuk memelihara kontrak dapat lebih besar daripada manfaatnya.
Potensi kekurangan dan cara memitigasinya
- Simpan kontrak dengan versi (versioned) bersamaan dengan kode yang dijelaskannya.
- Otomatiskan verifikasi di setiap proses CI untuk menghindari kontrak yang usang (stale).
- Tinjau perubahan kontrak dalam pull request untuk menangkap kerusakan yang tidak disengaja.
Kesimpulan
Jika Anda masih mengandalkan mock buatan manual untuk meyakinkan diri sendiri bahwa layanan Anda dapat berkomunikasi, Anda sedang bertaruh pada janji palsu. Contract testing mengubah taruhan tersebut menjadi perjanjian yang dapat diverifikasi, menangkap breaking change sebelum mencapai produksi dan, seperti yang ditunjukkan oleh angka-angka tim tersebut, dapat menghilangkan kegagalan integrasi sepenuhnya.
