แบบฝึกหัด — WireMock สำหรับ QA¶
Recall (Beginner)¶
ข้อ 1¶
อธิบายด้วยคำของคุณเองว่า "Mock API" คืออะไร และต่างจาก API จริงอย่างไร จากนั้นยกตัวอย่าง 2 สถานการณ์ในชีวิตการทำงาน QA ที่การมี mock server จะช่วยให้ทำงานได้ง่ายขึ้น (ห้ามใช้ตัวอย่างที่มีในบทเรียน)
เฉลย
Mock API คือ server ปลอมที่เราสร้างขึ้นเองเพื่อแทน API จริง return response ที่เรากำหนดได้เองโดยไม่มี business logic หรือ database จริงอยู่เบื้องหลัง ต่างจาก API จริงตรงที่ mock ไม่ "ทำ" อะไร มันแค่ "ตอบ" ตามที่เราสั่ง
ตัวอย่างสถานการณ์ (ตอบได้หลายแบบ): - ทดสอบ feature ใหม่ที่ต้องเรียก SMS API แต่ไม่อยากเสียเงินค่า SMS จริงระหว่าง dev - ทดสอบแอปในโหมด offline เพื่อดูว่า UI แสดง error message ถูกต้อง - ทดสอบ feature ที่ขึ้นอยู่กับ weather API โดยไม่ต้องรอสภาพอากาศจริง
ข้อ 2¶
เขียน JSON stub mapping สำหรับ endpoint ต่อไปนี้:
- Method: GET
- URL: /api/weather/bangkok
- Response status: 200
- Response body: JSON ที่มี field city, temperature, condition
เฉลย
ข้อ 3¶
คุณรัน java -jar wiremock.jar แล้วได้ error ว่า Address already in use: 8080 จะแก้ไขอย่างไร? และจะทดสอบว่า server พร้อมทำงานด้วยคำสั่งอะไร?
เฉลย
แก้โดยระบุ port อื่น: java -jar wiremock.jar --port 9090
ทดสอบว่าพร้อมด้วย: curl http://localhost:9090/__admin/mappings
ถ้าได้ {"mappings":[],"meta":{"total":0}} กลับมา แสดงว่าพร้อม
Application (Intermediate)¶
ข้อ 4¶
ทีมของคุณกำลังสร้างแอปจอง ticket ภาพยนตร์ มี API ดังนี้:
GET /api/movies— ดูรายการหนังทั้งหมดGET /api/movies/{id}/seats— ดูที่นั่งว่างของหนังPOST /api/bookings— จองที่นั่ง
ออกแบบ stub files สำหรับทดสอบ กรณีที่หนังเต็ม (ไม่มีที่นั่งว่าง) โดยเขียน JSON ให้ครบและอธิบายว่า test case นี้ทดสอบอะไร
เฉลย
mappings/movies/get-movie-seats-full.json:
{
"request": {
"method": "GET",
"urlPattern": "/api/movies/[0-9]+/seats"
},
"response": {
"status": 200,
"jsonBody": {
"movieId": 1,
"title": "Movie Title",
"availableSeats": 0,
"totalSeats": 150
},
"headers": { "Content-Type": "application/json" }
}
}
mappings/bookings/post-booking-sold-out.json:
{
"request": {
"method": "POST",
"urlPath": "/api/bookings"
},
"response": {
"status": 409,
"jsonBody": {
"error": "Conflict",
"message": "No seats available for this showing"
},
"headers": { "Content-Type": "application/json" }
}
}
Test case นี้ทดสอบว่า: UI แสดงข้อความ "ที่นั่งเต็มแล้ว" ให้ user ถูกต้อง และปุ่มจองถูก disable หรือแสดง error message ที่เหมาะสมเมื่อ API return 409
ข้อ 5¶
สร้าง Postman Test Script สำหรับ request POST /api/bookings ที่ verify ว่า:
1. Status code เป็น 201
2. Response body มี field bookingId
3. bookingId ไม่ใช่ string ว่าง
4. Response time น้อยกว่า 1000ms
เฉลย
pm.test("Status code is 201", function () {
pm.response.to.have.status(201);
});
pm.test("Response has bookingId", function () {
const body = pm.response.json();
pm.expect(body).to.have.property("bookingId");
});
pm.test("bookingId is not empty", function () {
const body = pm.response.json();
pm.expect(body.bookingId).to.not.be.empty;
});
pm.test("Response time is acceptable", function () {
pm.expect(pm.response.responseTime).to.be.below(1000);
});
ข้อ 6¶
เขียน Robot Framework test case สำหรับทดสอบ flow ต่อไปนี้โดยใช้ WireMock:
- GET
/api/notifications→ ได้ 0 notification - POST
/api/notifications/mark-all-read→ ได้ success - GET
/api/notifications→ ยังคง return 0 notification
(ไม่ต้องใช้ Scenario เพราะ GET return เหมือนกันทั้งก่อนและหลัง)
เฉลย
*** Settings ***
Library RequestsLibrary
Resource resources/wiremock.resource
Suite Setup Start WireMock
Suite Teardown Stop WireMock
*** Test Cases ***
Mark All Notifications As Read
# Step 1: ดู notifications ก่อน
${resp1}= GET On Session mock_api /api/notifications
Should Be Equal As Integers ${resp1.status_code} 200
Should Be Equal As Integers ${resp1.json()}[count] 0
# Step 2: Mark ทั้งหมดว่าอ่านแล้ว
${resp2}= POST On Session mock_api /api/notifications/mark-all-read
Should Be Equal As Integers ${resp2.status_code} 200
Should Be Equal ${resp2.json()}[message] All notifications marked as read
# Step 3: ตรวจสอบอีกครั้ง
${resp3}= GET On Session mock_api /api/notifications
Should Be Equal As Integers ${resp3.status_code} 200
Should Be Equal As Integers ${resp3.json()}[count] 0
Synthesis (Advanced)¶
ข้อ 7¶
ทีม QA กำลังทดสอบระบบ delivery tracking ที่มี workflow ดังนี้:
ออกแบบ WireMock Scenario สำหรับ endpoint GET /api/delivery/{trackingId} ที่ return status ต่างกันตาม workflow พร้อม:
- เขียน JSON stub ทุกไฟล์ให้ครบ
- อธิบาย state machine ที่ออกแบบ
- บอก trigger ที่ทำให้ state เปลี่ยน
เฉลย
State Machine:
Trigger: POST ไปที่ endpoint /__admin/scenarios/{name}/state หรือใช้ stub ที่ trigger state change
mappings/delivery-01-order-received.json:
{
"scenarioName": "Delivery Tracking",
"requiredScenarioState": "Started",
"newScenarioState": "Order Received",
"request": { "method": "GET", "urlPattern": "/api/delivery/[A-Z0-9]+" },
"response": {
"status": 200,
"jsonBody": {
"status": "pending",
"message": "Order received, waiting for pickup"
},
"headers": { "Content-Type": "application/json" }
}
}
mappings/delivery-02-in-transit.json:
{
"scenarioName": "Delivery Tracking",
"requiredScenarioState": "Order Received",
"newScenarioState": "In Transit",
"request": { "method": "GET", "urlPattern": "/api/delivery/[A-Z0-9]+" },
"response": {
"status": 200,
"jsonBody": {
"status": "in_transit",
"message": "Package is on the way",
"estimatedDelivery": "Tomorrow 14:00-18:00"
},
"headers": { "Content-Type": "application/json" }
}
}
mappings/delivery-03-delivered.json:
{
"scenarioName": "Delivery Tracking",
"requiredScenarioState": "In Transit",
"request": { "method": "GET", "urlPattern": "/api/delivery/[A-Z0-9]+" },
"response": {
"status": 200,
"jsonBody": {
"status": "delivered",
"message": "Package delivered successfully",
"deliveredAt": "2024-01-15 15:30:00"
},
"headers": { "Content-Type": "application/json" }
}
}
ใน test แต่ละ call GET จะเปลี่ยน state ไปทีละขั้น สามารถ verify ว่า UI แสดง tracking status ถูกต้องในแต่ละขั้น
ข้อ 8¶
ทีมของคุณมีปัญหา: QA แต่ละคนมี mock files ต่างกันในเครื่องตัวเอง ทำให้ test ผ่านในเครื่องหนึ่งแต่ fail ในเครื่องอื่น
วิเคราะห์ว่าปัญหานี้เกิดจากอะไร และออกแบบ process + โครงสร้างไฟล์ที่จะแก้ปัญหานี้ให้ถาวร โดยต้องครอบคลุม: การเก็บไฟล์, workflow ของทีม, และการตรวจสอบว่า mock ยังตรงกับ API จริง
เฉลย
สาเหตุ: Mock files ไม่ได้เก็บใน version control ร่วมกัน ทุกคนสร้าง/แก้ local copy แยกกัน
โครงสร้างที่แนะนำ:
project-repo/
├── tests/
└── mocks/
├── README.md
├── mappings/
│ ├── auth/
│ ├── orders/
│ └── ...
└── __files/
Process: 1. Mock files อยู่ใน Git repo เดียวกับ test code 2. เมื่อ dev เปลี่ยน API spec → เปิด PR → QA review และอัปเดต mock ใน PR เดียวกัน 3. CI pipeline รัน validation script ตรวจว่า mock ตอบสนองถูกต้อง 4. ห้าม merge ถ้า mock validation fail
การตรวจสอบ: - เปรียบเทียบ mock response กับ OpenAPI/Swagger spec ในทุก CI run - ถ้า spec เปลี่ยน → test fail → บังคับให้อัปเดต mock ก่อน merge