วิธีทำ API Mocking และทดสอบบน VPS ด้วยตัวเอง
เรียนรู้วิธีติดตั้ง WireMock และ Hurl บน VPS เพื่อจัดการ API mocking และการทดสอบอย่างเป็นระบบ ช่วยให้คุณควบคุมข้อมูลจำลองและผลลัพธ์การทดสอบได้แม่นยำโดยไม่ต้องพึ่งพาบริการภายนอก
สองงานที่ใช้ repository ร่วมกัน
การทำ API mocking และการทดสอบแบบ self-hosted เป็นงานสองประเภทที่แตกต่างกัน การรวมงานทั้งสองเข้าด้วยกันทำให้เสียเวลาโดยเปล่าประโยชน์ Mock server ทำหน้าที่แทน dependency ที่คุณไม่สามารถเรียกใช้งานจาก CI ได้ เช่น ผู้ให้บริการชำระเงิน, API ของพาร์ทเนอร์, upstream ที่มีการจำกัดอัตราการเรียกใช้ (rate limit) หรือบริการที่ทีมอื่นยังไม่ได้ปล่อยออกมา (ship) ส่วน API test runner จะเรียก endpoint ของคุณเองตามลำดับที่กำหนดไว้และตรวจสอบผลลัพธ์ (assert) โดยนำค่าจาก response หนึ่งไปใช้ใน request ถัดไป
งานทั้งสองส่วนนี้ไม่มีส่วนทับซ้อนกัน Mock server ไม่เคยรายงานผลว่าผ่านหรือล้มเหลว ส่วน test runner ก็ไม่มีความเห็นว่าผู้ให้บริการชำระเงินควรตอบกลับอย่างไรเมื่อบัตรถูกปฏิเสธ ทีมส่วนใหญ่ที่เช่าเซิร์ฟเวอร์อยู่แล้วมักจะรันงานทั้งสองอย่างควบคู่กันไป โดยเริ่มทำงานผ่าน Docker Compose file ชุดเดียวกันและผ่านการรีวิวใน pull request เดียวกัน
เหตุใดจึงควร self-host ระบบจำลอง API และการทดสอบ
ข้อมูลจำลอง (fixtures) ของคุณมีรูปแบบเหมือนข้อมูลใน production ร่างคำขอ (request body) ในการทดสอบ API คือข้อมูลลูกค้าจริงที่ถูกเปลี่ยนชื่อ หรืออาจไม่ได้เปลี่ยนชื่อเพราะไม่มีใครตรวจสอบ ส่วนการบันทึก stub นั้นแย่กว่า เพราะการบันทึกผ่าน proxy จะเก็บทุกอย่างที่ upstream ส่งกลับมา ดังนั้นไดเรกทอรี stub ที่สร้างจากการบันทึกจึงมีทั้ง token จริงและอีเมลลูกค้าจนกว่าจะมีคนเข้าไปอ่านไฟล์เหล่านั้นทั้งหมด หากใช้บริการที่โฮสต์บนคลาวด์ ข้อมูลดังกล่าวอาจกลายเป็นการรั่วไหลของข้อมูลและเป็นเหตุการณ์ที่ต้องรายงานต่อสาธารณะ
เหตุผลประการที่สองคือความสามารถในการเข้าถึง บริการที่ผูกอยู่กับ private address จะไม่สามารถเข้าถึงได้จาก hosted runner ทำให้การทดสอบไม่สามารถทำงานได้ ทุกวิธีแก้ปัญหาล้วนมีต้นทุน การเปิดเผย API สู่สาธารณะเพื่อทำการทดสอบเป็นการทำลายเหตุผลที่ต้องตั้งค่าให้เป็น private ตั้งแต่แรก การใช้ tunnel หรือการทำ staging copy แบบสาธารณะเป็นอีกระบบที่ต้องดูแลรักษา และ staging copy มักจะมีความแตกต่างจาก production เมื่อมีการปล่อย release ใหม่ๆ การใช้ runner บน private network เดียวกันจะช่วยให้เรียกใช้บริการได้โดยตรงโดยไม่ต้องพึ่งพาระบบเหล่านั้น ซึ่งเป็นเหตุผลเชิงปฏิบัติเบื้องหลัง การใช้ self-hosted GitHub Actions runner
คุณควรใช้งาน self-hosted mock server ตัวใด?
แต่ละตัวทำงานเป็น container บนเซิร์ฟเวอร์ที่คุณดูแลเอง คำถามสำคัญคือแต่ละตัวใช้สิ่งใดเป็นแหล่งข้อมูลหลัก (source of truth) เพราะนั่นเป็นตัวตัดสินว่าการสร้าง container ใหม่จะใช้เวลาเพียงครู่เดียวหรือต้องเสียเวลาทั้งบ่าย
- WireMock เก็บ stub ทุกตัวไว้เป็นไฟล์ JSON ในไดเรกทอรี
mappings/โดยเก็บ response body ขนาดใหญ่ไว้ใน__files/อิมเมจคือwiremock/wiremockและมี root directory ภายใน container คือ/home/wiremockนอกจากนี้ยังสามารถทำงานเป็น recording proxy ได้ การเก็บไฟล์ไว้บนดิสก์หมายความว่า mock เหล่านี้สามารถเก็บไว้ใน git ได้เหมือนกับโค้ดส่วนอื่น - Mockoon CLI เก็บ API mock ทั้งหมดไว้ในไฟล์ข้อมูล JSON เพียงไฟล์เดียว ติดตั้งด้วย
npm install -g @mockoon/cliและเริ่มการทำงานด้วยmockoon-cli start --data ./data-file.jsonหรือรันอิมเมจmockoon/cliโดยทำ bind mount ไฟล์ดังกล่าวเข้าไป แอปพลิเคชันบนเดสก์ท็อปจะแก้ไขไฟล์เดียวกันนี้ ดังนั้นการออกแบบผ่าน UI และการ commit ผลลัพธ์จึงใช้งานร่วมกันได้ - MockServer ทำงานจากอิมเมจ
mockserver/mockserverและฟังคำขอที่พอร์ต 1080 การตั้งค่า expectations จะทำผ่าน REST API ของตัวมันเอง ซึ่งสะดวกต่อการเขียนโค้ดทดสอบ แต่มีความเสี่ยงในการใช้งานจริง เนื่องจาก expectation ที่สร้างผ่าน HTTP call จะหายไปเมื่อ container รีสตาร์ท ควรใช้ไฟล์ JSON initialization สำหรับ stub ที่ต้องการให้คงอยู่ถาวร - Prism สร้าง mock จากเอกสาร OpenAPI ของคุณโดยตรงแทนการใช้ไฟล์ stub แยกต่างหาก ติดตั้งด้วย
npm install -g @stoplight/prism-cliจากนั้นรันprism mock openapi.yamlภายใน container ให้เพิ่มแฟล็ก-h 0.0.0.0เนื่องจากโดยปกติ Prism จะ bind เข้ากับ localhost ทำให้ไม่สามารถเข้าถึงจากภายนอก container ได้ - Microcks เป็นตัวเลือกขนาดใหญ่: มีเว็บ UI สำหรับนำเข้าเอกสาร OpenAPI และ Postman collections จากนั้นจะทำหน้าที่เป็น mock และรัน contract tests การติดตั้งแบบเต็มรูปแบบต้องใช้ MongoDB และ Keycloak รวมถึง Kafka สำหรับฟีเจอร์แบบ async อิมเมจแบบรวมทุกอย่าง
microcks-uberจะมาพร้อมกับ in-memory MongoDB ซึ่งเอกสารของโครงการระบุว่าเหมาะสำหรับการใช้งานชั่วคราว ดังนั้นให้ถือว่าข้อมูลใดก็ตามที่สร้างใน UI นั้นเป็นข้อมูลที่ลบทิ้งได้ และควรเก็บ source artifacts ไว้ใน git แทน
คุณควรเลือกใช้ API test runner แบบ self-hosted ตัวใด?
งานในส่วนนี้คือลำดับขั้นตอน: การยืนยันตัวตน, การสร้างคำสั่งซื้อ, การอ่านข้อมูลกลับ และการตรวจสอบว่าสถานะเปลี่ยนไปจริงหรือไม่ กระบวนการนี้จำเป็นต้องมีการดึงค่าจาก response หนึ่งเพื่อนำไปใช้ใน request ถัดไป เครื่องมือที่ไม่สามารถส่งผ่านสถานะระหว่างการเรียกใช้งานได้ถือเป็นเพียงเครื่องมือตรวจสอบสถานะ (health check) ไม่ใช่ API test
- Hurl รันไฟล์ข้อความธรรมดาที่บรรจุ HTTP requests จาก binary ไฟล์เดียว ส่วน
[Captures]ใช้สำหรับดึงค่าจาก response, ส่วน[Asserts]ใช้สำหรับตรวจสอบค่าเหล่านั้น และ--testเปลี่ยนให้มันกลายเป็น test runner ที่มีสรุปผลและ exit code เวอร์ชัน 8.0.1 เป็นเวอร์ชันปัจจุบัน ณ เดือนสิงหาคม 2026 - Bruno CLI รันโฟลเดอร์ที่บรรจุไฟล์
.bruติดตั้งด้วยnpm install -g @usebruno/cliจากนั้นรันด้วยbru run folder --env Local --reporter-junit results.xmlรูปแบบของ collection ถูกออกแบบมาให้เป็นไฟล์ข้อความในไดเรกทอรี ทำให้สามารถอ่านค่า diff ในการรีวิวได้ง่าย - Newman รัน Postman collections นอกโปรแกรม Postman: ใช้
npm install -g newmanจากนั้นรันnewman run collection.json -r cli,junit --reporter-junit-export results.xmlข้อควรระวังคือรูปแบบไฟล์ โดย collection จะเป็น JSON blob ที่ export ออกมาเพียงไฟล์เดียว ดังนั้นการแก้ไขต้องทำใน Postman และไฟล์ที่อยู่ใน git จะเป็นเพียงสำเนาที่อาจล้าสมัยได้ - Schemathesis เป็นการตรวจสอบอีกรูปแบบหนึ่ง โดยจะอ่าน OpenAPI schema และสร้างกรณีทดสอบเพื่อพยายามสร้าง response ที่ schema ของคุณระบุว่าเป็นไปไม่ได้:
uvx schemathesis run https://your.api/openapi.jsonเครื่องมือนี้ช่วยค้นหาการทำงานที่ผิดพลาด (crash) และการละเมิดสัญญา (contract violation) แต่เนื่องจากมันไม่ทราบกฎทางธุรกิจของคุณ จึงควรใช้ควบคู่ไปกับชุดทดสอบแบบสคริปต์แทนที่จะใช้ทดแทนกัน - Hoppscotch แบบ self-hosted เป็นตัวเลือกที่เป็น web UI และจำเป็นต้องใช้ Postgres instance โปรดทำความเข้าใจข้อแลกเปลี่ยนนี้ก่อนติดตั้ง: collection จะถูกเก็บไว้ในฐานข้อมูล ไม่ใช่ใน repository ของคุณ
สิ่งที่ควรหลีกเลี่ยง: Step CI ยังคงปรากฏอยู่ในบทความรวบรวมเครื่องมือและรูปแบบ workflow แบบ YAML ของมันก็อ่านง่าย แต่ repository ดังกล่าวมีการ commit ครั้งล่าสุดในเดือนสิงหาคม 2024 โปรแกรมที่ทำหน้าที่เป็นตัวกลางระหว่าง CI และ API ของคุณไม่ควรเป็นซอฟต์แวร์ที่ไม่มีการดูแลรักษา
วาง mock server ไว้หลัง firewall
การตั้งค่าด้านล่างนี้ใช้ WireMock เพื่อจำลองผู้ให้บริการชำระเงิน หากรูปแบบไฟล์ compose เป็นเรื่องใหม่สำหรับคุณ Docker Compose บน VPS ได้อธิบายคำสั่งจัดการ lifecycle ที่ส่วนนี้อ้างถึงไว้แล้ว
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-stoppedส่วนนำหน้า 127.0.0.1: บนพอร์ตเป็นส่วนที่สำคัญ การใช้เพียง 8080:8080 จะเผยแพร่ mock บนทุกอินเทอร์เฟซรวมถึง IP สาธารณะของคุณ ซึ่งจะยังคงเข้าถึงได้แม้ว่า ufw จะปฏิเสธพอร์ตนั้นอยู่ เพราะ Docker เขียนกฎของตัวเองลงใน chain DOCKER ของ iptables และกฎเหล่านั้นจะถูกประเมินก่อนกฎ INPUT ของ ufw ให้ bind เข้ากับ loopback address หรือที่อยู่ของ private interface แทน เพื่อให้ kernel ไม่ยอมรับการเชื่อมต่อจากภายนอก
จากนั้นบริการที่คุณกำลังทดสอบจะชี้ไปยัง mock เมื่อบริการทำงานใน compose project เดียวกัน URL พื้นฐานของ mock คือ http://mock-payments:8080 เนื่องจาก compose จะ resolve ชื่อบริการบนเครือข่ายของตัวเอง เมื่อบริการทำงานบน host จะเป็น http://127.0.0.1:8080 ให้ตั้งค่าผ่าน environment variable เท่านั้น ห้ามตั้งค่าในโค้ด มิฉะนั้น URL สำหรับทดสอบจะติดไปกับซอฟต์แวร์ที่นำไปใช้งานจริง (production)
ไฟล์ stub จะอยู่ใน ./mocks/payments/mappings/ โดยแยกเป็นไฟล์ JSON ไฟล์ละหนึ่งรายการ
{
"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 }
}
}เริ่มการทำงาน จากนั้นตรวจสอบสิ่งที่โหลดขึ้นมาจริง
docker compose up -d --wait mock-payments
curl -fsS http://127.0.0.1:8080/__admin/mappings--wait จะรอจนกว่า container จะรายงานสถานะว่า healthy ซึ่งทำงานได้เพราะ image ของ WireMock มี HEALTHCHECK ที่ตรวจสอบกับ endpoint /__admin/health ของมัน การเรียก mappings จะแสดงรายการ stub ทั้งหมดที่เซิร์ฟเวอร์อ่าน หาก stub ที่คุณเขียนไม่อยู่ในรายการนั้น แสดงว่ามันไม่เคยถูกโหลด ให้ตรวจสอบว่าไฟล์วางอยู่ใน mappings/ ไม่ใช่ที่ root ของ mount และตรวจสอบว่าไฟล์ JSON นั้นถูกต้อง
เมื่อมีคำขอเข้ามาและไม่มี stub ใดตรงกัน WireMock จะตอบกลับด้วย 404 พร้อมเนื้อหาที่ขึ้นต้นด้วย Request was not matched ตามด้วยส่วนต่าง (diff) เทียบกับ stub ที่ใกล้เคียงที่สุดที่มันมี ให้อ่านส่วนต่างนั้นก่อนที่จะแก้ไขสิ่งใด เพราะมันจะระบุฟิลด์ที่แตกต่างกันอย่างชัดเจน โดยปกติจะเป็น path ที่มี /v1/charge ในขณะที่ stub ระบุเป็น /v1/charges
เขียนการทดสอบเป็นลำดับขั้นตอนโดยส่งผ่านสถานะระหว่างการเรียกใช้งาน
ไฟล์ Hurl เป็นข้อความธรรมดา ให้ติดตั้งไฟล์ deb จากหน้า releases ของโปรเจกต์
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.debชุดทดสอบที่ใช้ตรวจสอบ API ของคุณเองเทียบกับ mock จะอยู่ใน 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"บล็อก [Captures] คือสิ่งที่ทำให้การทดสอบนี้เป็นการทดสอบ API แทนที่จะเป็นเพียงการส่งคำขอสองรายการที่ไม่เกี่ยวข้องกัน โดย order_id จะถูกอ่านจาก response แรกและนำไปแทรกใน URL ของคำขอที่สอง การตรวจสอบที่ charge_id คือจุดประสงค์หลักของการทดสอบนี้ ซึ่งเป็นการพิสูจน์ว่าบริการของคุณได้เรียกใช้งานผู้ให้บริการชำระเงินและจัดเก็บข้อมูลที่ได้รับกลับมาจริง โดยค่าที่นำมาเปรียบเทียบคือค่าที่คุณเขียนไว้ใน WireMock stub ไฟล์เดียวจึงครอบคลุมการทำงานทั้งสองส่วนของกระบวนการ
hurl --test --variable base_url=http://127.0.0.1:3000 \
--report-junit reports/junit.xml \
--report-json reports/json \
tests/การรันที่ผ่านจะแสดงผลลัพธ์หนึ่งบรรทัดต่อไฟล์พร้อมสรุปผล
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 msหากการทดสอบล้มเหลว ระบบจะแสดง error: Assert failure พร้อมระบุชื่อไฟล์และหมายเลขบรรทัด ตามด้วยค่าที่ได้รับจริงเทียบกับค่าที่คาดหวัง และ hurl จะส่งค่า exit code ที่ไม่ใช่ศูนย์เพื่อให้ CI หยุดทำงาน หาก status อ่านค่าได้ pending ในขณะที่คุณคาดหวัง paid แสดงว่าบริการของคุณไม่ได้ประมวลผล response จาก mock สิ่งที่ควรตรวจสอบลำดับถัดไปคือบันทึกการเรียกใช้งาน (request journal) ของ WireMock ที่ /__admin/requests ซึ่งจะแสดงให้เห็นว่าคำขอดังกล่าวส่งไปถึง mock หรือไม่
เรียกใช้ชุดทดสอบจาก CI runner ของคุณเอง
เมื่อมี runner ที่ลงทะเบียนไว้บนเครื่องเดียวกัน เวิร์กโฟลว์จะสั้นลง เนื่องจาก runner เป็นเพียง process หนึ่งบนโฮสต์ คุณจึงต้องติดตั้ง docker และ hurl ไว้บนโฮสต์นั้นด้วย โดยจะไม่มีการสืบทอดค่าใดๆ มาจาก hosted image
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() ในขั้นตอน archive มีความสำคัญ หากไม่มีคำสั่งนี้ การทดสอบที่ล้มเหลวจะข้ามขั้นตอนการคัดลอกไป ทำให้คุณไม่ได้รับรายงานที่ต้องการอ่าน นอกจากนี้ ไฟล์ที่คัดลอกต้องถูกจัดเก็บไว้นอก workspace เนื่องจาก runner จะล้างข้อมูลใน workspace ก่อนเริ่มงานถัดไป ซึ่งจะทำให้รายงานเหล่านั้นถูกลบไปด้วย
เก็บผลลัพธ์ไว้ ไม่ใช่แค่การรันครั้งล่าสุด
ไฟล์ JUnit XML ต่อหนึ่ง commit ตอบคำถามได้เพียงข้อเดียวคือ ผ่านหรือไม่ผ่าน แต่ไม่สามารถตอบได้ว่า endpoint เริ่มทำงานช้าลงตั้งแต่เมื่อใด เพราะไม่มีใครอ่านไฟล์เหล่านั้นหลังจากที่คุณเลิกเปิดดู หากต้องการดูแนวโน้ม ให้เพิ่มข้อมูลหนึ่งแถวต่อการรันหนึ่งครั้งลงในฐานข้อมูลขนาดเล็กบนเครื่องเดียวกัน ตารางเดียวที่เก็บค่า commit SHA, ชื่อไฟล์, จำนวนที่ผ่าน, จำนวนที่ล้มเหลว และระยะเวลาการทำงานก็เพียงพอแล้ว และ การใช้ SQLite ในสภาพแวดล้อม production บน VPS เป็นตัวเลือกที่เหมาะสมในการจัดเก็บ เนื่องจากเป็นไฟล์เดียว ไม่ต้องมีกระบวนการ server แยกต่างหาก และประวัติทั้งหมดจะถูกรวมอยู่ในไฟล์สำรองข้อมูลที่คุณทำไว้อยู่แล้ว ให้เลือกใช้วิธีการ parse ผลลัพธ์ --report-json ของ Hurl แทนการใช้ JUnit XML เนื่องจากเป็นรูปแบบที่เครื่องอ่านได้ง่ายกว่าในสองตัวเลือกนี้
สิ่งที่ต้องคงอยู่หลังจากการสร้างคอนเทนเนอร์ใหม่
คำนิยามจำลอง (mock definitions) และชุดทดสอบ (test suites) คือซอร์สโค้ด สิ่งเหล่านี้ควรอยู่ในที่เก็บข้อมูล (repository) เดียวกันกับบริการที่อธิบาย และต้องมีการเปลี่ยนแปลงใน pull request เดียวกันกับที่มีการแก้ไข endpoint การแก้ไข stub ผ่าน web UI หรือการส่ง expectation เข้าไปที่ MockServer ผ่าน REST API ในขณะที่โปรแกรมทำงาน จะคงอยู่เพียงในหน่วยความจำของคอนเทนเนอร์หรือฐานข้อมูลของเครื่องมือนั้นเท่านั้น เมื่อรัน docker compose down ข้อมูลเหล่านั้นจะหายไป และจะไม่มีใครสังเกตเห็นจนกว่าการทดสอบจะผ่านด้วยเหตุผลที่ผิดพลาด หาก repository ของคุณทำงานบนฮาร์ดแวร์ของคุณเอง เซิร์ฟเวอร์ git ที่โฮสต์เอง จะช่วยเก็บข้อมูลจำลอง (fixtures) และตัวบริการไว้ภายในขอบเขตความปลอดภัยเดียวกัน
จากนั้นคือหลักปฏิบัติจริง ให้ระบุ tag ของ image ให้ชัดเจน (pin image tags) เพราะ latest อาจเปลี่ยนวิธีการที่ mock ของคุณจับคู่คำขอโดยที่คุณไม่ได้แก้ไข repository เลย และความล้มเหลวนั้นจะสืบหาต้นตอได้ยากมาก ให้ mount ไดเรกทอรีของ stub แบบอ่านได้อย่างเดียว (read only) ในกรณีที่เครื่องมือไม่จำเป็นต้องเขียนข้อมูลลงไป ห้ามเก็บ stub ของ mock ไว้ใน named Docker volume เพราะ volume นั้นจะกลายเป็นแหล่งข้อมูลหลัก (source of truth) และสำเนาที่อยู่ใน git จะกลายเป็นข้อมูลที่ผิดพลาดโดยไม่รู้ตัว
อีกหนึ่งข้อที่มักทำให้เกิดปัญหา หากคุณสร้าง stub โดยการบันทึก traffic จริงผ่าน proxy ให้ตรวจสอบไฟล์ที่สร้างขึ้นทุกไฟล์ก่อนทำการ commit ข้อมูลที่บันทึกไว้จะเก็บสิ่งที่ upstream ส่งกลับมาทั้งหมด รวมถึง bearer token และที่อยู่อีเมลของลูกค้า การ commit ข้อมูลเหล่านี้จะทำให้ข้อมูลดังกล่าวอยู่ใน repository ของคุณอย่างถาวร เนื่องจาก git จะเก็บเนื้อหาที่ถูกลบไว้ในประวัติเสมอ
FAQ
API mock server กับ API test runner ต่างกันอย่างไร?
Mock server ทำหน้าที่ตอบรับคำขอ โดยจำลองการทำงานของ dependency ที่คุณไม่สามารถเรียกใช้งานได้จาก CI และไม่มีการรายงานผลว่าผ่านหรือล้มเหลว ส่วน API test runner จะส่งคำขอไปยังบริการของคุณ ตรวจสอบการตอบสนอง (assert) ส่งผ่านค่าจากคำขอหนึ่งไปยังอีกคำขอหนึ่ง และส่ง exit code ที่ไม่ใช่ศูนย์เมื่อการตรวจสอบล้มเหลว ทั้งสองเครื่องมือแก้ปัญหาคนละส่วนกัน ในการใช้งานทั่วไปมักใช้ร่วมกัน โดย runner จะเรียกบริการของคุณ ในขณะที่บริการของคุณจะเรียกไปยัง mock
ฉันสามารถทดสอบ API ภายในจาก hosted CI runner ได้หรือไม่?
ไม่ได้หากไม่เปิดเผย API นั้นออกมา เนื่องจาก hosted runner อยู่ภายนอกเครือข่ายของคุณ จึงไม่สามารถเข้าถึงบริการที่ผูกอยู่กับ private address ได้ ทางเลือกของคุณคือการเผยแพร่ API, การรัน tunnel หรือการดูแลสำเนา staging แบบสาธารณะ ซึ่งแต่ละวิธีล้วนเพิ่มระบบที่อาจเกิดความล้มเหลวหรือข้อมูลรั่วไหลได้ การใช้ runner ภายในเครือข่ายส่วนตัวเดียวกันจะสามารถเรียกบริการได้โดยตรง ซึ่งเป็นเหตุผลเชิงปฏิบัติหลักที่ทีมงานเลือกทำ self-host งานส่วนนี้เอง
ควรเก็บ mock stubs และ API test suites ไว้ที่ไหน?
ควรเก็บไว้ใน git คู่กับบริการที่เกี่ยวข้อง เครื่องมือที่จัดเก็บนิยามเป็นไฟล์ เช่น ไดเรกทอรี mappings/ ของ WireMock, ไฟล์ข้อมูลของ Mockoon, ไฟล์ Hurl และโฟลเดอร์ .bru ของ Bruno ช่วยให้คุณทำ code review และสร้าง container ใหม่ได้โดยไม่มีค่าใช้จ่าย ส่วนเครื่องมือที่เก็บนิยามไว้ในฐานข้อมูลหรือ web UI จำเป็นต้องมีแผนสำรองและขั้นตอนการ export ซึ่งมักเป็นสิ่งที่คนลืมทำจนกระทั่ง container หายไปแล้ว
ทำไม mock ของฉันถึงตอบกลับเป็น 404 ทั้งที่ stub ดูถูกต้องแล้ว?
WireMock จะให้บริการ stub เฉพาะเมื่อมีการจับคู่ที่ตรงกันทุกประการเท่านั้น คำขอที่ไม่ตรงกับ stub จะได้รับ 404 พร้อมเนื้อหาที่ขึ้นต้นด้วย Request was not matched ตามด้วยผลต่าง (diff) เทียบกับ stub ที่ใกล้เคียงที่สุด ซึ่งผลต่างนั้นจะระบุฟิลด์ที่แตกต่างกัน สาเหตุที่พบบ่อยคือการมี slash ต่อท้ายใน path, header Content-Type ที่ stub ต้องการแต่ client ของคุณไม่ได้ส่งมา, การใช้ urlPath ในจุดที่ stub ต้องการ urlPathPattern สำหรับส่วนที่เป็นตัวแปร และตัวจับคู่ body ที่ไม่ตรงกับ payload ให้ตรวจสอบ /__admin/requests ก่อนเพื่อยืนยันว่าคำขอมาถึง mock แล้วจริงหรือไม่
ฉันยังจำเป็นต้องใช้ mocks หากมี staging environment อยู่แล้วหรือไม่?
จำเป็น ด้วยเหตุผลสองประการ ประการแรก สำเนา staging ของ upstream ที่คุณควบคุมไม่ได้อาจล่มหรือจำกัดอัตราการใช้งาน (rate limit) ทำให้ชุดทดสอบของคุณล้มเหลวด้วยเหตุผลที่ไม่เกี่ยวข้องกับโค้ดของคุณ ประการที่สอง staging ไม่สามารถสร้างการตอบสนองที่คุณต้องการทดสอบได้ เช่น กรณีบัตรถูกปฏิเสธหรือ gateway timeout ในขณะที่ mock สามารถส่งการตอบสนองเหล่านั้นได้ตามต้องการด้วยความเร็วระดับเครือข่ายภายใน ซึ่งช่วยลดเวลาการรันชุดทดสอบจากหลายนาทีบน sandbox ให้เหลือเพียงไม่กี่วินาที ให้เก็บ staging ไว้สำหรับการตรวจสอบขั้นสุดท้ายก่อน release และใช้ mocks ในขั้นตอน CI