← MockServer Professional Day 2 • Module 2

Day 2 / Module 2: Advanced Expectation Matching, Action Types & Dynamic Response Logic

คู่มือพิมพ์เขียวเชิงลึกและการวิเคราะห์ทางเทคนิค: JSON Schema Matching, Partial JSON, Velocity & JavaScript Dynamic Templates, Action Types และ 6 Hands-on Drills

[1] ส่วนที่ 1: หมุดเชื่อมโยง & Roadmap Progress Tracker

Day Tracker: 2/6

ชื่อบท/หัวข้อหลัก: Module 2: Advanced Expectation Matching (Matchers), Action Types (Response, Forward, Callback, Error) & Dynamic Response Logic (Velocity, JS, WASM)

❓ หมุดตั้งโจทย์: "ทำไมบทนี้ถึงชื่อนี้? และเรากำลังเรียนเรื่องนี้ไปเพื่ออะไร?"

ตอบ: ในระบบจริง ข้อมูลที่ส่งหากันไม่ได้มีแค่ข้อความตายตัว (Static Text) แต่มีความผันแปรตาม Input เช่น สกุลเงิน, ID ผู้ใช้, หรือเงื่อนไขทางธุรกิจ บทนี้ตั้งชื่อว่า Advanced Expectation Matching & Dynamic Response Logic เพราะการใช้ Static Mocks ที่ตอบกลับค่าเดิมๆ จะทำให้เกิดภาวะ Combinatorial Explosion (ต้องเขียน Mocks นับร้อยข้อสำหรับทุกกรณี) บทนี้จึงสอนวิธีใช้ Advanced Matchers และ Dynamic Templates เพื่อให้ Expectation เพียงข้อเดียวสามารถคำนวณและตอบกลับข้อมูลที่เปลี่ยนไปตาม Request ได้อย่างชาญฉลาด

PROGRESS ROADMAP:
✓ Day 1: Mod 1
▶ Day 2: Mod 2
Day 3: Mod 3
Day 4: Mod 4
Day 5: Mod 5
Day 6: Mod 6

[2] ส่วนที่ 2: เรื่องราวและมโนทัศน์เชิงลึก (Narrative, Case Study & Mental Models)

1. ฉากสถานการณ์จริง (The Narrative/Case Study)

ระบบชำระเงินข้ามชาติ (Global Multi-Currency Payment Gateway) กับปัญหา Static Mock บานปลาย

บริษัทอีคอมเมิร์ซเปิดขยายบริการไปยัง 15 ประเทศ ระบบต้องรองรับการตัดเงินในสกุลเงินที่หลากหลาย (THB, USD, EUR, JPY ฯลฯ) พร้อมทั้งคำนวณอัตราแลกเปลี่ยน ภาษีมูลค่าเพิ่มประจำประเทศ และออกสุ่มรหัสธุรกรรม (transaction_id) ที่ไม่ซ้ำกันกลับมา

ในตอนแรก ทีมพัฒนาใช้วิธีสร้าง Static Mock คืนค่า JSON ตรงๆ ผลคือ เมื่อต้องทดสอบเคสชำระเงิน 15 สกุลเงิน ร่วมกับสถานะผู้ใช้งาน 4 ประเภท ทีมงานต้องนั่งเขียน Expectation ถึง 60 ข้อ (15 x 4)! และเมื่อ API มีการปรับโครงสร้างเพียงฟีลด์เดียว ทีมงานต้องตามแก้ไขไฟล์ JSON ทั้ง 60 ไฟล์ ส่งผลให้โค้ดการทดสอบพังพินาศ

💡 การนำ Advanced Matchers & Dynamic Response เข้ามาแก้ปัญหา: ทีมงานปฏิวัติตัว Mock ใหม่ทั้งหมด โดยใช้ JSON Schema Matcher ตรวจสอบความถูกต้องของ Payload และใช้ Velocity Template Logic สกัดค่า amount, currency, และ userId จาก Request มาคำนวณภาษี สุ่มรหัส UUID แล้วตอบกลับเป็น JSON ไดนามิก ผลคือใช้ Expectation เพียงข้อเดียวแทนการเขียน 60 ข้อเดิม!

2. ถอดรหัสศัพท์เทคนิคและอุปมาอุปไมย (Explicit Terms & Mental Models)
01. JSON Schema Matching
ตะแกรงร่อนขนาดสินค้ามาตรฐานสเปก

แปลตรงตัว: การเทียบโครงสร้าง JSON กับสเปกมาตรฐาน

อุปมาอุปไมย: เหมือน "ตะแกรงร่อนขนาดสินค้า" ที่ไม่สนใจว่าลูกบอลข้างในจะเป็นสีอะไร แต่ตรวจสอบว่าขนาดและรูปทรงของลูกบอล (Data Types, Required Fields) ตกผ่านช่องตะแกรงตามกฎมาตรฐานหรือไม่

02. Partial JSON (ONLY_MATCHING_FIELDS)
เครื่องสแกนจุดสำคัญบนใบหน้า

แปลตรงตัว: การดักจับเฉพาะฟีลด์ที่สนใจ

อุปมาอุปไมย: เหมือน "เครื่องสแกนใบหน้า" ที่ตรวจเฉพาะดวงตาและจมูก แม้ผู้ใช้จะใส่หมวกหรือใส่ต่างหูเพิ่มมา (มีฟีลด์อื่นเพิ่มมาใน JSON) เครื่องก็ยังจำหน้าได้ถูกต้อง

03. Dynamic Response Templates
พนักงานพิมพ์ดีดอัจฉริยะกรอกแบบฟอร์ม

แปลตรงตัว: เทมเพลตการตอบกลับแบบเปลี่ยนค่าได้ตามตรรกะ

อุปมาอุปไมย: เหมือน "พนักงานพิมพ์ดีดอัจฉริยะ" ที่หยิบกระดาษแบบฟอร์มเปล่ามา แล้วมองดูเอกสารที่ลูกค้าส่งมา จากนั้นคัดลอกชื่อ สกุลเงิน คำนวณเลขภาษีสดๆ แล้วพิมพ์ใส่แบบฟอร์มส่งกลับไปให้ทันที

04. Action Types
สวิตช์เลือกโหมดทำงาน 4 ปุ่ม

แปลตรงตัว: ประเภทของการกระทำเมื่อ Match

อุปมาอุปไมย: เหมือน "สวิตช์เลือกโหมดทำงาน 4 ปุ่ม" ได้แก่ คืนค่า (httpResponse), ส่งต่อ (httpForward), สคริปต์ (httpCallback), และตัดสายพัง (httpError)

⚠️ Root Cause (รากของปัญหา)

การเขียน Mock แบบ Shallow Modeling ที่ใช้ค่า Static ทำให้เกิดภาวะ Combinatorial Explosion คือไฟล์ตั้งค่า Mocks ขยายตัวตามจำนวน Test Cases จนไม่สามารถบำรุงรักษาได้

📉 Business Impact (ผลกระทบทางธุรกิจ)

นักพัฒนาต้องเสียเวลาถึง 40% ไปกับการนั่งแก้ไขไฟล์ Static Mocks แทนที่จะได้นำเวลาไปพัฒนาฟีเจอร์ทางธุรกิจที่มีมูลค่า

[3] ส่วนที่ 3: การถอดรหัสและอธิบายโค้ดอย่างละเอียด (Line-by-line Breakdown)

1. JSON Schema Request Matcher (body.jsonSchema)
{
  "httpRequest": {
    "method": "POST",
    "path": "/api/v2/register",
    "body": {
      "type": "JSON_SCHEMA",               // [Line 5] ระบุเอนจินการตรวจจับเป็น JSON_SCHEMA
      "jsonSchema": "{\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"email\": {\"type\": \"string\"}, \"age\": {\"type\": \"integer\", \"minimum\": 18}}, \"required\": [\"email\", \"age\"]}" // [Line 6] กำหนดสเปก: email ต้องเป็น string, age ต้อง >= 18 และทั้งคู่เป็นฟีลด์บังคับ
    }
  }
}
2. Velocity Dynamic Response Template (httpResponseTemplate)
{
  "httpResponseTemplate": {
    "templateType": "VELOCITY",            // [Line 3] ใช้ Apache Velocity Engine ในการประมวลผล
    "template": "{\n  \"statusCode\": 200,\n  \"headers\": { \"Content-Type\": [\"application/json\"] },\n  \"body\": \"{\\\"transactionId\\\": \\\"TX-$uuid.random()\\\", \\\"chargedAmount\\\": $request.body.amount, \\\"currency\\\": \\\"$request.body.currency\\\", \\\"vat7\\\": $request.body.amount * 0.07}\"\n}"
    // [Line 4] $uuid.random() สุ่ม UUID, $request.body.amount ดึงยอดเงินจาก Request มาคำนวณภาษี 7% สดๆ
  }
}
3. JavaScript Conditional Response Template (templateType: JAVASCRIPT)
{
  "httpResponseTemplate": {
    "templateType": "JAVASCRIPT",          // [Line 3] ใช้ JavaScript Engine (Nashorn/GraalVM)
    "template": "var body = JSON.parse(request.body);\nvar result = body.score >= 700 ? 'APPROVED' : 'REJECTED';\nreturn {\n  'statusCode': 200,\n  'headers': { 'Content-Type': ['application/json'] },\n  'body': JSON.stringify({\n    'applicant': body.name,\n    'score': body.score,\n    'status': result\n  })\n};"
    // [Line 4] สกัดอ่าน score จาก Request Body มาตัดเกรดผ่าน/ไม่ผ่านด้วย Ternary Operator ใน JS
  }
}

[4] ส่วนที่ 4: คู่มือการใช้งานผ่าน Web UI / Dashboard

เปิดเว็บเบราว์เซอร์ไปที่: http://localhost:1080/mockserver/dashboard

========================================================================================
                          MOCKSERVER WEB DASHBOARD (PORT 1080)
========================================================================================
 [ Active Expectations ] | [ Recorded Requests / Logs ] | [ Breakpoints ] | [ Reset All ]
----------------------------------------------------------------------------------------
Active Expectations Tab:    - ตรวจสอบว่าการดักจับในช่อง httpRequest เป็นประเภท JSON_SCHEMA, REGEX หรือ ONLY_MATCHING_FIELDS
                            - ตรวจดูว่าช่อง httpResponseTemplate แสดงเอนจิน VELOCITY หรือ JAVASCRIPT ครบถ้วน

Recorded Requests & Logs:   - หากยิง Request แล้วได้รับ 404 Not Found ให้เปิดแท็บ Recorded Requests / Logs
                            - ระบบจะแสดงรายละเอียด Mismatch สาเหตุอย่างชัดเจน เช่น "Body did not match JSON Schema: age is less than minimum"

[5] ส่วนที่ 5: โครงสร้างไฟล์โปรเจกต์มาตรฐาน (Enterprise File Structure Best Practice)

mockserver-module2-enterprise/
├── docker-compose.yml
├── config/
│   └── mockserver.properties
├── expectations/
│   ├── auth/
│   │   └── regex-jwt-matcher.json         # ดักจับ Regex Path & Headers
│   ├── payments/
│   │   ├── velocity-dynamic-payment.json  # คำนวณยอดเงินและภาษีด้วย Velocity
│   │   └── schema-validation-order.json   # ตรวจสเปกด้วย JSON Schema
│   └── credit/
│       └── javascript-conditional.json    # ตรรกะเงื่อนไขซับซ้อนด้วย JavaScript
├── scripts/
│   └── seed_module2_expectations.sh
└── tests/
    └── AdvancedMatchingIntegrationTest.java

[6] ส่วนที่ 6: แบบฝึกหัดภาคปฏิบัติแน่นๆ (Hands-on Refactoring & 6 Drills)

❌ Flawed Approach (เขียน Static Mocks แยก 60 ข้อ)

ต้องสร้างไฟล์แยกทีละข้อสำหรับ THB, USD, EUR, JPY...

{
  "httpRequest": { "method": "POST", "path": "/charge", "body": "{\"currency\":\"THB\",\"amount\":100}" },
  "httpResponse": { "body": "{\"currency\":\"THB\",\"vat\":7}" }
}
✅ Refactored Approach (ใช้ Velocity Template เพียง 1 ข้อ)

ข้อเดียวรับได้ทุกสกุลเงินและคำนวณไดนามิกอัตโนมัติ:

{
  "httpRequest": { "method": "POST", "path": "/charge" },
  "httpResponseTemplate": {
    "templateType": "VELOCITY",
    "template": "{\"body\": \"{\\\"currency\\\": \\\"$request.body.currency\\\", \\\"vat\\\": $request.body.amount * 0.07}\"}"
  }
}
ชุดแบบฝึกหัดปฏิบัติการ 6 ข้อ (Hands-on Drill Pack)

ก่อนเริ่มทำ ตรวจสอบว่าเปิด MockServer ไว้ที่ http://localhost:1080 เรียบร้อยแล้ว

Drill 2.1: JSON Schema Validation (ตรวจสเปกข้อมูลอย่างเข้มงวด)

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "POST", "path": "/api/v2/register", "body": { "type": "JSON_SCHEMA", "jsonSchema": "{\"$schema\": \"http://json-schema.org/draft-07/schema#\", \"type\": \"object\", \"properties\": {\"email\": {\"type\": \"string\"}, \"age\": {\"type\": \"integer\", \"minimum\": 18}}, \"required\": [\"email\", \"age\"]}" } }, "httpResponse": { "statusCode": 201, "headers": { "Content-Type": ["application/json"] }, "body": "{\"status\": \"REGISTERED\"}" }}'

Test & Expected: ผ่าน: curl -i -X POST http://localhost:1080/api/v2/register -H "Content-Type: application/json" -d '{"email": "[email protected]", "age": 20}' (ได้ 201 Created) | ไม่ผ่าน: curl -i -X POST http://localhost:1080/api/v2/register -H "Content-Type: application/json" -d '{"email": "[email protected]", "age": 15}' (ได้ 404)

Drill 2.2: Partial JSON Matching (ONLY_MATCHING_FIELDS)

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "POST", "path": "/api/v2/search", "body": { "type": "JSON", "json": "{\"filter\": \"active\"}", "matchType": "ONLY_MATCHING_FIELDS" } }, "httpResponse": { "statusCode": 200, "headers": { "Content-Type": ["application/json"] }, "body": "{\"results\": [\"Item 1\"]}" }}'

Test & Expected: curl -i -X POST http://localhost:1080/api/v2/search -H "Content-Type: application/json" -d '{"filter": "active", "page": 5}' (ได้ 200 OK แม้มีฟีลด์ page เพิ่มมา)

Drill 2.3: Regex Path & Dynamic Authorization Matching

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "GET", "path": { "type": "REGEX", "regex": "/api/v2/users/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}" }, "headers": { "Authorization": ["Bearer jwt\\..*"] } }, "httpResponse": { "statusCode": 200, "headers": { "Content-Type": ["application/json"] }, "body": "{\"profile\": \"Valid UUID\"}" }}'

Test & Expected: curl -i http://localhost:1080/api/v2/users/123e4567-e89b-12d3-a456-426614174000 -H "Authorization: Bearer jwt.xyz123" (ได้ 200 OK)

Drill 2.4: Apache Velocity Dynamic Calculation (ดึงค่าคำนวณสด)

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "POST", "path": "/api/v2/checkout" }, "httpResponseTemplate": { "templateType": "VELOCITY", "template": "{\n  \"statusCode\": 200,\n  \"headers\": { \"Content-Type\": [\"application/json\"] },\n  \"body\": \"{\\\"orderId\\\": \\\"ORD-$uuid.random()\\\", \\\"finalAmount\\\": $request.body.amount * 0.9}\"\n}" }}'

Test & Expected: curl -i -X POST http://localhost:1080/api/v2/checkout -H "Content-Type: application/json" -d '{"amount": 2000}' (ได้ finalAmount: 1800.0 พร้อมสุ่ม UUID orderId)

Drill 2.5: JavaScript Conditional Logic (แตกเงื่อนไขใน Response)

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "POST", "path": "/api/v2/credit-check" }, "httpResponseTemplate": { "templateType": "JAVASCRIPT", "template": "var body = JSON.parse(request.body);\nvar res = body.score >= 700 ? 'APPROVED' : 'REJECTED';\nreturn {'statusCode': 200, 'headers': {'Content-Type': ['application/json']}, 'body': JSON.stringify({'status': res})};" }}'

Test & Expected: curl -i -X POST http://localhost:1080/api/v2/credit-check -H "Content-Type: application/json" -d '{"name": "Somchai", "score": 750}' (ได้ "status": "APPROVED")

Drill 2.6: Network Fault Simulation (httpError - Drop Connection)

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "GET", "path": "/api/v2/unstable" }, "httpError": { "dropConnection": true }}'

Test & Expected: curl -i http://localhost:1080/api/v2/unstable (ได้ curl: (52) Empty reply from server ทันที)

⚡ Instant Feedback Quiz

ส่วนที่ 7: แบบทดสอบประเมินความรู้ Day 2 / Module 2 (10 ข้อ)

คลิกเลือกคำตอบข้อที่ถูกต้อง ระบบจะตรวจให้คะแนนและแสดงผลเฉลยพร้อมคำอธิบายทันที!

คะแนนสด 0 / 10
[8]

ส่วนที่ 8: สรุปหมุดเชื่อมโยงกลับหาหัวข้อบท (Closing Title Connection Wrap-up)

เนื้อหาและแบบฝึกหัดทั้งหมดในวันนี้ ตอบและสะท้อนชื่อบท Day 2 / Module 2: Advanced Expectation Matching, Action Types & Dynamic Response Logic อย่างสมบูรณ์: การใช้ Advanced Matchers (เช่น JSON Schema) ช่วยให้เรากรอง Request ได้อย่างแม่นยำ, การเลือก Action Types ที่เหมาะสมทำให้ควบคุมทิศทางของ Response ได้หลากหลาย และการใช้ Dynamic Response Logic (Velocity/JS) คือหัวใจสำคัญที่ช่วยให้ Expectation เพียงข้อเดียวรับมือกับข้อมูลนับพันรูปแบบได้โดยไม่ต้องนั่งเขียน Static Mocks ซ้ำซ้อนครับ!