Cara Mock API dan Ujian Automasi di VPS Sendiri
Ketahui cara mengendalikan WireMock untuk stub API dan Hurl untuk ujian integrasi pada pelayan VPS anda. Dapatkan panduan lengkap menguruskan data ujian yang kekal.
Dua tugasan yang berkongsi satu repositori
Mocking API dan pengujian yang dihoskan sendiri adalah dua tugasan yang berbeza, dan menganggapnya sebagai satu tugasan akan membazirkan masa selama seminggu. Pelayan mock berfungsi sebagai pengganti kepada dependency yang tidak boleh anda panggil daripada CI: pembekal pembayaran, API rakan kongsi, upstream yang dihadkan kadarnya (rate limited), atau servis yang belum dikeluarkan oleh pasukan lain. Pelayan ujian API memanggil endpoint anda sendiri dalam urutan yang tetap dan membuat pengesahan (assert) terhadap respons, dengan membawa nilai daripada satu respons ke permintaan seterusnya.
Kedua-duanya tidak bertindih. Pelayan mock tidak pernah melaporkan status lulus atau gagal. Pelayan ujian tidak mempunyai pendapat tentang apa yang dikembalikan oleh pembekal pembayaran apabila kad ditolak. Kebanyakan pasukan yang sudah menyewa pelayan akhirnya menjalankan satu daripada setiap jenis, dimulakan oleh fail Docker Compose yang sama dan disemak dalam pull request yang sama.
Mengapa perlu melakukan self-host untuk mocking dan pengujian API?
Fixtur anda merupakan data yang dibentuk mengikut data pengeluaran (production). Badan permintaan dalam ujian API adalah rekod pelanggan sebenar dengan nama yang telah ditukar, atau nama yang tidak ditukar kerana tiada semakan dilakukan. Stub yang dirakam adalah lebih buruk: rakaman proksi menyimpan apa sahaja yang dikembalikan oleh upstream, jadi direktori stub yang dibina melalui rakaman mengandungi token langsung dan alamat e-mel pelanggan sehingga seseorang membaca setiap fail tersebut. Pada perkhidmatan yang dihoskan, data tersebut menjadi insiden pihak lain dan pendedahan maklumat anda.
Sebab kedua ialah kebolehcapaian. Servis yang terikat pada alamat peribadi tidak boleh dicapai daripada runner yang dihoskan, jadi ujian tidak dapat dijalankan sama sekali. Setiap penyelesaian sementara mempunyai kosnya tersendiri. Menerbitkan API ke internet untuk tujuan ujian menghilangkan sebab mengapa ia bersifat peribadi pada asalnya. Terowong atau salinan staging awam merupakan sistem tambahan yang perlu diselenggara, dan salinan staging akan terpesong daripada versi pengeluaran antara setiap keluaran (release). Runner yang berada pada rangkaian peribadi yang sama memanggil servis secara terus dan tidak memerlukan semua itu, yang merupakan hujah praktikal di sebalik a self-hosted GitHub Actions runner.
Pelayan olok-olok (mock server) layan diri (self-hosted) yang manakah patut anda jalankan?
Setiap satu daripada ini berjalan sebagai kontena pada mesin milik anda. Persoalan yang penting ialah apakah yang dianggap sebagai sumber kebenaran (source of truth) oleh setiap satu, kerana ini menentukan sama ada membina semula kontena tersebut tidak menelan kos atau memakan masa sepanjang petang.
- WireMock menyimpan setiap stub sebagai fail JSON dalam direktori
mappings/, dengan badan respons yang besar dalam__files/. Imejnya ialahwiremock/wiremock, direktori root di dalam kontena ialah/home/wiremock, dan ia juga berjalan sebagai proksi rakaman. Fail pada cakera bermakna mock tersebut kekal dalam git seperti kod lain. - Mockoon CLI menyimpan keseluruhan API mock dalam satu fail data JSON. Pasang ia dengan
npm install -g @mockoon/clidan mulakan denganmockoon-cli start --data ./data-file.json, atau jalankan imejmockoon/clidengan fail tersebut dipasang (bind mounted). Aplikasi desktop menyunting fail yang sama, jadi mereka bentuk dalam UI dan melakukan commit pada hasilnya kekal serasi. - MockServer berjalan daripada imej
mockserver/mockserverdan mendengar pada port 1080. Jangkaan (expectations) tiba melalui API REST miliknya sendiri, yang berguna daripada kod ujian tetapi berisiko sebagai penempatan (deployment): jangkaan yang dicipta oleh panggilan HTTP akan hilang apabila kontena dimulakan semula. Gunakan fail permulaan JSON miliknya untuk stub yang bertujuan untuk menjadi kekal. - Prism membina mock daripada dokumen OpenAPI anda dan bukannya daripada fail stub yang berasingan. Pasang ia dengan
npm install -g @stoplight/prism-cli, kemudian jalankanprism mock openapi.yaml. Di dalam kontena, tambahkan-h 0.0.0.0, kerana Prism terikat pada localhost secara lalai dan jika tidak, ia tidak boleh dicapai dari luar kontena. - Microcks ialah pilihan yang besar: UI web yang mengimport dokumen OpenAPI dan koleksi Postman, kemudian menyajikannya sebagai mock dan menjalankan ujian kontrak. Pemasangan penuh memerlukan MongoDB dan Keycloak, serta Kafka untuk ciri asinkronusnya. Imej
microcks-ubersemua-dalam-satu menggabungkan MongoDB dalam memori, yang didokumenkan oleh projek tersebut sebagai sesuai untuk kegunaan sementara, jadi anggap apa sahaja yang dicipta dalam UI itu sebagai boleh dibuang dan simpan artifak sumber dalam git.
Penyelesai ujian API self-hosted yang manakah patut anda jalankan?
Tugas di sini ialah satu urutan: mengesahkan identiti, mencipta pesanan, membacanya semula, dan memastikan status telah berubah. Ini memerlukan nilai yang diambil daripada satu respons untuk digunakan dalam permintaan seterusnya. Alat yang tidak boleh membawa status antara panggilan hanyalah pemeriksaan kesihatan (health check), bukan ujian API.
- Hurl menjalankan fail teks biasa permintaan HTTP daripada satu binari tunggal. Bahagian
[Captures]mengambil nilai daripada respons, bahagian[Asserts]menyemaknya, dan--testmenjadikannya penyelesai ujian dengan ringkasan serta kod keluar (exit code). Versi 8.0.1 adalah versi semasa setakat Ogos 2026. - Bruno CLI menjalankan folder fail
.bru. Pasang dengannpm install -g @usebruno/cli, kemudian jalankanbru run folder --env Local --reporter-junit results.xml. Format koleksi ini direka sebagai fail teks dalam direktori, jadi perbezaan (diffs) mudah dibaca semasa semakan. - Newman menjalankan koleksi Postman di luar Postman:
npm install -g newman, kemudiannewman run collection.json -r cli,junit --reporter-junit-export results.xml. Kekurangannya ialah formatnya. Koleksi tersebut adalah satu gumpalan JSON yang dieksport, jadi penyuntingan dilakukan dalam Postman dan fail dalam git hanyalah salinan yang boleh menjadi lapuk. - Schemathesis ialah jenis pemeriksaan yang berbeza. Ia membaca skema OpenAPI dan menjana kes yang cuba menghasilkan respons yang didakwa mustahil oleh skema anda:
uvx schemathesis run https://your.api/openapi.json. Ia mencari kegagalan (crashes) dan pelanggaran kontrak, dan ia tidak mengetahui tentang peraturan perniagaan anda, jadi ia berfungsi di samping suite skrip dan bukannya menggantikannya. - Hoppscotch self-hosted ialah pilihan UI web, dan ia memerlukan instans Postgres. Fahami pertukaran (tradeoff) tersebut sebelum anda memasangnya: koleksi disimpan dalam pangkalan data, bukan dalam repositori anda.
Satu yang perlu dielakkan. Step CI masih muncul dalam senarai alat dan format aliran kerja YAML-nya mudah dibaca, tetapi repositori tersebut menerima komit terakhir pada Ogos 2024. Program yang berada di antara CI dan API anda adalah tempat yang tidak sesuai untuk kod yang tidak diselenggara.
Letakkan pelayan olok-olok di sebalik firewall
Persediaan di bawah menjalankan WireMock sebagai pengganti untuk penyedia pembayaran. Jika format fail compose baharu bagi anda, Docker Compose pada VPS merangkumi arahan kitaran hayat yang diandaikan dalam bahagian ini.
services:
mock-payments:
image: wiremock/wiremock:3.13.2
command: ["--verbose"]
volumes:
- ./mocks/payments:/home/wiremock
ports:
- "127.0.0.1:8080:8080"
restart: unless-stoppedAwalan 127.0.0.1: pada port adalah bahagian yang penting. 8080:8080 sahaja akan menerbitkan pelayan olok-olok pada setiap antara muka termasuk IP awam anda, dan ia kekal boleh dicapai walaupun ufw menafikan port tersebut, kerana Docker menulis peraturannya sendiri ke dalam rantaian iptables DOCKER dan peraturan tersebut dinilai sebelum peraturan INPUT ufw. Ikat (bind) kepada alamat loopback sebaliknya, atau kepada alamat antara muka peribadi, dan kernel tidak akan menerima sambungan dari luar.
Servis anda yang sedang diuji kemudiannya menghala ke pelayan olok-olok tersebut. Apabila servis dijalankan dalam projek compose yang sama, URL asas pelayan olok-olok ialah http://mock-payments:8080, kerana compose menyelesaikan nama servis pada rangkaiannya sendiri. Apabila servis dijalankan pada hos, ia adalah http://127.0.0.1:8080. Tetapkan perkara itu melalui pemboleh ubah persekitaran, jangan sekali-kali dalam kod, atau URL ujian tersebut akan dihantar ke pengeluaran (production).
Stub diletakkan dalam ./mocks/payments/mappings/, satu fail JSON setiap satu.
{
"request": {
"method": "POST",
"urlPath": "/v1/charges",
"bodyPatterns": [{ "matchesJsonPath": "$.amount" }]
},
"response": {
"status": 201,
"headers": { "Content-Type": "application/json" },
"jsonBody": { "id": "ch_test_001", "status": "succeeded", "amount": 4200 }
}
}Mulakannya, kemudian semak apa yang sebenarnya dimuatkan.
docker compose up -d --wait mock-payments
curl -fsS http://127.0.0.1:8080/__admin/mappings--wait menyekat sehingga kontena melaporkan status sihat, yang berfungsi kerana imej WireMock menghantar HEALTHCHECK terhadap endpoint /__admin/health miliknya. Panggilan mappings menyenaraikan setiap stub yang dibaca oleh pelayan. Stub yang anda tulis tetapi tiada dalam senarai itu tidak pernah dimuatkan: semak sama ada fail tersebut berada di bawah mappings/ dan bukannya di root yang dipasang (mounted), dan semak sama ada JSON tersebut boleh dihuraikan (parse).
Apabila permintaan tiba dan tiada stub yang sepadan, WireMock menjawab 404 dengan badan yang bermula dengan Request was not matched, diikuti dengan perbezaan (diff) berbanding stub terdekat yang dimilikinya. Baca perbezaan tersebut sebelum mengubah apa-apa, kerana ia menamakan medan tepat yang berbeza. Ia biasanya merupakan laluan dengan /v1/charge di mana stub menyatakan /v1/charges.
Tulis ujian sebagai urutan dengan keadaan yang dibawa antara panggilan
Fail Hurl adalah teks biasa. Pasang fail deb daripada keluaran (releases) projek tersebut.
VERSION=8.0.1
curl --location --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/$VERSION/hurl_${VERSION}_amd64.deb
sudo apt update && sudo apt install ./hurl_${VERSION}_amd64.debSatu suite yang menguji API anda sendiri terhadap mock tersebut terletak di tests/checkout.hurl.
POST {{base_url}}/orders
Content-Type: application/json
{
"sku": "ssd-1tb",
"amount": 4200
}
HTTP 201
[Captures]
order_id: jsonpath "$['id']"
GET {{base_url}}/orders/{{order_id}}
HTTP 200
[Asserts]
jsonpath "$.status" == "paid"
jsonpath "$.charge_id" == "ch_test_001"Blok [Captures] adalah perkara yang menjadikan ini satu ujian API dan bukannya dua permintaan yang tidak berkaitan. order_id dibaca daripada respons pertama dan disisipkan ke dalam URL permintaan kedua. Pernyataan (assertion) pada charge_id adalah tujuan keseluruhan latihan ini: ia membuktikan perkhidmatan anda telah memanggil pembekal pembayaran dan menyimpan maklumat yang diterima, dan nilai yang dibandingkan adalah nilai yang anda tulis ke dalam stub WireMock. Satu fail kini merangkumi kedua-dua bahagian aliran tersebut.
hurl --test --variable base_url=http://127.0.0.1:3000 \
--report-junit reports/junit.xml \
--report-json reports/json \
tests/Larian yang berjaya akan mencetak satu baris bagi setiap fail beserta ringkasan.
tests/checkout.hurl: Success (2 request(s) in 61 ms)
Executed files: 1
Executed requests: 2 (30.1/s)
Succeeded files: 1 (100.0%)
Failed files: 0 (0.0%)
Duration: 64 msKegagalan akan mencetak error: Assert failure bersama fail dan nombor baris, kemudian nilai yang diterima berbanding nilai yang dijangkakan, dan hurl akan keluar dengan kod bukan sifar supaya CI berhenti. Jika status membaca pending di mana anda menjangkakan paid, perkhidmatan anda tidak memproses respons daripada mock tersebut. Perkara seterusnya yang perlu dibaca ialah jurnal permintaan WireMock di /__admin/requests, yang menunjukkan sama ada panggilan tersebut sampai ke mock atau tidak.
Cetuskan suite daripada runner CI anda sendiri
Dengan runner yang didaftarkan pada mesin yang sama, aliran kerja menjadi singkat. Runner tersebut merupakan proses biasa pada hos, jadi docker dan hurl mesti dipasang pada hos tersebut. Tiada apa-apa yang diwarisi daripada imej yang dihoskan.
name: api-tests
on: [push]
jobs:
hurl:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Start the mock
run: docker compose up -d --wait mock-payments
- name: Run the suite
run: hurl --test --variable base_url=http://127.0.0.1:3000 --report-junit reports/junit.xml tests/
- name: Archive the reports
if: always()
run: install -d /srv/api-tests/reports/$GITHUB_SHA && cp -r reports/. /srv/api-tests/reports/$GITHUB_SHA/
- name: Stop the mock
if: always()
run: docker compose downif: always() pada langkah arkib adalah penting. Tanpanya, ujian yang gagal akan melangkau proses penyalinan, menyebabkan anda kehilangan laporan yang ingin dibaca. Salinan tersebut juga perlu diletakkan di luar ruang kerja, kerana runner akan membersihkan ruang kerja sebelum tugasan seterusnya dan laporan tersebut akan turut terpadam.
Simpan hasil, bukan sekadar larian terakhir
Fail JUnit XML bagi setiap commit menjawab satu soalan: adakah ia lulus. Ia tidak menjawab bila sesuatu endpoint mula menjadi perlahan, kerana tiada apa yang membaca fail tersebut sebaik sahaja anda berhenti membukanya. Untuk melihat trend, tambahkan satu baris bagi setiap larian ke dalam pangkalan data kecil pada mesin yang sama. Satu jadual yang menyimpan commit SHA, nama fail, bilangan lulus, bilangan gagal dan tempoh masa sudah memadai, dan SQLite dalam pengeluaran pada VPS merupakan tempat yang munasabah untuk menyimpannya: satu fail, tiada proses pelayan, dan keseluruhan sejarah akan disertakan dalam sandaran yang anda ambil. Parse output --report-json daripada Hurl dan bukannya JUnit XML, kerana ia merupakan format yang lebih sesuai untuk dibaca oleh mesin antara kedua-duanya.
Perkara yang perlu dikekalkan selepas pembinaan semula kontena
Definisi olok-olok (mock) dan suite ujian adalah kod sumber. Ia perlu berada dalam repositori di sebelah servis yang diterangkannya, dan diubah dalam pull request yang sama dengan perubahan pada endpoint. Stub yang disunting melalui UI web, atau jangkaan (expectation) yang dihantar ke MockServer melalui API REST semasa runtime, hanya wujud dalam memori kontena atau pangkalan data alat tersebut. Jalankan docker compose down dan ia akan hilang, dan tiada siapa yang akan menyedarinya sehingga ujian mula lulus atas sebab yang salah. Jika repositori anda juga berjalan pada perkakasan sendiri, pelayan git yang dihoskan sendiri memastikan fixture dan servis berada dalam satu sempadan kepercayaan (trust boundary).
Seterusnya, peraturan praktikal. Tetapkan tag imej (pin image tags), kerana latest boleh mengubah cara mock anda memadankan permintaan tanpa sebarang perubahan dalam repositori anda, dan kegagalan tersebut sangat sukar dikesan puncanya. Lekapkan direktori stub sebagai baca sahaja (read only) apabila alat tersebut tidak perlu menulis kepadanya. Jangan sekali-kali meletakkan stub mock dalam Docker volume bernama, kerana volume tersebut akan menjadi sumber kebenaran (source of truth) dan salinan dalam git akan menjadi salah secara senyap.
Satu lagi perkara, dan ini sering memerangkap pengguna. Jika anda membina stub dengan merakam trafik sebenar melalui proksi, baca setiap fail yang dijana sebelum anda melakukan commit. Rakaman mengandungi apa yang dihantar balik oleh upstream, termasuk bearer token dan alamat e-mel pelanggan. Melakukan commit terhadapnya akan meletakkan data tersebut dalam repositori anda secara kekal, kerana git menyimpan kandungan yang dipadam dalam sejarahnya.
FAQ
Apakah perbezaan antara pelayan mock API dan pelari ujian API?
Pelayan mock menjawab permintaan. Ia menggantikan dependensi yang tidak boleh anda panggil daripada CI, dan ia tidak pernah melaporkan lulus atau gagal. Pelari ujian API menghantar permintaan kepada servis anda sendiri, membuat asersi terhadap respons, membawa nilai daripada satu panggilan ke panggilan seterusnya, dan keluar dengan kod bukan sifar apabila asersi gagal. Ia menyelesaikan masalah yang berbeza, dan persediaan biasa menjalankan kedua-duanya serentak: pelari memanggil servis anda sementara servis anda memanggil mock.
Bolehkah saya menguji API dalaman daripada pelari CI yang dihoskan?
Tidak boleh tanpa mendedahkannya. Pelari yang dihoskan berada di luar rangkaian anda, jadi ia tidak boleh mencapai servis yang terikat pada alamat peribadi. Pilihan anda ialah menerbitkan API tersebut, menjalankan tunnel, atau menyelenggara salinan staging awam, dan setiap satunya menambah sistem yang boleh gagal atau bocor. Pelari pada rangkaian peribadi yang sama memanggil servis secara terus, yang merupakan sebab praktikal utama pasukan melakukan self-host untuk kerja ini.
Di manakah stub mock dan suite ujian API harus disimpan?
Dalam git, bersebelahan dengan servis yang diterangkannya. Alat yang menyimpan definisi sebagai fail, seperti direktori mappings/ WireMock, fail data Mockoon, fail Hurl dan folder .bru Bruno, memberikan anda semakan kod dan pembinaan semula kontena yang tidak menelan kos. Alat yang menyimpan definisi dalam pangkalan data atau UI web memerlukan pelan sandaran dan langkah eksport, dan eksport adalah bahagian yang dilupakan orang sehingga kontena sudah tiada.
Mengapa mock saya mengembalikan 404 sedangkan stub kelihatan betul?
WireMock menyajikan stub hanya pada padanan yang tepat. Permintaan yang tidak sepadan mendapat 404 dengan badan yang bermula dengan Request was not matched, diikuti oleh perbezaan (diff) berbanding stub yang paling hampir, dan perbezaan itu menamakan medan yang berbeza. Punca biasa ialah garis miring (trailing slash) pada path, header Content-Type yang diperlukan oleh stub tetapi tidak dihantar oleh klien anda, urlPath yang digunakan di mana stub memerlukan urlPathPattern untuk segmen pemboleh ubah, dan pemadan badan (body matcher) yang tidak sesuai dengan payload. Semak /__admin/requests dahulu untuk mengesahkan sama ada permintaan itu sampai ke mock atau tidak.
Adakah saya masih memerlukan mock jika saya mempunyai persekitaran staging?
Ya, atas dua sebab. Salinan staging bagi upstream yang tidak anda kawal masih boleh tergendala dan masih mengenakan had kadar (rate limit) kepada anda, jadi suite anda gagal atas sebab yang tiada kaitan dengan kod anda. Ia juga tidak boleh menghasilkan respons yang paling anda perlukan untuk diuji, seperti kad yang ditolak atau tamat masa gateway. Mock mengembalikan perkara tersebut atas permintaan pada kelajuan rangkaian tempatan, yang menukarkan suite yang mengambil masa beberapa minit terhadap sandbox kepada beberapa saat sahaja. Kekalkan staging untuk semakan akhir sebelum release dan gunakan mock dalam CI.