Tes yang bergantung pada API langsung adalah tes yang gagal karena alasan yang salah. Server *staging* mati, pembatasan *rate* pihak ketiga berlaku, rekan tim mengubah catatan, dan tiba-tiba *suite* Anda berwarna merah meskipun kode Anda baik-baik saja. *Mocking* API menghilangkan kerapuhan tersebut. Anda mengganti *endpoint* asli dengan pengganti terkontrol yang mengembalikan respons persis seperti yang Anda minta, setiap saat.
Panduan ini membahas langkah-langkah sebenarnya untuk *mocking* API untuk pengujian. Anda akan menentukan skema, menghasilkan respons *mock*, mengarahkan kode pengujian Anda ke *mock*, dan mencakup jalur kesalahan yang jarang dihasilkan oleh server asli sesuai permintaan. Contoh-contoh menggunakan API manajemen pesanan kecil, tetapi alur kerja ini berlaku untuk layanan REST atau GraphQL apa pun.
Kapan *mocking* API adalah pilihan yang tepat
*Mock* API ketika hal yang ingin Anda uji adalah kode Anda sendiri, bukan jaringan. Tes unit dan sebagian besar tes integrasi termasuk dalam kategori ini. Anda ingin tahu bahwa klien Anda mengurai 200 dengan benar, mencoba ulang pada 503, dan menampilkan pesan yang jelas pada 404. Semua itu tidak memerlukan server asli.
Pertahankan API asli untuk tes kontrak dan lapisan tipis pemeriksaan *end-to-end*. Itu ada untuk memastikan *mock* masih cocok dengan kenyataan. Jika Anda *mock* semuanya dan tidak pernah memverifikasi terhadap layanan langsung, *suite* Anda tetap hijau sementara produksi rusak. Pembagiannya kira-kira: *mock* untuk kecepatan dan isolasi, hubungi yang asli untuk mengonfirmasi kontrak. Untuk melihat lebih dalam di mana masing-masing cocok, lihat rincian ini tentang skenario di mana *mocking* API menguntungkan dan perbedaan antara server *mock* dan server asli.
Sekilas alur kerja lima langkah
*Mocking* API untuk pengujian selalu melibatkan lima langkah yang sama, terlepas dari bahasa atau *framework*:
- Definisikan skema agar *mock* mencerminkan bentuk respons asli.
- Hasilkan respons *mock*, statis atau dinamis, dari skema tersebut.
- Jalankan server *mock* yang menyajikan respons tersebut di URL.
- Arahkan tes Anda ke *mock* dengan membuat URL dasar dapat dikonfigurasi.
- Uji jalur kesalahan yang tidak akan dihasilkan server asli sesuai permintaan.
Sisa panduan ini membahas setiap langkah dengan contoh konkret: API manajemen pesanan kecil dengan *endpoint* GET /orders/{id}. Ingat *endpoint* ini sebagai benang merah.
Langkah 1: Definisikan skema
Sebuah *mock* hanya berguna jika mencerminkan bentuk respons asli. Mulailah dari skema. Jika API Anda sudah memiliki dokumen OpenAPI, gunakanlah. Jika tidak, tulis satu untuk *endpoint* yang sedang diuji. Berikut adalah definisi yang dipersingkat untuk GET /orders/{id}:
paths:
/orders/{id}:
get:
parameters:
- name: id
in: path
required: true
schema: { type: string }
responses:
'200':
content:
application/json:
schema:
type: object
properties:
id: { type: string }
status: { type: string, enum: [pending, shipped, delivered] }
total: { type: number }
items: { type: array, items: { type: object } }
'404':
description: Order not found
Skema melakukan dua pekerjaan. Ini memberi tahu *mock* bidang apa yang harus dikembalikan, dan ini memberi Anda satu sumber kebenaran. Ketika *backend* mengubah kontrak, Anda memperbarui skema dan setiap *mock* yang berasal darinya tetap akurat. *Mocking* berbasis skema adalah yang menjaga pengujian kontrak API tetap jujur.
Langkah 2: Hasilkan respons *mock*
Anda memiliki dua cara untuk menghasilkan *body* respons.
Respons statis adalah JSON tetap. Anda menulis *payload* yang persis sama satu kali dan *mock* mengembalikannya tanpa perubahan. Respons ini dapat diprediksi dan mudah untuk diuji, yang membuatnya ideal untuk tes unit di mana Anda menginginkan satu nilai yang diketahui.
Respons dinamis dihasilkan per permintaan. Daripada mengkodekan secara *hardcode* "total": 149.99, *mock* mengisi bidang dengan nilai yang dihasilkan secara realistis: UUID untuk id, anggota *enum* acak untuk status, jumlah mata uang yang masuk akal untuk total. Data dinamis menangkap *bug* yang disembunyikan oleh satu *payload* tetap, seperti *parser* yang rusak pada *string* panjang atau bidang *null* yang tidak terduga.
Sebagian besar tim menggunakan keduanya. *Payload* statis untuk tes yang banyak mengandalkan pernyataan (*assertion*), dan generasi dinamis untuk cakupan gaya *fuzz*. Dengan Apidog, Anda tidak menulis keduanya secara manual. Apidog membaca skema dan secara otomatis menghasilkan *endpoint* *mock*, mencocokkan nama bidang seperti email, phone, atau avatar dengan tipe data yang benar. Anda mengarahkan *browser* ke URL *mock* dan langsung mendapatkan respons yang valid.
Ketika Anda menulis *payload* secara manual, buatlah realistis. Pesanan uji dengan "total": 0 dan larik items kosong akan melewati *parser* yang naif tetapi menyembunyikan *bug*. Gunakan nilai yang menyerupai produksi: total yang terlihat nyata, dua atau tiga item baris, status yang benar-benar ada di *enum*. Semakin dekat data *mock* dengan apa yang dikembalikan oleh permintaan asli, semakin berharga tes Anda.
Langkah 3: Jalankan server *mock*
Respons *mock* tidak ada gunanya sampai ada sesuatu yang menyajikannya. Anda memiliki dua pilihan *hosting*.
Server *mock* lokal berjalan di mesin Anda, biasanya di *port* seperti localhost:4010. Ini cepat, berfungsi *offline*, dan merupakan *default* untuk tes unit dan integrasi. Alat ringan seperti Prism dapat menjalankannya langsung dari file OpenAPI:
prism mock openapi.yaml
# Mock server listening on http://127.0.0.1:4010
Server *mock* *cloud* memiliki URL publik. Gunakan ini ketika aplikasi seluler, *runner* CI, atau kolaborator eksternal perlu memanggil *mock* tanpa akses ke laptop Anda. Apidog memberikan setiap proyek URL *Cloud Mock* yang di-hosting, sehingga rekan tim di jaringan lain dapat mengakses *endpoint* yang sama dengan Anda.
Untuk menjalankan tes, lebih baik gunakan yang lokal. Ini tidak memiliki latensi jaringan dan tidak ada status bersama, sehingga dua *build* tidak akan pernah bertabrakan. Gunakan opsi *cloud* untuk demo dan pengujian *cross-device*.
Langkah 4: Arahkan tes Anda ke *mock*
Sekarang hubungkan kode tes ke *mock* alih-alih ke produksi. Pendekatan paling bersih adalah URL dasar yang dapat dikonfigurasi. Baca dari variabel lingkungan agar file tes yang sama berjalan terhadap *mock* secara lokal dan API asli dalam pekerjaan kontrak.
// orderClient.test.js
import { getOrder } from './orderClient.js';
const BASE_URL = process.env.API_BASE_URL || 'http://127.0.0.1:4010';
test('parses a shipped order', async () => {
const order = await getOrder('order_8842', BASE_URL);
expect(order.status).toBe('shipped');
expect(typeof order.total).toBe('number');
});
Klien mengambil URL dasar sebagai argumen; tidak ada dalam kode produksi yang tahu bahwa itu sedang di-mock. Di CI Anda mengatur API_BASE_URL ke alamat mock sebelum suite berjalan. Pola ini menjaga mocking sepenuhnya di luar logika aplikasi Anda, di mana ia seharusnya berada. Jika Anda menjalankan tes melalui pipeline, ide yang sama berlaku ketika Anda mengotomatiskan tes API di CI/CD.
Langkah 5: Uji jalur kesalahan
Ini adalah langkah yang paling sering dilewati oleh sebagian besar tim, dan ini adalah langkah yang paling menguntungkan. Server sungguhan jarang mengembalikan 500 saat Anda menginginkannya. Sebuah *mock* mengembalikan satu sesuai perintah.
Konfigurasi *mock* untuk menyajikan respons kegagalan tertentu, lalu pastikan klien Anda menangani setiap kasus:
| Skenario | *Mock* mengembalikan | Apa yang Anda pastikan |
|---|---|---|
| Catatan hilang | 404 |
Klien memunculkan kesalahan "tidak ditemukan" yang jelas |
| Kegagalan server | 500 |
Klien mencoba ulang, lalu menampilkan *fallback* |
| Terbatas *rate* | 429 dengan Retry-After |
Klien menunda dalam jumlah yang tepat |
| Respons lambat | 200 setelah penundaan 5 detik |
Klien *timeout* dan pulih |
| *Body* tidak valid | 200 dengan JSON rusak |
Klien gagal dengan anggun, tidak *crash* |
Aturan *mock* canggih Apidog memungkinkan Anda mengembalikan respons yang berbeda berdasarkan permintaan, sehingga permintaan untuk order_404 menghasilkan 404 sementara setiap ID lainnya mengembalikan 200 normal. Itu memberi Anda satu *endpoint* *mock* yang mencakup jalur sukses dan kegagalan. Gabungkan ini dengan pernyataan API yang kuat dan *suite* Anda memverifikasi perilaku, bukan hanya kode status.
Mengatur *mock* di seluruh *suite* pengujian yang berkembang
Satu *endpoint* *mock* itu mudah. Seratus *endpoint* *mock*, yang tersebar di seluruh *suite*, adalah saat tim kehilangan kendali. Beberapa kebiasaan menjaga agar set ini tetap dapat dikelola.
Kelompokkan *mock* berdasarkan layanan nyata yang mereka gantikan, bukan berdasarkan tes yang menggunakannya. Ketika API pembayaran berubah, Anda ingin satu tempat untuk memperbarui, bukan dua puluh file tes. Beri nama *fixture* *mock* sesuai skenario yang mereka representasikan, seperti order-shipped atau order-rate-limited, sehingga tes yang gagal terbaca dengan jelas. Simpan definisi *mock* dalam kontrol versi di samping tes, karena *mock* adalah bagian dari tes dan layak mendapatkan tinjauan yang sama.
Tahan keinginan untuk memberikan setiap tes *payload* khusus sendiri. Sebagian besar tes menginginkan objek pesanan realistis yang sama dengan satu bidang diubah. Definisikan respons dasar sekali dan timpa hanya bidang yang sedang diuji. Itu membuat *suite* dapat dibaca dan berarti perubahan kontrak menyentuh satu definisi dasar alih-alih salinan yang tersebar. Disiplin yang sama yang membuat suite tes untuk otomatisasi API dapat dipelihara berlaku langsung untuk *mock* di belakangnya.
Menjaga *mock* tetap jujur
Sebuah *mock* bisa melenceng. *Backend* menambahkan bidang, mengubah nama total menjadi amount, atau mengubah *enum*, dan *mock* Anda terus mengembalikan bentuk lama. Tes lulus; produksi gagal. Ini adalah cara *mocking* paling umum yang salah, dan itu terjadi secara diam-diam. Tidak ada dalam *suite* Anda yang mengeluh, karena *suite* mengukur *mock* terhadap dirinya sendiri.
Dua kebiasaan mencegah hal ini. Pertama, dapatkan *mock* dari skema yang sama yang diterbitkan *backend*, sehingga perubahan kontrak memperbarui keduanya sekaligus. *Mock* yang dihasilkan dari file OpenAPI akan dihasilkan ulang ketika file itu berubah; *mock* yang diketik secara manual tidak. Kedua, jalankan serangkaian kecil tes kontrak terhadap API asli sesuai jadwal. Tugas mereka hanya untuk mengonfirmasi bahwa respons langsung masih cocok dengan skema yang digunakan *mock* Anda. Ketika mereka gagal, Anda tahu *mock* sudah usang sebelum pengguna mengetahuinya.
Ini juga membantu meninjau *mock* selama tinjauan kode. Ketika *pull request* mengubah respons API, peninjau harus memeriksa bahwa *mock* yang sesuai juga berubah. Memperlakukan *mock* sebagai bagian dari kontrak, daripada pembantu tes sekali pakai, adalah yang menjaga *suite* yang di-mock dapat dipercaya selama berbulan-bulan perubahan.
Jika Anda menginginkan satu lingkungan yang menyimpan skema, menghasilkan *mock*, dan menjalankan tes terhadapnya, Unduh Apidog. Ini menjaga desain, server *mock*, dan *suite* tes tetap sinkron, sehingga *mock* yang Anda uji selalu merupakan kontrak saat ini. Untuk opsi yang lebih luas, bandingkan bidang dalam rangkuman alat *mocking* API REST ini.
Pertanyaan yang Sering Diajukan
Haruskah saya melakukan *mock* API untuk setiap tes?
Tidak. Lakukan *mock* untuk tes unit dan integrasi di mana Anda memeriksa kode Anda sendiri. Pertahankan serangkaian kecil tes kontrak dan *end-to-end* yang mengakses API asli, karena itu mengonfirmasi bahwa *mock* Anda masih cocok dengan produksi. *Mocking* segalanya menyembunyikan perbedaan kontrak.
Apa perbedaan antara respons *mock* statis dan dinamis?
Respons statis adalah *payload* JSON tetap yang tidak pernah berubah, yang bagus untuk pernyataan (*assertion*) yang dapat diprediksi. Respons dinamis dihasilkan per permintaan dengan nilai realistis, yang menangkap *bug* yang akan terlewatkan oleh satu *payload* tetap. Sebagian besar tim menggunakan keduanya.
Bagaimana saya memastikan *mock* saya tetap akurat?
Hasilkan *mock* dari skema yang sama yang digunakan *backend*, idealnya dokumen OpenAPI. Kemudian jalankan tes kontrak terjadwal terhadap API asli untuk mengonfirmasi bahwa respons langsung masih cocok dengan skema itu. Jika gagal, *mock* Anda perlu diperbarui.
Bisakah *mock* mensimulasikan respons lambat atau gagal?
Ya, dan ini adalah salah satu alasan terkuat untuk melakukan *mock*. Anda dapat mengonfigurasi *mock* untuk mengembalikan 500, 429 dengan *header* Retry-After, atau 200 yang tertunda. Itu memungkinkan Anda memverifikasi logika percobaan ulang dan *timeout* yang tidak akan pernah dipicu oleh server asli yang sehat sesuai permintaan.
Server *mock* lokal atau server *mock* *cloud* untuk pengujian?
Gunakan server *mock* lokal untuk menjalankan tes. Ini cepat, tidak memiliki latensi jaringan, dan menghindari status bersama antar *build*. Gunakan *mock* yang di-hosting di *cloud* ketika perangkat seluler, *runner* CI, atau kolaborator eksternal perlu mencapai *mock* tanpa akses ke mesin Anda.
