Checklist contract testing untuk API auth dan webhook dibutuhkan ketika integrasi lintas layanan tidak cukup dijaga oleh unit test. Pada level fungsi, kode Anda mungkin benar. Namun pada level integrasi, perubahan kecil seperti nama field, status code, urutan event, format timestamp, atau perilaku retry dapat membuat sistem rusak tanpa error yang jelas.
Masalah ini paling sering muncul pada dua area yang sensitif: API auth dan webhook. API auth biasanya menjadi gerbang semua request lain, sehingga perubahan kontrak token, header, atau error response akan menyebar ke seluruh alur. Webhook lebih rawan lagi karena sifatnya asinkron, bisa retry, bisa terkirim duplikat, dan sering bergantung pada signature verification serta asumsi urutan event.
Jika Anda mengikuti diskusi yang sering muncul di komunitas engineering tentang rapuhnya software modern, konteksnya bukan sekadar bahwa testing harus lebih banyak, tetapi bahwa jenis testing harus tepat. Untuk integrasi API yang rawan gagal diam-diam, contract testing memberi perlindungan yang tidak bisa digantikan unit test biasa.
Mengapa unit test saja tidak cukup
Unit test memverifikasi logika internal aplikasi Anda dalam isolasi. Itu penting, tetapi tidak membuktikan bahwa aplikasi Anda masih kompatibel dengan layanan eksternal. Saat integrasi bergantung pada kontrak yang berada di luar repositori Anda, sumber kegagalannya bukan hanya bug lokal, melainkan juga perubahan perilaku dari provider atau kesalahpahaman terhadap spesifikasi.
Contoh kegagalan yang sering terjadi:
- Provider auth mengubah field
expires_indari integer menjadi string. - Endpoint token yang dulu mengembalikan
200kini mengembalikan201atau204pada kondisi tertentu. - Field
errortetap ada, tetapi strukturdetailsberubah sehingga parser gagal. - Webhook pembayaran yang biasanya mengirim
payment.succeededsetelahpayment.createdtiba-tiba mengirim urutan terbalik. - Provider melakukan retry webhook dengan event yang sama, tetapi sistem Anda memprosesnya dua kali karena tidak idempotent.
Semua kasus di atas bisa lolos dari unit test jika unit test menggunakan mock yang terlalu ideal atau fixture yang tidak mewakili realitas. Contract testing menutup celah itu dengan menguji ekspektasi antarlayanan, bukan hanya implementasi internal.
Apa yang harus diuji dalam contract testing
Pada integrasi API auth dan webhook, kontrak tidak berhenti di bentuk JSON. Kontrak juga mencakup perilaku protokol, kode status, header, retry, timeout, dan semantik event.
1. Skema request dan response
Ini lapisan paling dasar:
- Field wajib dan opsional.
- Tipe data: string, integer, boolean, object, array.
- Format nilai: ISO-8601 timestamp, UUID, currency code, enum status.
- Header yang wajib ada, misalnya
Authorization,Content-Type, atau header signature. - Apakah field tambahan boleh diabaikan atau dianggap error.
Untuk API auth, contohnya meliputi request token, response access token, token type, masa berlaku, dan error jika credential salah. Untuk webhook, contohnya payload event, event id, resource id, tipe event, timestamp, dan signature header.
2. Error contract
Banyak integrasi rusak justru pada jalur error karena hanya jalur sukses yang diuji. Error contract perlu menjawab:
- Status code apa yang dipakai untuk auth gagal, token kedaluwarsa, rate limit, atau payload invalid.
- Apakah body error konsisten dan bisa diparse.
- Field apa yang stabil untuk pengambilan keputusan, misalnya
error_codedibanding pesan teks. - Apakah retry aman untuk jenis error tertentu.
Hindari mengandalkan pesan error bebas seperti "invalid request" sebagai dasar logika aplikasi. Gunakan kode error yang stabil bila tersedia.
3. Versioning
Versi kontrak harus eksplisit. Bentuknya bisa melalui path, header, media type, atau dokumentasi perubahan yang jelas. Yang penting, tim consumer tahu kapan perubahan bersifat backward compatible dan kapan tidak.
Yang perlu diuji:
- Apakah versi lama masih diterima selama masa transisi.
- Apakah field baru benar-benar opsional bagi consumer lama.
- Apakah penghapusan field lama terdeteksi oleh contract test sebelum rilis.
4. Idempotency key
Pada API auth, idempotency sering tidak dominan untuk endpoint token biasa, tetapi sangat penting untuk endpoint yang memicu perubahan state, misalnya pembuatan session, link login, atau proses refresh tertentu di provider tertentu. Pada webhook, konsepnya lebih jelas: event yang sama bisa dikirim lebih dari sekali.
Yang perlu diuji:
- Request dengan
Idempotency-Keyyang sama tidak menyebabkan duplikasi state. - Response untuk retry tetap konsisten atau setidaknya aman ditangani.
- Event webhook dengan
event_idyang sama hanya diproses sekali.
5. Signature verification
Webhook tanpa verifikasi signature berarti membuka pintu untuk request palsu. Namun verifikasi signature juga sering menjadi sumber bug karena perbedaan canonical payload, encoding, newline, atau penggunaan timestamp.
Yang perlu diuji:
- Header signature wajib ada.
- Payload diverifikasi terhadap raw body, bukan JSON yang sudah diparse lalu diserialisasi ulang.
- Timestamp signature diperiksa untuk mencegah replay attack.
- Request dengan signature salah ditolak dengan jelas.
6. Retry behavior, timeout, dan duplicate delivery
Webhook dan API eksternal tidak selalu deterministik. Contract test perlu menguji perilaku, bukan hanya struktur data:
- Berapa lama consumer boleh menunggu sebelum menganggap timeout.
- Apakah provider akan retry jika endpoint mengembalikan
5xxatau timeout. - Apakah provider menganggap
2xxsebagai ack final. - Apakah duplicate delivery mungkin terjadi walau response sebelumnya sudah sukses.
Jangan mendesain sistem webhook dengan asumsi “satu event datang satu kali dan urut”. Itu asumsi yang rapuh.
Contoh kasus nyata yang sering merusak integrasi
Field berubah tanpa breaking change yang diumumkan jelas
Misalnya provider auth sebelumnya mengirim:
{
"access_token": "abc",
"expires_in": 3600,
"token_type": "Bearer"
}Lalu di lingkungan tertentu atau rollout baru menjadi:
{
"access_token": "abc",
"expires_in": "3600",
"token_type": "Bearer"
}Jika parser Anda ketat dan mengharapkan integer murni, proses refresh token bisa gagal. Jika test Anda hanya memakai fixture lama, perubahan ini tidak akan terdeteksi sampai produksi.
Status code berubah, body tetap mirip
Aplikasi Anda mungkin hanya memperlakukan 200 sebagai sukses, padahal provider mulai mengembalikan 201 atau 204 pada skenario tertentu. Akibatnya, request sebenarnya berhasil tetapi aplikasi menandainya gagal dan memicu retry yang tidak perlu.
Urutan event berubah
Untuk webhook, banyak sistem secara tidak sadar mengasumsikan urutan event. Contoh: aplikasi hanya mau memproses payment.succeeded jika payment.created sudah lebih dulu masuk. Saat provider mengirim terbalik karena sistem event mereka bersifat eventual consistency, order internal Anda tidak pernah terupdate, atau lebih buruk, terupdate salah.
Pertahanan yang lebih aman adalah menjadikan event sebagai signal lalu menarik state terbaru dari API provider bila diperlukan, atau mendesain state machine lokal yang toleran terhadap urutan tidak pasti.
Checklist contract testing untuk API auth dan webhook
Bagian ini bisa dipakai langsung sebagai acuan review implementasi dan test plan.
Checklist untuk API auth
- Request contract
- Method, path, query, dan header tervalidasi.
- Content type sesuai yang didukung provider.
- Field credential wajib diuji untuk kondisi ada, kosong, salah format, dan tidak dikirim.
- Response sukses
- Field token wajib ada dan bertipe benar.
- Masa berlaku token diparse aman.
token_typetidak diasumsikan case-sensitive jika dokumentasi tidak menjamin.- Field tambahan diabaikan dengan aman.
- Error contract
401,403,400,429, dan5xxdipetakan ke perilaku yang tepat.- Retry hanya untuk error transient, bukan credential salah.
- Body error tanpa field opsional tetap bisa ditangani.
- Versioning
- Perubahan field opsional tidak mematahkan consumer.
- Field yang deprecated masih tertangani selama masa transisi.
- Timeout dan resiliency
- Timeout koneksi dan read timeout ditetapkan eksplisit.
- Refresh token tidak dilakukan paralel tanpa kontrol jika bisa memicu race condition.
- Keamanan
- Token tidak ditulis ke log mentah.
- Secret tidak ikut muncul di pesan error.
Checklist untuk webhook
- Request contract
- Header signature, event id, dan timestamp tersedia bila dijanjikan provider.
- Payload minimum untuk identifikasi event tervalidasi.
- Event type yang tidak dikenal tidak membuat sistem crash.
- Signature verification
- Verifikasi memakai raw body.
- Request dengan signature salah ditolak.
- Timestamp lama atau replay di luar toleransi ditolak bila skema mendukung.
- Idempotency dan duplicate delivery
- Event yang sama hanya diproses sekali berdasarkan event id stabil.
- Retry dari provider tidak menggandakan side effect.
- Retry behavior
- Handler mengembalikan
2xxhanya jika event sudah diterima dengan aman. - Jika pemrosesan berat, event segera diack lalu dipindahkan ke queue internal.
4xxdan5xxdipilih dengan sengaja karena memengaruhi retry provider.
- Handler mengembalikan
- Ordering dan state
- Sistem tidak mengasumsikan urutan event.
- Event lama yang datang belakangan tidak menimpa state lebih baru tanpa validasi versi atau timestamp.
- Observability
- Log menyimpan event id, provider, tipe event, hasil verifikasi, dan hasil deduplikasi.
- Metrik retry, duplicate delivery, dan signature failure tersedia.
Strategi consumer-driven contract yang praktis
Consumer-driven contract berarti consumer mendefinisikan ekspektasi minimum yang ia butuhkan dari provider, lalu ekspektasi itu diverifikasi terhadap implementasi provider atau simulasi yang dikontrol. Pendekatan ini cocok ketika tim consumer lebih dulu merasakan dampak perubahan kontrak.
Apa yang sebaiknya didefinisikan oleh consumer
- Field minimum yang benar-benar dipakai aplikasi.
- Nilai enum atau status yang diharapkan.
- Status code yang dianggap sukses atau retryable.
- Contoh error yang harus bisa ditangani.
Hindari menulis kontrak yang terlalu ketat pada field yang tidak Anda pakai. Jika provider menambah field baru, itu seharusnya tidak memecahkan consumer yang didesain robust. Kontrak yang terlalu kaku justru menghasilkan noise dan biaya maintenance tinggi.
Pola kontrak yang sehat
- Tegas pada hal penting: field wajib, tipe data, kode status, header keamanan.
- Longgar pada hal tambahan: field ekstra, urutan properti JSON, whitespace, deskripsi teks error.
- Pisahkan jalur sukses dan gagal: jangan hanya punya satu fixture happy path.
Fixture yang stabil: jangan terlalu ideal, jangan terlalu rapuh
Fixture sering menjadi sumber ilusi keamanan. Jika fixture dibuat terlalu bersih, test lulus tetapi tidak mewakili produksi. Jika fixture terlalu detail dan mengikuti seluruh payload provider, sedikit perubahan yang tidak relevan akan membuat test berisik.
Prinsip fixture yang stabil:
- Simpan payload nyata yang sudah disanitasi dari data sensitif.
- Pertahankan field yang benar-benar penting bagi logika bisnis Anda.
- Tambahkan variasi untuk kasus field opsional hilang, nilai null, tipe salah, event duplikat, dan event out-of-order.
- Jangan mengasumsikan urutan object JSON.
Untuk webhook, simpan juga raw body dan header signature contoh. Ini penting karena verifikasi signature tidak bisa diuji akurat jika hanya memakai object JSON hasil parse.
Negative test yang wajib ada
Negative test sering lebih bernilai daripada happy path karena integrasi gagal justru saat data tidak ideal.
Negative test untuk API auth
- Credential salah.
- Response sukses tetapi field token hilang.
- Status code sukses dengan body kosong.
429rate limit.5xxdan timeout.- Response JSON invalid atau content type tidak sesuai.
Negative test untuk webhook
- Signature salah.
- Timestamp replay terlalu lama.
- Payload valid tetapi event type tidak dikenal.
- Event id sama dikirim dua kali.
- Event datang terbalik.
- Body rusak atau JSON tidak valid.
Tujuan negative test bukan membuktikan semua input salah bisa diproses, tetapi memastikan sistem gagal dengan aman, dapat diamati, dan tidak menghasilkan side effect berbahaya.
Kapan memakai sandbox, kapan mock
Keduanya dibutuhkan, tetapi perannya berbeda.
Mock cocok untuk
- Test cepat di CI.
- Simulasi skenario error yang sulit dipicu di sandbox.
- Verifikasi logika consumer secara deterministik.
- Pengujian kontrak minimum yang Anda kontrol penuh.
Kelemahan mock adalah ia hanya sebaik asumsi Anda. Jika mock tidak mencerminkan provider yang sebenarnya, test bisa memberi rasa aman palsu.
Sandbox cocok untuk
- Memverifikasi integrasi dengan perilaku provider yang lebih nyata.
- Menguji handshake auth, sertifikat, timeout, dan beberapa edge case protokol.
- Validasi akhir sebelum rilis untuk alur kritis.
Kelemahan sandbox adalah sering tidak sepenuhnya sama dengan produksi. Data bisa terbatas, perilaku retry bisa berbeda, dan stabilitas lingkungan belum tentu baik untuk test yang deterministik.
Aturan praktis
Gunakan mock untuk cakupan luas dan cepat, lalu gunakan sandbox untuk validasi integrasi nyata pada jalur paling kritis. Jangan menggantungkan seluruh strategi testing hanya pada salah satunya.
Contoh implementasi praktis
Berikut contoh sederhana struktur verifikasi webhook yang fokus pada kontrak penting: raw body, signature, idempotency, dan ack cepat.
function handleWebhook(request) {
const rawBody = request.rawBody;
const signature = request.headers['x-signature'];
const eventId = request.headers['x-event-id'];
if (!signature || !verifySignature(rawBody, signature)) {
return { status: 401, body: 'invalid signature' };
}
if (!eventId) {
return { status: 400, body: 'missing event id' };
}
if (isDuplicateEvent(eventId)) {
return { status: 200, body: 'duplicate ignored' };
}
const event = JSON.parse(rawBody);
enqueueForAsyncProcessing({ eventId, event });
markEventReceived(eventId);
return { status: 202, body: 'accepted' };
}Poin penting dari contoh ini:
- Verifikasi dilakukan sebelum parse bisnis lebih jauh.
- Duplicate delivery ditangani eksplisit.
- Pemrosesan berat dipindahkan ke queue.
- Response cepat mengurangi timeout dan retry yang tidak perlu.
Untuk API auth, contoh kontrak minimum yang layak diuji bisa berupa validasi struktur response:
function validateTokenResponse(body) {
if (!body || typeof body.access_token !== 'string') {
throw new Error('invalid token response: access_token missing');
}
if (body.expires_in != null && Number.isNaN(Number(body.expires_in))) {
throw new Error('invalid token response: expires_in is not numeric');
}
return {
accessToken: body.access_token,
expiresIn: body.expires_in != null ? Number(body.expires_in) : null,
tokenType: body.token_type || 'Bearer'
};
}Contoh ini sengaja tidak terlalu ketat pada hal yang bisa berubah tanpa merusak semantik, tetapi tetap tegas pada field inti yang dibutuhkan consumer.
Debugging tips saat contract test gagal
- Bandingkan raw request/response, bukan hanya object hasil parse.
- Cek header: banyak bug ada pada
Content-Type, charset, signature, atau authorization scheme. - Log event id dan correlation id untuk melacak retry dan duplicate delivery.
- Bedakan error transient dan permanen agar retry tidak memperburuk keadaan.
- Uji dengan payload nyata yang disanitasi, bukan hanya contoh dari dokumentasi.
- Periksa asumsi ordering jika bug hanya muncul sesekali pada webhook.
Kesalahan umum yang perlu dihindari
- Menganggap dokumentasi provider selalu sama dengan perilaku aktual.
- Menggunakan mock yang terlalu sempurna dan tidak pernah mengirim data aneh.
- Tidak menguji jalur error dan retry.
- Mengandalkan pesan error bebas untuk logika program.
- Tidak menyimpan event id untuk deduplikasi.
- Memverifikasi signature dari JSON yang sudah dimodifikasi parser.
- Menganggap event webhook selalu datang sekali dan berurutan.
Penutup
Checklist contract testing untuk API auth dan webhook pada dasarnya adalah cara mengubah integrasi yang rapuh menjadi sistem yang bisa diverifikasi. Fokus utamanya bukan menambah test sebanyak mungkin, melainkan menguji kontrak yang benar-benar menentukan apakah dua layanan masih kompatibel: skema request/response, error contract, versioning, idempotency key, signature verification, retry behavior, timeout, dan duplicate delivery.
Jika Anda hanya mengandalkan unit test, banyak kegagalan integrasi akan baru terlihat setelah produksi. Dengan contract testing yang pragmatis, fixture yang stabil, negative test yang serius, dan pemakaian sandbox serta mock di tempat yang tepat, perubahan kecil dari provider tidak lagi mudah merusak sistem Anda secara diam-diam.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!