← MockServer Professional Day 1 • Module 1

Day 1 / Module 1: Architecture, Core Roles (Mock, Proxy, Verify), Port Unification & Enterprise Deployment

คู่มือพิมพ์เขียวเชิงลึกและการวิเคราะห์ทางเทคนิค: สถาปัตยกรรมภายใน, 3 บทบาทหลัก, Port Unification, Docker, Kubernetes, Testcontainers และแบบฝึกหัดปฏิบัติการ 6 Drills

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

Day Tracker: 1/6

ชื่อบท/หัวข้อหลัก: Module 1: Architecture, Core Roles (Mock, Proxy, Verify), Port Unification & Enterprise Deployment (Docker, Testcontainers, K8s)

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

ตอบ: ในการสร้างระบบซอฟต์แวร์ระดับองค์กร ปัญหาใหญ่ที่สุดคือแอปพลิเคชันของเราต้องพึ่งพาระบบภายนอกที่ไม่เสถียร (Unstable External Dependencies) บทนี้ตั้งชื่อว่า Architecture, Core Roles & Enterprise Deployment เพราะเราจำเป็นต้องเข้าใจสถาปัตยกรรมภายใน รู้จัก 3 บทบาทหลัก (Mock, Proxy, Verify) และเรียนรู้วิธีการติดตั้งรันในองค์กร (Docker/K8s/Testcontainers) เพื่อ "ตัดขาดความเสี่ยง" จากระบบภายนอก ทำให้ทีมพัฒนาสามารถเขียนและทดสอบซอฟต์แวร์ได้เสถียร รวดเร็ว และเป็น Deterministic 100% โดยไม่ต้องง้อใคร

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)

ระบบ E-Commerce ตกเป็นตัวประกันของเซิร์ฟเวอร์ Staging ในคืนวันมหกรรมลดราคา

ทีมพัฒนาแอปพลิเคชันช้อปปิ้งออนไลน์กำลังเตรียมเปิดตัวระบบชำระเงินใหม่ก่อนเทศกาล 11.11 แต่ในสภาพแวดล้อม Staging เซิร์ฟเวอร์ชำระเงินของธนาคารร่วมเปิดทดสอบล่มตลอดเวลา เนื่องจากมีทีมอื่นยิง Load Test ขนานกัน ผลคือ Pipeline CI/CD ของทีมล้มเหลว (Build Failed) ทุกครั้ง และนักพัฒนาไม่สามารถรัน Integration Test บนเครื่องตนเองได้เลย

นอกจากนี้ เมื่อพยายามรัน Docker บนเครื่อง Local พอร์ตการเชื่อมต่อของบริการต่างๆ ก็ชนกันมั่วไปหมด ส่งผลให้งานล่าช้ากว่ากำหนดไป 3 สัปดาห์

💡 การนำ MockServer สถาปัตยกรรมระดับองค์กรเข้ามาแก้ปัญหา: ทีมงานเปลี่ยนวิธีคิด โดยติดตั้ง MockServer ผ่าน Testcontainers ลงในชุดซอร์สโค้ดการทดสอบโดยตรง และรันแบบ Centralized Container บน Kubernetes Cluster สำหรับทีมพัฒนา ทำให้ระบบทดสอบไม่ต้องวิ่งออกไปหาเซิร์ฟเวอร์ธนาคารจริงใน Staging อีกต่อไป แต่ยิงเข้าหา MockServer ที่ทำหน้าที่ตอบกลับข้อมูลจำลองได้อย่างรวดเร็วและแน่นอน (Deterministic)

2. ถอดรหัสศัพท์เทคนิคและอุปมาอุปไมย (Explicit Terms & Mental Models)
01. Mock Role
ตัวแสดงแทนที่ท่องบทมาเป๊ะๆ

แปลตรงตัว: การจำลองการตอบกลับ (Response)

อุปมาอุปไมย: เหมือน "ดาราตัวแสดงแทน (Stunt Double)" ที่ยืนรอรับบทแทนตัวจริง เมื่อแอปพลิเคชันถามอะไรมา ตัวแสดงแทนจะพูดบทตามสคริปต์ (Expectation) ที่เตรียมไว้ทันที

02. Proxy Role
กล้องวงจรปิดและด่านตรวจคั่นกลาง

แปลตรงตัว: ตัวกลางส่งผ่านข้อมูล

อุปมาอุปไมย: เหมือน "ด่านตรวจศุลกากร" ที่ยืนคั่นกลางระหว่างแอปพลิเคชันของเรากับเซิร์ฟเวอร์จริง คอยส่องดูบันทึกทราฟฟิก หรือส่งต่อ Request ไปยังจุดหมายปลายทาง

03. Verify Role
ศาลพิจารณาคดีหลังจบงาน

แปลตรงตัว: การตรวจสอบการเรียกใช้งาน

อุปมาอุปไมย: เหมือน "ผู้ตรวจการแผ่นดิน" ที่มาเปิดดูป้ายบันทึกหลังจบงาน เพื่อตรวจสอบว่าแอปพลิเคชันของเราได้ส่ง Request ออกไปถูกต้องตรงตามสัญญาหรือไม่

04. Port Unification
สวิตช์บอร์ดอัจฉริยะช่องเดียว

แปลตรงตัว: การรวมพอร์ตการเชื่อมต่อ

อุปมาอุปไมย: เหมือน "พนักงานโอเปอเรเตอร์ต้อนรับ" ที่นั่งอยู่หน้าประตูเดียว (พอร์ต 1080) ไม่ว่าแขกที่เดินเข้ามาจะพูดภาษา HTTP/1.1, HTTP/2, HTTPS หรือ SOCKS พนักงานคนนี้จะฟังออกและจัดการเปลี่ยนโหมดต้อนรับให้อัตโนมัติ

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

การผูกติดระบบทดสอบเข้ากับ External Shared Environment (Staging) ทำให้เกิดภาวะ Fragile Test คือการทดสอบตกเพราะระบบคนอื่นล่ม ไม่ใช่เพราะโค้ดของเรามี Bug

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

สูญเสียค่าใช้จ่ายจากการที่พัฒนาซอฟต์แวร์ล่าช้า (Delayed Time-to-Market), ทีมงานเสียเวลาสืบหา Bug เทียม (False Positive Failure) และเสี่ยงต่อการหลุด Bug ไปยัง Production

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

1. ไฟล์ตั้งค่า Docker Compose (docker-compose.yml)
version: '3.8'

services:
  mockserver:
    image: mockserver/mockserver:latest    # [Line 5] ดึง Docker Image ล่าสุดของ MockServer
    container_name: mockserver-lab         # [Line 6] ตั้งชื่อ Container
    ports:
      - "1080:1080"                        # [Line 8] Map พอร์ต Host 1080 เข้ากับ Container 1080 (Port Unification)
    environment:
      MOCKSERVER_PROPERTY_FILE: /config/mockserver.properties # [Line 10] ชี้ไฟล์ Config
      MOCKSERVER_LOG_LEVEL: INFO           # [Line 11] กำหนดระดับการบันทึก Log
    volumes:
      - ./config:/config                   # [Line 13] Mount โฟลเดอร์ตั้งค่าจากเครื่อง Host
2. โครงสร้าง JSON Expectation (PUT /mockserver/expectation)
{
  "httpRequest": {                         // [Section 1] กำหนดเงื่อนไขการตรวจจับ Request
    "method": "GET",                       // ตรวจจับ HTTP Method ต้องเป็น GET เท่านั้น
    "path": "/api/v1/health"               // ตรวจจับ URL Path ต้องเป็น /api/v1/health
  },
  "httpResponse": {                        // [Section 2] กำหนดสิ่งที่จะตอบกลับ (Action)
    "statusCode": 200,                     // ตอบกลับด้วย HTTP Status Code 200 OK
    "headers": {
      "Content-Type": ["application/json"] // ระบุ Header Response เป็น JSON
    },
    "body": "{\"status\": \"UP\"}"         // เนื้อหาข้อมูล JSON Response Body
  }
}
3. โค้ด Java Testcontainers (PaymentServiceTest.java)
@Testcontainers // [Line 1] สั่งเปิดการใช้งาน Testcontainers ใน JUnit 5
public class PaymentServiceTest {

    @Container  // [Line 4] สร้าง Container MockServer อัตโนมัติก่อนเริ่มรัน Test
    public static MockServerContainer mockServer = new MockServerContainer(
        DockerImageName.parse("mockserver/mockserver:latest")
    );

    private MockServerClient mockClient;

    @BeforeEach
    void setUp() {
        // [Line 12] เชื่อมต่อ Client เข้ากับ Dynamic Host & Port ของ Testcontainers
        mockClient = new MockServerClient(mockServer.getHost(), mockServer.getServerPort());
    }

    @Test
    void testPaymentSuccess() {
        // [Line 18] MOCK ROLE: ตั้งสคริปต์ Expectation ตอบกลับ SUCCESS
        mockClient.when(
            request().withMethod("POST").withPath("/api/v1/charge")
        ).respond(
            response().withStatusCode(200).withBody("{\"status\":\"SUCCESS\"}")
        );

        // [Line 26] VERIFY ROLE: ยืนยันว่าแอปพลิเคชันยิง Request เข้ามาตรง 1 ครั้งจริง
        mockClient.verify(
            request().withMethod("POST").withPath("/api/v1/charge"),
            VerificationTimes.exactly(1)
        );
    }
}

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

เมื่อรัน Container เรียบร้อย สามารถเปิดเว็บเบราว์เซอร์ไปที่: http://localhost:1080/mockserver/dashboard

========================================================================================
                          MOCKSERVER WEB DASHBOARD (PORT 1080)
========================================================================================
 [ Active Expectations ] | [ Recorded Requests / Logs ] | [ Breakpoints ] | [ Reset All ]
----------------------------------------------------------------------------------------
Active Expectations Tab: ใช้ดูรายการ Mocks ที่กำลัง active อยู่ทั้งหมด
                         - สามารถกดดูรายละเอียด httpRequest และ httpResponse
                         - มีปุ่ม Delete สำหรับลบ Expectation ออกรายข้อได้ทันที

Recorded Requests & Logs Tab: แสดงประวัติทราฟฟิกจริงทุก Request ที่ยิงเข้าสู่ MockServer
                              - สามารถคลิกดู Headers, Path, และ Body ของแต่ละ Request ได้
                              - มีปุ่ม "Create Expectation" เพื่อสกัดดึง Log ทราฟฟิกจริงไปทำเป็น Mocks ได้ทันที

Reset Button (มุมขวาบน): ปุ่มสั่งส่งคำสั่ง PUT /mockserver/reset เพื่อล้าง Mocks และ Logs ทั้งหมดออกในคลิกเดียว

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

mockserver-module1-enterprise/
├── docker-compose.yml                  # ไฟล์ Container Orchestration
├── config/
│   └── mockserver.properties           # ไฟล์ตั้งค่า Port, Log Level, CORS
├── expectations/
│   └── initializerJson.json            # Expectation เริ่มต้นที่กางขึ้นมาทันทีเมื่อบูต
├── scripts/
│   ├── seed_expectations.sh            # สคริปต์ยิง cURL โหลด Mocks
│   └── reset_mockserver.sh             # สคริปต์สำหรับ Reset ค่า
└── tests/
    └── PaymentServiceTest.java         # Test Suite ฝั่ง Integration Test

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

❌ Flawed Approach (ยิงตรงหา External Staging)

พึ่งพา Staging เซิร์ฟเวอร์จริง ถ้านอกบ้านล่ม Test จะพังทันที:

PaymentResponse res = paymentClient.charge("https://staging-bank.com/api", 100);
assertEquals("SUCCESS", res.getStatus());
✅ Refactored Approach (ยิงหา Isolated MockServer)

รันผ่าน Dynamic Port ของ Testcontainers ได้ผลแน่นอน 100%:

PaymentResponse res = paymentClient.charge(mockServer.getEndpoint() + "/api", 100);
assertEquals("SUCCESS", res.getStatus());
ชุดแบบฝึกหัดปฏิบัติการ 6 ข้อ (Hands-on Drill Pack)

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

Drill 1.1: Basic GET Endpoint (สร้าง Mock พื้นฐาน)

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "GET", "path": "/api/v1/health" }, "httpResponse": { "statusCode": 200, "headers": { "Content-Type": ["application/json"] }, "body": "{\"status\": \"UP\"}" }}'

Test & Expected: curl -i http://localhost:1080/api/v1/health (ได้ HTTP 200 OK และ body {"status": "UP"})

Drill 1.2: Query Parameters Matching (ดักจับ Parameter บน URL)

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "GET", "path": "/api/v1/products", "queryStringParameters": { "category": ["books"], "page": ["1"] } }, "httpResponse": { "statusCode": 200, "headers": { "Content-Type": ["application/json"] }, "body": "[{\"id\": 101, \"title\": \"Clean Code\"}]" }}'

Test & Expected: curl -i "http://localhost:1080/api/v1/products?category=books&page=1" (ได้ 200 OK)

Drill 1.3: Custom Headers Validation (ตรวจสอบ Header เฉพาะ)

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "GET", "path": "/api/v1/config", "headers": { "X-Client-Id": ["iOS-App"] } }, "httpResponse": { "statusCode": 200, "headers": { "Content-Type": ["application/json"] }, "body": "{\"theme\": \"dark\"}" }}'

Test & Expected: curl -i http://localhost:1080/api/v1/config -H "X-Client-Id: iOS-App" (ได้ 200 OK)

Drill 1.4: POST Exact JSON Body Matching (ตรวจสอบ Payload ล็อกอิน)

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "POST", "path": "/api/v1/login", "body": { "type": "JSON", "json": "{\"username\": \"admin\", \"password\": \"secret123\"}", "matchType": "STRICT" } }, "httpResponse": { "statusCode": 200, "headers": { "Content-Type": ["application/json"] }, "body": "{\"token\": \"session-xyz-999\"}" }}'

Test & Expected: curl -i -X POST http://localhost:1080/api/v1/login -H "Content-Type: application/json" -d '{"username": "admin", "password": "secret123"}' (ได้ HTTP 200 OK)

Drill 1.5: HTTP Status Codes & Error Simulation (จำลองสถานะ Error)

Setup:

curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "POST", "path": "/api/v1/cart/checkout" }, "httpResponse": { "statusCode": 409, "headers": { "Content-Type": ["application/json"] }, "body": "{\"errorCode\": \"OUT_OF_STOCK\"}" }}'

Test & Expected: curl -i -X POST http://localhost:1080/api/v1/cart/checkout (HTTP 409 Conflict)

Drill 1.6: Verification Assertions (ฝึกตรวจเช็กการเรียกใช้งาน)

Verify Call:

curl -X PUT "http://localhost:1080/mockserver/verify" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "GET", "path": "/api/v1/health" }, "times": { "atLeast": 1 }}'

Expected: HTTP 202 Accepted (พบประวัติการยิงตรงตามเงื่อนไขอย่างน้อย 1 ครั้ง)

⚡ Instant Feedback Quiz

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

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

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

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

เนื้อหา สถาปัตยกรรม คำอธิบายโค้ด คู่มือ Dashboard และแบบฝึกหัด 6 Drills ทั้งหมดในวันนี้ ตอบและสะท้อนชื่อบท Day 1 / Module 1: Architecture, Core Roles & Enterprise Deployment อย่างชัดเจน: การที่เราเข้าใจ 3 บทบาทหลัก (Mock, Proxy, Verify) ร่วมกับ Port Unification ช่วยให้เราเลือกเครื่องมือมาตัดขาดจากระบบที่ไม่เสถียรได้ตรงจุด และการนำไปรันผ่าน Docker, Kubernetes หรือ Testcontainers ก็คือหัวใจของการนำไปปรับใช้จริงในระดับองค์กรนั่นเองครับ!