แนวคิดหลักคือ "Design Once, Tag by Level" — ออกแบบ Test Case ครั้งเดียว แต่ติด tag ว่า TC นั้นควรรันที่ level ไหน


โครงสร้างของ Test Suite ที่แนะนำ

Test Suite ที่ดีประกอบด้วย 3 ชั้น:

Test Suite
├── Test Scenario (SC-xxx)       ← "อยากทดสอบอะไร" — 1 scenario ต่อ 1 พฤติกรรม
│   └── Test Case (TC-xxx)       ← "ทดสอบยังไง" — 1 TC ต่อ 1 เงื่อนไข/ชุดข้อมูล
│       └── Test Data (TD-xxx)   ← "ใช้ข้อมูลอะไร" — แยก data ออกจาก steps

Level Tag System — หัวใจของ Multi-Level Suite

แต่ละ TC ต้องติด Level tag เพื่อบอกว่า TC นั้นรันที่ระดับไหน:

Levelรันที่ไหนใครรันFocus
UnitFunction / ClassDevLogic อย่างเดียว, mock ทุกอย่าง
Integrationหลาย component รวมกันDev / QAService A → DB, Service A → B
APIHTTP endpoint จริงQARequest/Response contract
E2E-ManualBrowser + UI ทั้งระบบQA / Manual TesterUser flow ตั้งแต่ต้นจนจบ

TC บาง TC สามารถติดหลาย level ได้ เพราะ logic เดียวกัน แต่ verify คนละมุม


Template มาตรฐาน — ใช้ได้กับทุก Level

═══════════════════════════════════════════════════════
TEST SCENARIO
═══════════════════════════════════════════════════════
SC-ID:        SC-[FEATURE]-[SEQ]
Feature:      [ชื่อ Feature]
Scenario:     [อธิบาย 1 ประโยค — ทดสอบพฤติกรรมอะไร]
Precondition: [สถานะระบบก่อนเริ่ม]
Expected:     [ผลลัพธ์ที่ควรได้]
Priority:     High / Medium / Low
Type:         Functional | Boundary | Negative | Security | State

───────────────────────────────────────────────────────
TEST CASE (ลูกของ SC นี้)
───────────────────────────────────────────────────────
TC-ID:        TC-[FEATURE]-[SEQ]
Title:        [อธิบาย 1 ประโยค — ทดสอบเงื่อนไขอะไร]
Parent SC:    SC-[FEATURE]-[SEQ]

Level:        [ ] Unit  [ ] Integration  [ ] API  [ ] E2E-Manual
              (ติ๊กได้มากกว่า 1 ถ้า TC นี้ verify ได้หลาย level)

Priority:     P1 (blocker) | P2 (major) | P3 (minor)
Technique:    EP | BVA | State Transition | Decision Table | Error Guessing

Preconditions:
  - [Auth state ที่ต้องการ]
  - [Seed data ที่ต้องมีก่อน]
  - [Environment: local | dev | staging]

Steps:
  1. [Action]
  2. [Action]
  3. [Action]

Expected Output:
  Status:       [HTTP status หรือ UI state]
  Response:     [Fields และ values ที่คาดหวัง]
  Side Effects: [DB เปลี่ยนยังไง, event ถูกส่งไหม, email ถูกส่งไหม]

Test Data Ref: TD-[SEQ]   ← ชี้ไปที่ Test Data แยกต่างหาก

Level-Specific Notes:
  Unit:         [Mock อะไรบ้าง, function ไหนที่ test]
  Integration:  [Component ไหนรวมกัน, DB จริงหรือ test DB]
  API:          [Endpoint, Headers, Auth token ที่ใช้]
  E2E-Manual:   [Browser, URL เริ่มต้น, ขั้นตอน UI]

═══════════════════════════════════════════════════════
TEST DATA
═══════════════════════════════════════════════════════
TD-ID:        TD-[SEQ]
Used By:      TC-[SEQ], TC-[SEQ]   ← TC ที่ใช้ data ชุดนี้

Input:
  [field]:    [value]   # [เหตุผล เช่น "min boundary = 3 chars"]
  [field]:    [value]

Expected State After:
  [entity]:   [state ที่คาดหวังหลัง action]

ตัวอย่างจริง — Feature: User Login

Scenario
SC-ID:       SC-LOGIN-001
Feature:     User Login
Scenario:    ผู้ใช้ที่มี account ถูกต้องสามารถ login และรับ token ได้
Precondition: มี user account ที่ active อยู่ในระบบ
Expected:    ได้รับ access token และ redirect ไปหน้า dashboard
Priority:    High
Type:        Functional
Test Cases ลูก — แต่ละ TC ติด Level ต่างกัน
TC-ID:    TC-LOGIN-001
Title:    Login ด้วย email/password ถูกต้อง — ได้รับ token
Parent:   SC-LOGIN-001
Level:    [x] Unit  [x] API  [x] E2E-Manual
Priority: P1
Technique: Happy Path

Steps:
  1. ส่ง credentials ที่ถูกต้อง
  2. ตรวจสอบ response

Expected Output:
  Status:   200 OK
  Response: { token: <non-empty string>, user: { id, email, role } }
  Side Effects: last_login_at ถูก update ใน DB

Test Data Ref: TD-LOGIN-001

Level-Specific Notes:
  Unit:        Test authService.login() โดย mock UserRepository + JwtService
  API:         POST /api/v1/auth/login — ใช้ test DB, ไม่ต้อง mock
  E2E-Manual:  เปิด browser → /login → กรอก form → คลิก Submit → ตรวจ redirect

───────────────────────────────────────────────────────
TC-ID:    TC-LOGIN-002
Title:    Login ด้วย password ผิด — ได้รับ 401 และ error message
Parent:   SC-LOGIN-001
Level:    [x] Unit  [x] API  [x] E2E-Manual
Priority: P1
Technique: Error Guessing / Negative

Expected Output:
  Status:   401 Unauthorized
  Response: { error: "Invalid credentials" }
  Side Effects: ไม่มี token ถูกออก, ไม่มี session ถูกสร้าง

Level-Specific Notes:
  Unit:        ตรวจสอบว่า authService throw UnauthorizedException
  API:         ตรวจสอบ status code + response body
  E2E-Manual:  ตรวจสอบว่า error message แสดงบน UI และ user ยังอยู่หน้า login

───────────────────────────────────────────────────────
TC-ID:    TC-LOGIN-003
Title:    Login ด้วย email ที่ไม่มีในระบบ
Parent:   SC-LOGIN-001
Level:    [ ] Unit  [x] API  [x] E2E-Manual
Priority: P2
Technique: EP (invalid partition)

Level-Specific Notes:
  Unit:        ไม่จำเป็น — ครอบคลุมโดย TC-002 แล้ว (logic เดิม)
  API:         ต้องตรวจสอบว่า error message ไม่เปิดเผยว่า "email ไม่มีในระบบ" (security)
  E2E-Manual:  ตรวจสอบ UX — message ที่แสดงบน UI
Test Data แยกออกมา
TD-ID:   TD-LOGIN-001
Used By: TC-LOGIN-001

Input:
  email:    "[email protected]"    # registered, active account
  password: "ValidPass1!"               # correct password

Expected State After:
  users table: last_login_at = NOW() (within 5 sec)
  sessions table: new session record created

───────────────────────────────────────────────────────
TD-ID:   TD-LOGIN-002
Used By: TC-LOGIN-002, TC-LOGIN-003

Input (TC-002 - wrong password):
  email:    "[email protected]"
  password: "WrongPass999!"

Input (TC-003 - unknown email):
  email:    "[email protected]"
  password: "AnyPass1!"

วิธีจัดระเบียบ Test Suite ทั้งหมด

แนะนำให้จัดแบบนี้ ไม่ว่าจะเก็บใน Excel, Notion, Jira, หรือ Markdown:

Test Suite
├── Feature: Authentication
│   ├── SC-AUTH-001: Register
│   │   ├── TC-AUTH-001 (Unit, API, E2E)
│   │   └── TC-AUTH-002 (API, E2E)
│   ├── SC-AUTH-002: Login
│   │   ├── TC-AUTH-003 (Unit, API, E2E)
│   │   └── TC-AUTH-004 (API, E2E)
│   └── SC-AUTH-003: Logout
│       └── TC-AUTH-005 (Integration, API, E2E)
│
├── Feature: Order Management
│   ├── SC-ORDER-001: Create Order
│   └── SC-ORDER-002: Cancel Order
│
└── Test Data Pool
    ├── TD-001 ~ TD-010: Auth data
    └── TD-011 ~ TD-020: Order data

สรุป — สิ่งที่ทำให้ Test Suite ใช้ได้หลาย Level

หลักการรายละเอียด
Level Tag บน TCบอกชัดว่า TC นี้รันที่ level ไหน — ไม่ต้องเขียนซ้ำ
Level-Specific Notesบอก mock strategy / endpoint / UI steps แยกตาม level
Test Data แยกอิสระTD ถูก reuse ข้ามหลาย TC และหลาย level ได้
TC ติด Parent SCtrace กลับได้ว่า TC นี้มาจาก scenario ไหน
Steps เป็น business languageอ่านได้ทั้ง Dev (unit) และ Manual Tester (E2E)